WinForm DataGridView学习小记(2)
来源:互联网 发布:白银期货分析软件 编辑:程序博客网 时间:2024/06/10 11:01
在工作中,需要自定义DataGridView中承载的控件,就简单地学习了一下,上篇文章把DGV中简单的一些用法,包括“非绑定”方式的填充和“绑定”方式的填充数据方式介绍了一下,还简单介绍了更改列的显示内容的情况,这篇主要介绍如何让DGV能够承载自定义控件的情况,应该来讲,对于我这种WINFORM菜鸟来讲,这部分内容还是有点难度的,MSDN上的相关介绍是这篇文章的主要参考,具体地址是这里
简单地介绍下我想实现的效果,首先扩展系统自带的ComboBox控件,让它自动地加载系统定义的颜色,然后在DGV中利用该控件来完成拾取颜色的功能,最终实现的效果如下图所示,左图是DGV加载后的效果,右图为进入拾取颜色状态时的效果.
废话不多说,下来马上来一步步地实现这个例子。第一步,我们要通过扩展ComboBox来实现加载系统自定义颜色的ComboBox控件,这步是比较简单的,直接上代码:
/// <summary> /// An example to customize control in datagridview /// </summary> class CustomComboBox: ComboBox {#region constructor public CustomComboBox() : base() { this.DrawMode = DrawMode.OwnerDrawFixed; //要把模式调成用户自行绘制的模式,否则没有效果 this.DropDownStyle = ComboBoxStyle.DropDownList; //下拉列表风格,用户无法编辑列表内容 addColors(); } private void addColors() { //add colors Items.Clear(); Array colors = System.Enum.GetNames(typeof(KnownColor)); //这里有两种方法,getNames和getValues,有细微的区别 foreach (object obj in colors) Items.Add(obj); }#endregion#region override function protected override void OnDrawItem(DrawItemEventArgs e) { if (e.Index < 0) return; Rectangle rect = e.Bounds; rect.Inflate(-2, -2); Color color=Color.FromName(Items[e.Index] as String); Brush brush=new SolidBrush(color); Random random = new Random(); //很简陋的取“反色”的方式来显示文字 Brush brushForString = new SolidBrush(Color.FromArgb(255 - color.R,255 - color.G,255 - color.B)); e.Graphics.DrawRectangle(Pens.Black, rect); e.Graphics.FillRectangle(brush, rect); e.Graphics.DrawString(Items[e.Index] as String, e.Font, brushForString, rect.Location); }#endregion在构造函数中,我们调用addColors方法加载了系统自定义的所有颜色,然后重载onDrawItem来自定义列表的外观,这样一个定制的控件就完成了,还是比较轻松的。
接下来我们就要在DGV中使用这个控件了,查阅MSDN的文档可以看到,如果要让DGV能够承载自定义控件,要完成以下的事情:
1. 自定义控件,并且该控件要实现IDataGridViewEditingControl接口
2.继承DataGridViewCell类,并重载InitializeEditingControl方法,在方法中实现承载控件的初始化和赋值操作
3. 继承DataGridViewColumn类,使用继承的DataGridViewCell类作为模板
在我们实现这些步骤之前,有一个操作上的逻辑还是有必要明确一下,当DGV的表格中使用编辑控件时,当用户点击单元格时,DGV就会调用该单元格所在列DataGridViewColumn的单元格模板,也就是DataGridViewCell中的InitializeEditingControl方法,在这个方法中,把显示在单元格上的值(AKA:Cell value)转化成编辑控件的值,而显示在编辑控件上的值也被称为FormattedValue,用户通过编辑控件修改值以后,会通过DataGridViewCell的ParseFormattedValue方法将FormattedValue转化成CellValue,同时退出编辑状态.我认为这个操作逻辑可能是在学习并实现DGV承载自定义控件过程中最模糊也最重要的一个点,理解这个过程对于实现IDataGridViewEditingControl接口中各种方法的意义有很直接的作用。
好了,我们首先来完成CustomComboBox的IDataGridViewEditingControl接口实现,代码如下,哦,对了,不要忘了在CustomComboBox的后面加上接口声明:
#region IDataGridViewEdtingControl Interface // Summary: // Gets or sets the System.Windows.Forms.DataGridView that contains the cell. // // Returns: // The System.Windows.Forms.DataGridView that contains the System.Windows.Forms.DataGridViewCell // that is being edited; null if there is no associated System.Windows.Forms.DataGridView. // 用来指示该控件的宿主DGV控件 private DataGridView m_gridView; public DataGridView EditingControlDataGridView { get { //Debug.WriteLine("Get EditingControlDataGridView"); return m_gridView; } set { //Debug.WriteLine("Set EditingControlDataGridView"); m_gridView = value; } } // // Summary: // Gets or sets the formatted value of the cell being modified by the editor. // // Returns: // An System.Object that represents the formatted value of the cell. // 该属性用来控制编辑控件的显示值(AKA:FormattedValue),在进入编辑状态时,单元格会调用InitilizeEdintingControl方法来设置这个属性, //而在退出编辑状态之前,单元格也会通过 ParsingFormattedValue方法来解析这个值,并将它转化成单元格的值 public object EditingControlFormattedValue { get { //Debug.WriteLine("Get EditingControlFormattedValue"); if (this.SelectedItem != null) return this.SelectedItem.ToString(); else return String.Empty; } set { //Debug.WriteLine("Set EditingControlFormattedValue"); if (value is String) { this.SelectedItem = value as String; } } } // // Summary: // Gets or sets the index of the hosting cell's parent row. // // Returns: // The index of the row that contains the cell, or –1 if there is no parent // row. //单元格所在行索引 private int m_nRowIndex = -1; public int EditingControlRowIndex { get { //Debug.WriteLine("Get EditingControlRowIndex"); return m_nRowIndex; } set { //Debug.WriteLine("Set EditingControlRowIndex"); m_nRowIndex = value; } } // // Summary: // Gets or sets a value indicating whether the value of the editing control // differs from the value of the hosting cell. // // Returns: // true if the value of the control differs from the cell value; otherwise, // false. // 指示编辑控件的值是否改变 private bool m_bValueChanged = false; public bool EditingControlValueChanged { get { //Debug.WriteLine("Get EditingControlValueChanged"); return m_bValueChanged; } set { //Debug.WriteLine("Set EditingControlValueChanged"); m_bValueChanged = value; } } // // Summary: // Gets the cursor used when the mouse pointer is over the System.Windows.Forms.DataGridView.EditingPanel // but not over the editing control. // // Returns: // A System.Windows.Forms.Cursor that represents the mouse pointer used for // the editing panel. public Cursor EditingPanelCursor { get { //Debug.WriteLine("Get EditingPanelCursor"); return base.Cursor; } } // // Summary: // Gets or sets a value indicating whether the cell contents need to be repositioned // whenever the value changes. // // Returns: // true if the contents need to be repositioned; otherwise, false. //指示当单元格内容改变时,是否需要复位,这个属性的作用目前还不是很清楚 public bool RepositionEditingControlOnValueChange { get { //Debug.WriteLine("Get RepositionEditingControlOnValueChange"); return false; } } // Summary: // Changes the control's user interface (UI) to be consistent with the specified // cell style. // // Parameters: // dataGridViewCellStyle: // The System.Windows.Forms.DataGridViewCellStyle to use as the model for the // UI. // 在编辑控件显示之前,会调用该方法,给用户再一次修改编辑控件的外观的机会(比如背景色,字体之类的),使得它与单元格的样式保持一致 public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) { //do nothing Debug.WriteLine("ApplyCellStyleToEditingControl"); //SelectedItem = dataGridViewCellStyle.BackColor.Name; } // // Summary: // Determines whether the specified key is a regular input key that the editing // control should process or a special key that the System.Windows.Forms.DataGridView // should process. // // Parameters: // keyData: // A System.Windows.Forms.Keys that represents the key that was pressed. // // dataGridViewWantsInputKey: // true when the System.Windows.Forms.DataGridView wants to process the System.Windows.Forms.Keys // in keyData; otherwise, false. // // Returns: // true if the specified key is a regular input key that should be handled by // the editing control; otherwise, false. // 指示编辑控件希望自己来捕获哪些按键事件,返回true代表控件需要自己来处理这些事件 // 返回false表示控件不处理这些事件,交由DGV来处理 public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) { Debug.WriteLine("EditingControlWantsInputKey"); switch (keyData) { //case Keys.Left: //case Keys.Right: case Keys.Down: case Keys.Up: return true; default: return !dataGridViewWantsInputKey; } } // // Summary: // Retrieves the formatted value of the cell. // // Parameters: // context: // A bitwise combination of System.Windows.Forms.DataGridViewDataErrorContexts // values that specifies the context in which the data is needed. // // Returns: // An System.Object that represents the formatted version of the cell contents. // 得到编辑的formattedValue public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) { Debug.WriteLine("GetEditingControlFormattedValue"); return EditingControlFormattedValue; } // // Summary: // Prepares the currently selected cell for editing. // // Parameters: // selectAll: // true to select all of the cell's content; otherwise, false. public void PrepareEditingControlForEdit(bool selectAll) { Debug.WriteLine("PrepareEditingControlForEdit"); } //重载的ComboBox的事件,指示编辑控件的值已经改变 protected override void OnSelectedIndexChanged(EventArgs eventargs) { //Debug.WriteLine("OnSelectedIndexChanged"); // Notify the DataGridView that the contents of the cell // have changed. m_bValueChanged = true; this.EditingControlDataGridView.NotifyCurrentCellDirty(true); //注意到这里,是为了通知DGV当前单元格的内容已经作了修改 base.OnSelectedIndexChanged(eventargs); } //重载的ComboBox的事件,指示编辑控件的值已经改变 protected override void OnSelectedItemChanged(EventArgs e) { //Debug.WriteLine("OnSelectedItemChanged"); m_bValueChanged = true; this.EditingControlDataGridView.NotifyCurrentCellDirty(true); //这里也是 base.OnSelectedItemChanged(e); }#endregion
到这里,自定义控件的第一个步骤,另外,在每个方法的实现中,我都用Debug输出了该方法的信息,是为了在后续中通过这些信息更好地跟踪这些方法调用的顺序,而且我也保留了原始的英文注释,方便将来查阅和对照,好吧,接着来实现第二步:
class CustomDataGridViewCell: DataGridViewTextBoxCell //为了方便,我们直接从DataGridViewTextBoxCell来继承 { public CustomDataGridViewCell() :base() { } // // Summary: // Attaches and initializes the hosted editing control. // // Parameters: // rowIndex: // The index of the row being edited. // // initialFormattedValue: // The initial value to be displayed in the control. // // dataGridViewCellStyle: // A cell style that is used to determine the appearance of the hosted control. //该方法是在进入编辑状态时被调用,主要作用是利用CellValue值来初始化编辑控件的显示值,即FormattedValue public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) { Debug.WriteLine("InitializeEditingControl"); CustomComboBox editControl = this.DataGridView.EditingControl as CustomComboBox; if (editControl == null) { editControl = new CustomComboBox(); editControl.SelectedIndex = 0; } else { editControl.SelectedItem = dataGridViewCellStyle.BackColor.Name; } base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle); } // // // Returns: // A System.Type representing the data type of the value in the cell. // 该属性指示的是单元的值类型 public override Type ValueType { get { //Debug.WriteLine("Return ValueType"); return typeof(String); } } // // Summary: // Gets the type of the cell's hosted editing control. // // Returns: // A System.Type representing the System.Windows.Forms.DataGridViewTextBoxEditingControl // type. // 该属性指示的是编辑控件的类型 public override Type EditType { get { Debug.WriteLine("Return EditType"); return typeof(CustomComboBox); } } public override object DefaultNewRowValue { get { Debug.WriteLine("Return DefaultNewRowValue"); return System.Drawing.Color.Black.Name; } } // // Summary: // Converts a value formatted for display to an actual cell value. // // Parameters: // formattedValue: // The display value of the cell. // // cellStyle: // The System.Windows.Forms.DataGridViewCellStyle in effect for the cell. // // formattedValueTypeConverter: // A System.ComponentModel.TypeConverter for the display value type, or null // to use the default converter. // // valueTypeConverter: // A System.ComponentModel.TypeConverter for the cell value type, or null to // use the default converter. // // Returns: // The cell value. // // Exceptions: // System.ArgumentNullException: // cellStyle is null. // // System.FormatException: // The System.Windows.Forms.DataGridViewCell.FormattedValueType property value // is null.-or-The System.Windows.Forms.DataGridViewCell.ValueType property // value is null.-or-formattedValue cannot be converted. // // System.ArgumentException: // formattedValue is null.-or-The type of formattedValue does not match the // type indicated by the System.Windows.Forms.DataGridViewCell.FormattedValueType // property. //在退出编辑状态之前,通过调用该方法,完成控件的显示值(FormattedValue)到单元的值(CellValue)的转换 public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, System.ComponentModel.TypeConverter formattedValueTypeConverter, System.ComponentModel.TypeConverter valueTypeConverter) { Debug.WriteLine("ParseFormmatedValue"); this.Style.BackColor = System.Drawing.Color.FromName(formattedValue as String); //利用控件中拾取的值,将单元格的背景色设为相应的颜色 return base.ParseFormattedValue(formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter); }这样,我们就完成了第二步,第三步的话相对来讲比较简单,只要把列的模板设置为我们继承的Cell类就可以了,如下:
class CustomDataGridViewColumn: DataGridViewColumn { public CustomDataGridViewColumn() : base(new CustomDataGridViewCell()) //将列的单元格模板设置了自定义的类 { } public override DataGridViewCell CellTemplate { get { Debug.WriteLine("Get CellTemplate"); return new CustomDataGridViewCell(); } set { Debug.WriteLine("Set CellTemplate"); // Ensure that the cell used for the template is a CalendarCell. if (value != null && !value.GetType().IsAssignableFrom(typeof(CustomDataGridViewCell))) { throw new InvalidCastException("Must be a CalendarCell"); } base.CellTemplate = value; } }这样,我们就完成了所有的步骤,马上来试试效果吧:
class CustomClass //首先先定义个简单的类来存储数据 { public CustomClass(Color color) { m_color = color; } private Color m_color = Color.Empty; //如果为只读属性,则作为数据源填充时,双击后不会激发InitializeEditingControl事件 public Color InnerColor { get { return m_color; } set { m_color = value; } } }private List<CustomClass> m_listColors = new List<CustomClass>(); private void Form1_Load(object sender, EventArgs e) { //testListSerialization(); this.dataGridView1.DataSource = bindingSource1; dataGridView1.AutoGenerateColumns = true;//让DGV显示它自动为我们填充的列内容,方便比较 bindingSource1.DataSource = m_listColors; //绑定数据源 //dataGridView1.Columns.Clear(); CustomDataGridViewColumn col = new CustomDataGridViewColumn(); //DataGridViewTextBoxColumn col = new DataGridViewTextBoxColumn(); col.DataPropertyName = "InnerColor"; //绑定数据源中的某个属性,这种用法已经在上篇文章中提到 col.HeaderText = "Color"; this.dataGridView1.Columns.Add(col); }在这个例子中,我们简单地定义了一个类,用来存储颜色列表,使用中把m_listColors绑定到DGV上,然后利用上述我们自定义的列类型生成一个列,两个列的显示效果形成对比,如下图,左中右分别表示:列的显示效果、右列进入编辑状态,左列进入编辑状态时的效果。另外,值得一提的是,我们使用DataPropertyName属性将InnerColor属性绑定到控件上,如果InnerColor只设置为可读属性时,点击单元格,并不会进入编辑状态,也不会调用 InitializeEditingControl方法
关于DGV承载自定义控件的阐述到这里基本上已经完整了,不过,为了验证文章开始时说的操作逻辑的事情,我在代码中加了一些调试的代码,既然已经加了,就看看结果呗:
这个是我在右列中执行 进入编辑状态-修改值-退出编辑状态 时的输出结果,可以看到,基本上和之前说的一致,首先先得到编辑控件的类型,然后进入初始化,控件显示样式,最后在退出前触发解析方法。
终于把这个文章写完了,关于DGV的学习,也基本上比较全面了,当然很多关于DGV的高级用法在这里都没有涉及,只是有了这些基础,再做其它的应用应该都能很快上手了,希望这个文章能给要学习DGV的人一些帮助。本文中涉及的源码
- WinForm DataGridView学习小记(2)
- WinForm DataGridView学习小记(1)
- WinForm TreeView学习小记(1)
- winform小记
- C# winform DataGridView 常用属性(全)
- MVC5学习小记(2)
- DataGridView的使用小记
- DataGridView学习笔记(一):DataGridView简介
- WinForm DataGridView CheckBox
- winform DataGridView 属性说明
- WinForm DataGridView 显示行号
- winform datagridview 绑定
- Winform DataGridView 删除、修改
- winform 绑定数据 dataGridView
- winform 打印datagridview表格
- winform打印DataGridView I
- winform DataGridView 属性说明
- winform DataGridView 显示序号
- Fedora12 中安装中文字体
- Windows异步IO
- iPad的应用程序开发和iPad的游戏开发简介
- UISearchBar 按钮的文字和颜色更改
- iPad的Web应用程序开发的有效替代原生应用程序
- WinForm DataGridView学习小记(2)
- Web运作机制
- DIY的iPhone维修的优势
- 创建测试套件(Creating Test Suites)
- BB枪史
- Linux I2C驱动完全分析(一)
- 3D游戏发展进步历年
- 为插件创建测试(Creating Tests for Plugins)
- 善用快捷键,玩转Windows 7 便签程序