• [Winform] DataGridView 总结(FAQ)


    Q1.  如何使单元格不可编辑?

    A:设置 ReadOnly 属性,可以设置的对象包括 DataGridViewRow(行)、DataGridViewColumn(列)、DataGridViewCell(单元格)以及自身 DataGridView 对象均可设置 ReadOnly 属性来限制单元格的编辑状态。

    扩展:需要注意的是,当 DataGridView 通过 DataSource 绑定数据自动生成行列时,如果直接在 Form 的构造函数初始化界面 InitializeComponent 后直接设置 ReadOnly 属性,会造成一些意想不到的效果……

      1 public MainForm()
      2 {
      3     InitializeComponent()
      4 
      5     Application.DoEvents()
      6     dataGridView.DataSource = Person.GetPersons()
      7     dataGridView[0, 0].ReadOnly = true
      8     dataGridView.Rows[2].ReadOnly = true
      9     dataGridView.Columns[1].ReadOnly = true
     10 } 

    此时对 DataGridViewCell、DataGridViewRow的ReadOnly 设置无效,而对 DataGridViewColumn 的 ReadOnly 设置有效。

    另外,ReadOnly 属性只是限制用户在界面上对单元格内容编辑的限制,并不影响在编程代码中对该单元格的编辑以及删除行列操作。

    当然,你也可以在 CellBeginEdit 事件中对单元格进行判断,然后直接结束编辑即可,如下:

      1 dataGridView.CellBeginEdit += (sender, e) =>
      2 {
      3     if (e.RowIndex == 0 && e.ColumnIndex == 0)
      4     {
      5         e.Cancel = true
      6         // 或者
      7         // dataGridView.EndEdit();
      8     }
      9 }

    Q2.  如何禁用单元格(Disable)?

    A:DataGridView 不支持设置单元格的不可用状态,所以采用折中办法,使该“禁用”单元格的选中状态和其背景颜色相同,给人暗示性的外观提示该单元格“禁用”。

    有一种便捷的方式,就是将该“禁用”单元格的选中颜色设置成非选中颜色,即如果未选中前是白底黑字,则将该“禁用”单元格的选中状态也改成白底黑字即可,对单元格、行和列均适用,举例如下:

      1 dataGridView[2, 2].Style.SelectionBackColor = Color.White
      2 dataGridView[2, 2].Style.SelectionForeColor = Color.Black
      3 
      4 dataGridView.Rows[1].DefaultCellStyle.SelectionBackColor = Color.White
      5 dataGridView.Rows[1].DefaultCellStyle.SelectionForeColor = Color.Black
      6 
      7 dataGridView.Columns[0].DefaultCellStyle.SelectionBackColor = Color.White
      8 dataGridView.Columns[0].DefaultCellStyle.SelectionForeColor = Color.Black

    需要注意的是,同 Q1 中一样,在 InitializeComponent 方法后面直接操作,其中对单元格的设置无效,对行、列的设置有效!!

    但是这种方法对文本内容的单元有效,对于 DataGridViewButtonColumn、DataGridViewCheckBoxColumn、DataGridViewComboBoxColumn 等其他特殊列就没效果了,毕竟对于特殊列,禁用单元格就是要禁用其中的特殊控件,这时候就需要重写其中的单元格模版了,以 DataGridViewButtonColumn 为例,代码如下:

    public class DataGridViewButtonColumnExt : DataGridViewButtonColum
    {
        public DataGridViewButtonColumnExt()
        {
            this.CellTemplate = new DataGridViewButtonCellExt()
        }
    }
    
    public class DataGridViewButtonCellExt : DataGridViewButtonCell
    {
        private bool _Enabled;// 设置该单元格是否可用
    
        /// <summary>
        /// 单元格是否可用
        /// </summary>
        public bool Enabled
        {
            get
            {
                return _Enabled
            }
            set
            {
                _Enabled = value
            }
        }
    
        public override object Clone()
        {
            DataGridViewButtonCellExt cell =(DataGridViewButtonCellExt)base.Clone()
            cell.Enabled = this.Enabled
    
            return cell
        }
    
        public DataGridViewButtonCellExt()
        {
            this._Enabled = true
        }
    
        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds,
            int rowIndex, DataGridViewElementStates elementState, object value,
            object formattedValue, string errorText, DataGridViewCellStyle cellStyle,
            DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            if (!this._Enabled)
            {
                // 绘制背景
                if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)
                {
                    SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor)
                    graphics.FillRectangle(cellBackground, cellBounds)
                    cellBackground.Dispose()
                }
    
                // 绘制边框
                if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)
                {
                    PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
                }
    
                Rectangle buttonArea = cellBound
                Rectangle buttonAdjustment = this.BorderWidths(advancedBorderStyle)
                buttonArea.X += buttonAdjustment.X
                buttonArea.Y += buttonAdjustment.Y
                buttonArea.Height -= buttonAdjustment.Height
                buttonArea.Width -= buttonAdjustment.Width
                
                // 绘制按钮控件
                ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled)
                // 绘制文本内容
                if (this.FormattedValue is String)
                {
                    TextRenderer.DrawText(graphics, (string)this.FormattedValue,
                        this.DataGridView.Font, buttonArea, SystemColors.GrayText)
                }
            }
            else
            {
                base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue,
                    errorText, cellStyle, advancedBorderStyle, paintParts)
            }
        }
    }
    
    
    View Code

    下面是 CheckBox 列的重写例子,因为复选框有两种禁用效果,一种选中时禁用,一种是未选中时禁用,所以加了一个 IsChecked 属性:

    public class DataGridViewCheckBoxColumnExt : DataGridViewCheckBoxColum
    {
    	public DataGridViewCheckBoxColumnExt()
    	{
    		this.CellTemplate = new DataGridViewCheckBoxCellExt()
    	}
    }
    
    public class DataGridViewCheckBoxCellExt : DataGridViewCheckBoxCell
    {
    	private bool _Enable
    	private bool _IsChecked
    	
    	/// <summary>
    	/// 是否选中
    	/// </summary>
    	public bool IsChecked
    	{
    		get
    		{
    			return _IsChecked
    		}
    		set
    		{
    			_IsChecked = value
    		}
    	}
    
    	/// <summary>
    	/// 是否可用
    	/// </summary>
    	public bool Enable
    	{
    		get
    		{
    			return _Enable
    		}
    		set
    		{
    			_Enable = value
    		}
    	}
    
    	public DataGridViewCheckBoxCellExt()
    	{
    		_Enable = true
    		_IsChecked = false
    	}
    
    	public override object Clone()
    	{
    		DataGridViewCheckBoxCellExt dgvcce = (DataGridViewCheckBoxCellExt)base.Clone()
    		dgvcce.Enable = this.Enable
    		dgvcce.IsChecked = this.IsChecked
    
    		return dgvcce
    	}
    
    	protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds,
    		int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue,
    		string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle,
    		DataGridViewPaintParts paintParts)
    	{
    		if (!_Enable)
    		{
    			// 绘制背景
    			if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)
    			{
    				SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor)
    				graphics.FillRectangle(cellBackground, cellBounds)
    				cellBackground.Dispose()
    			}
    
    			// 绘制边框
    			if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)
    			{
    				PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
    			}
    
    			Point pos = cellBounds.Locatio
    			// 调整位置居中
    			pos.X = pos.X + cellBounds.Width / 2 - 7
    			pos.Y = pos.Y + cellBounds.Height / 2 - 7
    			// 绘制按钮控件
    			CheckBoxRenderer.DrawCheckBox(graphics, pos,
    				IsChecked ? CheckBoxState.CheckedDisabled : CheckBoxState.UncheckedDisabled)
    		}
    		else
    		{
    			base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value,
    				formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
    		}
    	}
    }
    
    View Code

    Q3.  如何在单元格内同时显示图片和文字?

    A:参考网上查到的资料,将文本列(DataGridViewTextBoxColum)中的文本向右偏移,然后在前面置入图片;

    public class TextAndImageColumn : DataGridViewTextBoxColum
    {
        private Image m_ImageValue
        private Size m_ImageSize
    
        public TextAndImageColumn()
        {
            this.CellTemplate = new TextAndImageCell()
        }
    
        public override object Clone()
        {
            TextAndImageColumn c = base.Clone() as TextAndImageColum
            c.m_ImageValue = this.m_ImageValue
            c.m_ImageSize = this.m_ImageSize
    
            return c
        }
    
        public Image Image
        {
            get
            {
                return this.m_ImageValue
            }
            set
            {
                if (this.Image != value)
                {
                    this.m_ImageValue = value
                    this.m_ImageSize = value.Size
                    if (this.InheritedStyle != null)
                    {
                        Padding inheritedPadding = this.InheritedStyle.Padding
                        this.DefaultCellStyle.Padding = new Padding(m_ImageSize.Width,
                            inheritedPadding.Top, inheritedPadding.Right,
                            inheritedPadding.Bottom)
                    }
                }
            }
        }
    
        public Size ImageSize
        {
            get
            {
                return m_ImageSize
            }
            set
            {
                m_ImageSize = value
            }
        }
    
        private TextAndImageCell TextAndImageCellTemplate
        {
            get
            {
                return this.CellTemplate as TextAndImageCell
            }
        }
    }
    
    public class TextAndImageCell : DataGridViewTextBoxCell
    {
        private Image m_ImageValue
        private Size m_ImageSize
    
        public override object Clone()
        {
            TextAndImageCell c = base.Clone() as TextAndImageCell
            c.m_ImageValue = this.m_ImageValue
            c.m_ImageSize = this.m_ImageSize
    
            return c
        }
    
        public Image Image
        {
            get
            {
                if (this.OwningColumn == null || this.OwningTextAndImageColumn == null)
                {
                    return m_ImageValue
                }
                else if (this.m_ImageValue != null)
                {
                    return this.m_ImageValue
                }
                else
                {
                    return this.OwningTextAndImageColumn.Image
                }
            }
            set
            {
                if (this.m_ImageValue != value)
                {
                    this.m_ImageValue = value
                    this.m_ImageSize = value.Size
                    Padding inheritedPadding = this.InheritedStyle.Padding
                    this.Style.Padding = new Padding(m_ImageSize.Width,
                        inheritedPadding.Top, inheritedPadding.Right,
                        inheritedPadding.Bottom)
                }
            }
        }
    
        protected override void Paint(Graphics graphics, Rectangle clipBounds,
            Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState,
            object value, object formattedValue, string errorText,
            DataGridViewCellStyle cellStyle,
            DataGridViewAdvancedBorderStyle advancedBorderStyle,
            DataGridViewPaintParts paintParts)
        {
            // Paint the base content
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState,
              value, formattedValue, errorText, cellStyle,
              advancedBorderStyle, paintParts)
    
            if (this.Image != null)
            {
                // Draw the image clipped to the cell.
                System.Drawing.Drawing2D.GraphicsContainer container = graphics.BeginContainer()
                graphics.SetClip(cellBounds)
                graphics.DrawImageUnscaled(this.Image, cellBounds.Location)
                graphics.EndContainer(container)
            }
        }
    
        private TextAndImageColumn OwningTextAndImageColum
        {
            get
            {
                return this.OwningColumn as TextAndImageColum
            }
        }
    }
    
    View Code

    还有一种实现方式是在 CellPainting 事件中对指定单元格进行图文绘制,参考百度文库 

    Q4.  如何让 DataGridViewComboBox 可编辑?

    A:见 DataGridView 中 DataGridViewComboBox 的可编辑

    还有一种根据 参考资料1 提供的方法,更简捷,就是在 CellValidating 事件中将新编辑的内容添加到 Items 集合中,在 EditingControlShowing 事件中将下拉框类型 DropDownStyle 设置成 ComboBoxStyle.DropDown,使用户可以进入编辑状态,代码如下:

      1 private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
      2 {
      3     try
      4     {
      5         if (dgv4.CurrentCellAddress.X == 4)
      6         {
      7             ComboBox cb = e.Control as ComboBox;
      8             if (cb != null)
      9             {
     10                 cb.DropDownStyle = ComboBoxStyle.DropDown;
     11             }
     12         }
     13     }
     14     catch (Exception ex)
     15     {
     16         MessageBox.Show(ex.Message);
     17     }
     18 }
     19 
     20 private void dgv4_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
     21 {
     22     try
     23     {
     24         if (e.ColumnIndex == 4)
     25         {
     26             DataGridViewComboBoxColumn dgvcbc = (DataGridViewComboBoxColumn)dgv4.Columns[4];
     27             if (!dgvcbc.Items.Contains(e.FormattedValue))
     28             {
     29                 dgvcbc.Items.Add(e.FormattedValue);
     30             }
     31         }
     32     }
     33     catch (Exception ex)
     34     {
     35         MessageBox.Show(ex.Message);
     36     }
     37 }
    

    Q5.  如何多列排序?

    A:分绑定数据和非绑定数据两种情况处理。

    1、绑定数据

    如果绑定的数据源(如 DataView)支持多列排序,则 DataGridView 在绑定数据后会保留数据源排序后的结果,但是只有第一个进行排序的列会在 DataGridView 的显示排序标记。而且,DataGridView 的 SortedColumn 属性也只返回第一个排序列。

    如果数据源实现了 IBindingListView 接口,并提供了对 Sort 属性的支持,那么该数据源就可以支持多列排序。为了弥补上面提到的只标记了第一排序列的缺陷,可以手动对进行排序的列设置 SortGlyphDirection 属性来标记。

      1 BindingSource bindingSource = new BindingSource();
      2 dgv4.AutoGenerateColumns = false;
      3 dgv4.DataSource = bindingSource;
      4 
      5 DataTable dt = new DataTable();
      6 dt.Columns.Add("C1", typeof(int));
      7 dt.Columns.Add("C2", typeof(string));
      8 dt.Columns.Add("C3", typeof(string));
      9 
     10 dt.Rows.Add(1, "1", "Test1");
     11 dt.Rows.Add(2, "2", "Test2");
     12 dt.Rows.Add(2, "2", "Test1");
     13 dt.Rows.Add(3, "3", "Test3");
     14 dt.Rows.Add(4, "4", "Test4");
     15 dt.Rows.Add(4, "4", "Test3");
     16 
     17 DataView view = dt.DefaultView;
     18 view.Sort = "C2 ASC,C3 DESC";
     19 bindingSource.DataSource = view;
     20 
     21 DataGridViewTextBoxColumn col0 = new DataGridViewTextBoxColumn();
     22 col0.DataPropertyName = "C1";
     23 dgv4.Columns.Add(col0);
     24 col0.SortMode = DataGridViewColumnSortMode.Programmatic;
     25 col0.HeaderCell.SortGlyphDirection = SortOrder.None;
     26 
     27 DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn();
     28 col1.DataPropertyName = "C2";
     29 dgv4.Columns.Add(col1);
     30 col1.SortMode = DataGridViewColumnSortMode.Programmatic;
     31 col1.HeaderCell.SortGlyphDirection = SortOrder.Ascending;
     32 
     33 DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn();
     34 col2.DataPropertyName = "C3";
     35 dgv4.Columns.Add(col2);
     36 col2.SortMode = DataGridViewColumnSortMode.Programmatic;
     37 col2.HeaderCell.SortGlyphDirection = SortOrder.Descending;

    需要注意的是,对 SortGlyphDirection 属性的设置要在 DataGridView 绑定 DataSource 后面操作,否则会不生效。image

    上面代码来自资料参考2的,可以简化成:

    DataTable dt = new DataTable();
    dt.Columns.Add("C1", typeof(int));
    dt.Columns.Add("C2", typeof(string));
    dt.Columns.Add("C3", typeof(string));
    
    dt.Rows.Add(1, "1", "Test1");
    dt.Rows.Add(2, "2", "Test2");
    dt.Rows.Add(2, "2", "Test1");
    dt.Rows.Add(3, "3", "Test3");
    dt.Rows.Add(4, "4", "Test4");
    dt.Rows.Add(4, "4", "Test3");
    
    DataView view = dt.DefaultView;
    view.Sort = "C2 ASC,C3 DESC";
    
    dgv4.DataSource = dt;
    dgv4.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic;
    dgv4.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
    dgv4.Columns[2].SortMode = DataGridViewColumnSortMode.Programmatic;
    dgv4.Columns[2].HeaderCell.SortGlyphDirection = SortOrder.Descending;
    
    View Code

    2、非绑定数据

    为了提供对多个列排序的支持,可以通过处理 SortCompare 事件,或者调用重载的 Sort ( IComparer ) 方法以更灵活的方式进行排序。

    2.1、SortCompare 事件

    private void dgv4_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
    {
        try
        {
            e.SortResult = String.Compare(e.CellValue1.ToString(), e.CellValue2.ToString());
            if (e.SortResult == 0 && e.Column.Name != "ID")
            {
                e.SortResult = string.Compare(
                    dgv4.Rows[e.RowIndex1].Cells["ID"].Value.ToString(),
                    dgv4.Rows[e.RowIndex2].Cells["ID"].Value.ToString());
            }
    
            e.Handled = true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    

    上面的例子表示当你点击列头对某一列进行排序时,如果值相同时,则会按照对应 ID 值进行排序;所以如果要支持多列排序,在该事件里处理即可。

    2.2、IComparer 接口

    楼主扩展了资料里提供的参考例子,改成通用的多列排序接口实现,

      1 public class RowComparer : IComparer
      2 {
      3     private Dictionary<string, int> m_SortList;
      4 
      5     /// <summary>
      6     /// 排序字符串,格式:(ColName1 AES,ColName2 DESC,ColName3 AES,...)
      7     /// </summary>
      8     public string Sort
      9     {
     10         get;
     11         set;
     12     }
     13 
     14     /// <summary>
     15     /// 构造函数,初始化排序条件
     16     /// </summary>
     17     public RowComparer(string sort)
     18     {
     19         Sort = sort;
     20         try
     21         {
     22             string[] tmp = Sort.Split(',');
     23             m_SortList = new Dictionary<string, int>();
     24             for (int i = 0; i < tmp.Length; i++)
     25             {
     26                 string[] tmp2 = tmp[i].Split(new char[] { ' ' },
     27                     StringSplitOptions.RemoveEmptyEntries);
     28                 string colName = tmp2[0].ToLower();
     29                 int sortType = tmp2[1].ToLower().Equals("AES") ? 1 : -1;
     30                 if (m_SortList.ContainsKey(colName))
     31                 {
     32                     m_SortList[colName] = sortType;
     33                 }
     34                 else
     35                 {
     36                     m_SortList.Add(colName, sortType);
     37                 }
     38             }
     39         }
     40         catch (Exception ex)
     41         {
     42             throw new Exception(ex.Message);
     43         }
     44     }
     45 
     46     #region IComparer 成员
     47 
     48     public int Compare(object x, object y)
     49     {
     50         int compareResult = 0;// 比较结果
     51         int sortMode = 0;// 排序方式
     52         try
     53         {
     54             DataGridViewRow dgvr1 = (DataGridViewRow)x;
     55             DataGridViewRow dgvr2 = (DataGridViewRow)y;
     56             foreach (string colName in m_SortList.Keys)
     57             {
     58                 compareResult = string.Compare(dgvr1.Cells[colName].Value.ToString(),
     59                     dgvr2.Cells[colName].Value.ToString());
     60                 sortMode = m_SortList[colName];
     61                 if (compareResult != 0)
     62                 {
     63                     break;
     64                 }
     65             }
     66         }
     67         catch (Exception ex)
     68         {
     69             MessageBox.Show(ex.Message);
     70         }
     71 
     72         return compareResult * sortMode;
     73     }
     74 
     75     #endregion
     76 }
    
    View Code

    Sort 属性采用 DataView 的 Sort 属性设置,然后在 RowComparer 构造函数对排序字符串进行处理,最后在接口方法 Compare 中依先后顺序逐级排序;

    Q6.  绑定 List 时,如何使 DataGridView 和 List 数据源同步修改?(3)

    A:当 DataGridView 绑定 List 数据源时,对 List 进行操作后并不会实时更新到 DataGridView 上,这时候采用 BindingList 就可以很好的解决问题。BindingList 类可以用作创建双向数据绑定机制的基类。BindingList 提供 IBindingList 接口的具体泛型实现,这样就不必实现完整的 IBindingList 接口了。

    BindingList 还可以通过扩展的 AddNew 方法支持工厂创建的示例;通过 EndNew CancelNew 方法实现新项的事务性提交和回滚。

    Q7.  如何在用户删除记录时显示确认对话框?

    A:用户选中一行后按 Delete 键会触发 UserDeletingRow 事件(当然,前提是要设置 DataGridView 的 AllowUserToDeleteRows 属性为 True 才可以),在该事件里提示用户是否删除当前选中记录即可。

    private void dgv4_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
    {
        try
        {
            if (!e.Row.IsNewRow)
            {
                if (MessageBox.Show("确定删除当前选中数据?", "删除",
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                    MessageBoxDefaultButton.Button2)
                    == System.Windows.Forms.DialogResult.No)
                {
                    e.Cancel = true;
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    

    Q8.  如何为单元格内的控件添加事件处理函数?

    A:诸如 DataGridViewButtonColumn 列里的 Button,DataGridViewCheckBoxColumn 列里的 CheckBox 等等,要给 Button 或 CheckBox 控件添加事件处理函数,则可以通过实现 DataGridView 的 EditingControlShowing 事件,该事件是在单元格进入编辑模式时触发,可以处理执行该编辑控件的自定义初始化。它的第二个参数 DataGridViewEditingControlShowingEventArgs 类型,其 Control 属性就是该单元格内的编辑控件了。拿 DataGridViewComboBoxColumn 列里的 ComboBox 事件举个例子:

    private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
    {
        try
        {
            ComboBox cb = e.Control as ComboBox;
            if (cb != null)
            {
                cb.SelectedIndexChanged -= new EventHandler(cb_SelectedIndexChanged);
                cb.SelectedIndexChanged += cb_SelectedIndexChanged;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    
    private void cb_SelectedIndexChanged(object sender, EventArgs e)
    {
        MessageBox.Show("Selected Index Changed!");
    }
    
    View Code

    需要注意的是,在 EditingControlShowing 事件里对编辑控件进行事件绑定时,要防止其添加多个相同的事件处理函数,所以在绑定事件前可以先移除相应的事件处理函数。

    Q9.  如何让数据显示区域(列)填充整个 DataGridView 大小?

    A:可以设置 DataGridView 的 AutoSizeColumnsMode 属性,设置为 DataGridViewAutoSizeColumnsMode.Fill,此时列宽会自动调整到使所有列宽精确填充控件的显示区域。当然,你可以通过设置每一列的 DataGridViewColumn.FillWeight 属性来设置每一列的相对宽度。

    如果只是想把最后一列填充剩下的空间,而前面那些列都是固定大小的,那可以直接设置最后一列的 DataGridViewColumn.AutoSizeMode 的属性为 DataGridViewAutoSizeColumnMode.Fill 即可。

    Q10.  如何设置 Image 列中当值为 NULL 时不显示图像(默认显示“X”图像)?

    A:设置 Image 列的 dataGridView.DefaultCellStyle.NullValue = null;

    Q11.  如何设置单元格内容自动换行?

    A:设置自动换行:

    datagridview.DefaultCellStyle.WrapMode = DataGridViewTriState.True;

    然后设置自动调整高度,设置

    datagridview.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders;

    或者 datagridview.AutoResizeRows(DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders);

    需要注意的是,该设置对中文有效;如果是英文,一个单词的宽度超过单元格的宽度,则是不会自动处理换行的,但是英文单词与单词之间有空格的话,也会处理自动换行。

    Q12.  如何禁用列的自动排序?

    A:设置 DataGridViewColumn 的 SortMode 属性,该属性默认值为 DataGridViewColumnSortMode.Automatic,即自动排序,改成 DataGridViewColumnSortMode.NotSortable 则可避免默认的排序;或者设置成 DataGridViewColumnSortMode.Programmatic,仅允许通过编程的方式对列进行排序。

    Q13.  如何拖动调整行顺序?

    A:设置 DataGridView 的 AllowDrop 属性为 true,并实现 MouseDown、MouseMove、DragOver、DragDrop 四个事件,参考代码如下:

      1 private Rectangle _dragBox;
      2 private int _rowIndexFrom;// 鼠标按下位置的行索引
      3 private int _rowIndexTo;// 将要拖动到的行索引位置
      4 private void dgv4_MouseDown(object sender, MouseEventArgs e)
      5 {
      6     try
      7     {
      8         _rowIndexFrom = dgv4.HitTest(e.X, e.Y).RowIndex;
      9         if (_rowIndexFrom != -1)
     10         {
     11             Size size = SystemInformation.DragSize;
     12             _dragBox = new Rectangle(new Point(e.X - (size.Width / 2), e.Y - (size.Height / 2)), size);
     13         }
     14         else
     15         {
     16             _dragBox = Rectangle.Empty;
     17         }
     18     }
     19     catch (Exception ex)
     20     {
     21         MessageBox.Show(ex.Message);
     22     }
     23 }
     24 private void dgv4_MouseMove(object sender, MouseEventArgs e)
     25 {
     26     try
     27     {
     28         if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
     29         {
     30             if (_dragBox != Rectangle.Empty && !_dragBox.Contains(e.X, e.Y))
     31             {
     32                 DragDropEffects dropEffect = dgv4.DoDragDrop(dgv4.Rows[_rowIndexFrom], DragDropEffects.Move);
     33             }
     34         }
     35     }
     36     catch (Exception ex)
     37     {
     38         MessageBox.Show(ex.Message);
     39     }
     40 }
     41 private void dgv4_DragDrop(object sender, DragEventArgs e)
     42 {
     43     try
     44     {
     45         // 鼠标坐标是相对于屏幕的,转换成工作区 DataGridView 上的坐标
     46         Point clientPoint = dgv4.PointToClient(new Point(e.X, e.Y));
     47         // 获取鼠标下面的所在行号
     48         _rowIndexTo = dgv4.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
     49         if (e.Effect == DragDropEffects.Move)
     50         {
     51             DataGridViewRow rowToMove = e.Data.GetData(typeof(DataGridViewRow)) as DataGridViewRow;
     52             dgv4.Rows.RemoveAt(_rowIndexFrom);
     53             dgv4.Rows.Insert(_rowIndexTo, rowToMove);
     54         }
     55     }
     56     catch (Exception ex)
     57     {
     58         MessageBox.Show(ex.Message);
     59     }
     60 }
     61 private void dgv4_DragOver(object sender, DragEventArgs e)
     62 {
     63     e.Effect = DragDropEffects.Move;// 设置拖动效果
     64 }
    
    View Code

    Q14.  如何显示主从表?

    A:主从表的意思就是通过两个 DataGridView 控件显示具有主从关系的两个数据表,在主表中选择一行记录,则从表中也会随之改变,显示对应的记录数据。

    1、下面代码是 DataGridView 直接绑定 DataSet 数据集:

    DataTable masterDT = Person.GetPersons2();// 主表,主键"ID"
    DataTable detailDT = Person.GetPersons3();// 从表,外键"ID"
    
    DataSet dataDS = new DataSet();
    dataDS.Tables.Add(masterDT);
    dataDS.Tables.Add(detailDT);
    // 建立主从表关系
    dataDS.Relations.Add("Custom", masterDT.Columns["ID"], detailDT.Columns["ID"]);
    
    // 数据绑定
    dgv5_1.DataSource = dataDS;
    dgv5_1.DataMember = dataDS.Tables[0].TableName;
    
    dgv5_2.DataSource = dataDS;
    dgv5_2.DataMember = dataDS.Tables[0].TableName + ".Custom";
    
    dgv5_1.MultiSelect = false;
    

    代码中主表 masterDT,从表 detailDT,通过 DataSet 的 Relations 建立主从关系,注意其中 Add 方法的第一个参数是关系名称,在后面的数据绑定中要用到。

    负责显示主表数据的 DataGridView(dgv5_1),负责显示从表数据的 DataGridView(dgv5_2),其 DataSource 均绑定同一个 DataSet,DataMember 注意区别,主表的 dgv5_1 直接绑定主表名称即可,从表的 dgv5_2 的 DataMember 属性则要绑定"主表名称.关系名称"。

    2、下面代码是 DataGridView 绑定 BindingSource

    DataTable masterDT = Person.GetPersons2();// 主表,主键"ID"
    DataTable detailDT = Person.GetPersons3();// 从表,外键"ID"
    DataSet dataDS = new DataSet();
    dataDS.Tables.Add(masterDT);
    dataDS.Tables.Add(detailDT);
    dataDS.Relations.Add("Custom", masterDT.Columns["ID"], detailDT.Columns["ID"]);
    
    BindingSource masterBS = new BindingSource();
    masterBS.DataSource = dataDS;
    masterBS.DataMember = masterDT.TableName;
    dgv5_1.DataSource = masterBS;
    
    BindingSource detailBS = new BindingSource();
    detailBS.DataSource = masterBS;
    detailBS.DataMember = "Custom";
    dgv5_2.DataSource = detailBS;
    
    dgv5_1.MultiSelect = false;
    

    注意如果 DataGridView 通过 BindingSource 绑定数据,并且在 BindingSource 中设置 DataSource 和 DataMember 时,此时的从表对应的 BindingSource 设置的 DataMember 应该直接用关系名称即可,与上面的"主表名称.关系名称"区别开来!

    Q15.  如何同时显示绑定数据和非绑定数据?

    A:楼主就以资料1中例子来讲解,下面代码是楼主亲敲亲测的。该例子是说如何创建一列复选框列,用来负责选择数据。并且通过字典缓存(_CheckState)以及相应的事件,保证在用户排序时不会丢失已有的选择状态。

    private Dictionary<int, bool> _CheckState;// 存储非绑定列数据
    public MainForm()
    {
        InitializeComponent();
    
        _CheckState = new Dictionary<int, bool>();
        dgv4.AutoGenerateColumns = true;
        dgv4.ReadOnly = false;
        dgv4.DataSource = Person.GetPersons2();
        dgv4.VirtualMode = true;
        dgv4.Columns.Insert(0, new DataGridViewCheckBoxColumn());
        dgv4.CellValueChanged += (s, e) =>
        {
            if (e.ColumnIndex == 0 && e.RowIndex != -1)
            {
                int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value);
                _CheckState[id] = Convert.ToBoolean(dgv4.Rows[e.RowIndex].Cells[0].Value);
            }
        };
        dgv4.CellValueNeeded += (s, e) =>
        {
            if (e.ColumnIndex == 0)
            {
                int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value);
                if (_CheckState.ContainsKey(id))
                {
                    e.Value = _CheckState[id];
                }
                else
                {
                    e.Value = false;
                }
            }
        };
        dgv4.CellValuePushed += (s, e) =>
        {
            if (e.ColumnIndex == 0)
            {
                int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value);
                if (!_CheckState.ContainsKey(id))
                {
                    _CheckState.Add(id, Convert.ToBoolean(e.Value));
                }
                else
                {
                    _CheckState[id] = Convert.ToBoolean(e.Value);
                }
            }
        };
    }
    

    1、复选框值改变时通过 CellValueChanged 事件缓存到 _ChekState 字典中;2、VirtualMode 为 true,并且单元格有值时需要显示和格式化的处理,是通过 CellValueNeeded 事件;3、VirtualMode 属性为 true,并且单元格值已更改并需要存储在基础数据源中时通过 CellValuePushed 事件处理。

    Q16.  列头右键菜单控制列的显隐[4]

    A:需求是在列头位置处增加右键菜单,控制 DataGridView 所有列的显示和隐藏。

    首先在 DataGridView 列的增删时对菜单选项进行更新,然后在菜单点击事件中修改列的可见性以及菜单栏该选项的 IsChecked 属性,最后在右键单击处显示菜单即可。

      1 // 列头位置显示右键菜单
      2 dataGridView.ColumnHeaderMouseClick += (sender, e) =>
      3 {
      4     if (e.Button == MouseButtons.Right)
      5     {
      6         contextMenuStrip.Show(MousePosition.X, MousePosition.Y);
      7     }
      8 };
      9 // 增加列时
     10 dataGridView.ColumnAdded += (sender, e) =>
     11 {
     12     ToolStripMenuItem item = new ToolStripMenuItem(e.Column.Name);
     13     contextMenuStrip.Items.Add(item);
     14     item.Checked = e.Column.Visible;
     15 };
     16 // 删除列时
     17 dataGridView.ColumnRemoved += (sender, e) =>
     18 {
     19     int count = contextMenuStrip.Items.Count;
     20     for (int i = 0; i < count; i++)
     21     {
     22         ToolStripMenuItem tsm = (ToolStripMenuItem)contextMenuStrip.Items[i];
     23         if (tsm.Text.Equals(e.Column.Name))
     24         {
     25             contextMenuStrip.Items.Remove(tsm);
     26             break;
     27         }
     28     }
     29 };
     30 // 菜单栏控制列的显示和隐藏
     31 contextMenuStrip.ItemClicked += (sender, e) =>
     32 {
     33     string colName = e.ClickedItem.Text;
     34     bool isChecked = ((ToolStripMenuItem)e.ClickedItem).Checked;
     35     dataGridView.Columns[colName].Visible = !isChecked;
     36     ((ToolStripMenuItem)e.ClickedItem).Checked = !isChecked;
     37 };
    

    资料参考

    1、http://www.cnblogs.com/xiaofengfeng/archive/2011/04/16/2018504.html (主要参考)

    2、http://blogs.msdn.com/b/jfoscoding/archive/2005/11/17/494012.aspx

    3、https://msdn.microsoft.com/zh-cn/library/ms132679.aspx#Mtps_DropDownFilterTextBindingList

    4、http://www.cnblogs.com/over140/archive/2012/04/16/2451200.html

  • 相关阅读:
    阿里云SQL Server远程连接配置
    RSA签名验证无法通过,检查以下部分
    windows开机自动登录
    c# 进程调用exe
    JavaScript console控制台调试 post
    Tesseract-OCR 训练教程(二) 合并新的训练文件
    获取手机唯一标识
    sqlserver 日期与字符串之间的转换
    linq根据英文首字母姓名排序
    js调用浏览器下载
  • 原文地址:https://www.cnblogs.com/memento/p/4555351.html
Copyright © 2020-2023  润新知