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的人一些帮助。本文中涉及的源码

0 0
原创粉丝点击