[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方法支持工厂创建的示例;通过EndNewCancelNew方法实现新项的事务性提交和回滚。

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

你可能感兴趣的:(datagridview)