• 怎样让WinForms下DataGrid可以像ASP.NET下的DataGrid一样使用自定义的模板列


    昨天被问到一个问题:怎么把WinForms里的DataGrid的绑定了数据库bit字段的列默认显示的CheckBox换成“男”和“女”,也就是说怎么样像ASP.NET的模板列那样可以自定义。(此处不考虑在SQL在用Case把数据结果转换了)
    由于,基本没有搞过WinForms,所以这个问题弄了很久才搞掂,可能对于WinForms高手来说,这是一个很简单的问题。(我搜了一下网页,没有找到直接的解决方案,问了一些搞过WinForms的朋友,也没有直接的解决方案,所以把我的解决方案放在博客园首页,DUDU如觉不适合,请移除。)解决这个问题的副作用就是对WinForms的机制有了一点了解。

    最终实现效果:


    开始的思路还是ASP.NET的思路,企图用WinForms的DataGrid的事件来实现。试了ControlAdde,BindingComplated等事件,都没有用,有些能拿到绑定时的控件,却拿不到对应的数据。
    后来有朋友启发用CurrencyManager来实现,试了半天,能拿到数据,又拿不到对应的绑定生成的控件了。
    晕,后来还是控件开发的思想,既然可以用DataGridTextBoxColumnStyle和DataGridBoolColumnStyle分别生成TextBox和CheckBox,为什么不可以自定义一个DataGridColumnStyle来实现自定义呢?
    结果还真是可行的:

    //用Label显示"男"和"女",并且点击一次变成相反的
        class DataGridCustomBoolColumnStyle : DataGridColumnStyle
        
    {
            
    private Label _customLabel = new Label();
            
    private bool _isEditing = false;
            
    public DataGridCustomBoolColumnStyle()
            
    {
                _customLabel.Visible 
    = false;
                _customLabel.Click 
    +=new EventHandler(_customLabel_Click);
            }

            
    protected override void Abort(int rowNum)
            
    {
                _isEditing 
    = false;
                Invalidate();
            }


            
    void _customLabel_Click(object sender, EventArgs e)
            
    {
                Label lbl 
    = sender as Label;
                
    if (lbl.Text == "")
                    lbl.Text 
    = "";
                
    else
                    lbl.Text 
    = "";
                
    this._isEditing = true;
                
    base.ColumnStartedEditing(_customLabel);
            }


            
    protected override object GetColumnValueAtRow(CurrencyManager source, int rowNum)
            
    {
                
    object o = base.GetColumnValueAtRow(source, rowNum);
                
    bool value = true;
                
    if (Convert.IsDBNull(o))
                    value 
    = true;
                
    else
                
    {
                    value 
    = (bool)o;
                }

                
    return value;
            }

            
    protected override void SetDataGridInColumn(DataGrid value)
            
    {
                
    base.SetDataGridInColumn(value);
                
    if (_customLabel.Parent != null)
                
    {
                    _customLabel.Parent.Controls.Remove(_customLabel);
                }

                
    if (value != null)
                
    {
                    value.Controls.Add(_customLabel);
                }

            }


            
    protected override bool Commit(CurrencyManager dataSource, int rowNum)
            
    {
                _customLabel.Bounds 
    = Rectangle.Empty;
                _customLabel.Visible 
    = false;

                
    if (!_isEditing)
                    
    return true;

                _isEditing 
    = false;

                
    try
                
    {
                    
    bool value = (_customLabel.Text == "");
                    SetColumnValueAtRow(dataSource, rowNum, value);
                }

                
    catch (Exception)
                
    {
                    Abort(rowNum);
                    
    return false;
                }


                Invalidate();
                
    return true;
            }


            
    protected override void Edit(CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string displayText, bool cellIsVisible)
            
    {
                
    bool value = (bool)GetColumnValueAtRow(source, rowNum);
                
    if (cellIsVisible)
                
    {
                    _customLabel.Bounds 
    = new Rectangle(bounds.X + 2, bounds.Y + 2, bounds.Width - 4, bounds.Height - 4);
                    _customLabel.Visible 
    = true;
                }

                
    else
                
    {
                    _customLabel.Visible 
    = false;
                }

                _customLabel.Text 
    = value ? "" : "";

                
    if (_customLabel.Visible)
                
    {
                    DataGridTableStyle.DataGrid.Invalidate(bounds);
                }


                _customLabel.BackColor 
    = Color.Red;
            }


            
    protected override int GetMinimumHeight()
            
    {
                
    return _customLabel.PreferredHeight + 4;
            }


            
    protected override int GetPreferredHeight(System.Drawing.Graphics g, object value)
            
    {
                
    return _customLabel.PreferredHeight + 4;
            }


            
    protected override System.Drawing.Size GetPreferredSize(System.Drawing.Graphics g, object value)
            
    {
                
    return new Size(40, _customLabel.PreferredHeight + 4);
            }



            
    protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
            
    {
                
    bool value = (bool)GetColumnValueAtRow(source, rowNum);
                g.FillRectangle(backBrush, bounds);
                bounds.Offset(
    02);
                bounds.Height 
    -= 2;
                g.DrawString(value 
    ? "" : "", DataGridTableStyle.DataGrid.Font, foreBrush, bounds);
            }


            
    protected override void Paint(Graphics g,Rectangle bounds,CurrencyManager source,int rowNum)
            
    {
                Paint(g, bounds, source, rowNum, 
    false);
            }


            
    protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source,int rowNum,bool alignToRight)
            
    {
                Brush coreBrush 
    = _isEditing ? Brushes.Red : Brushes.White;
                Brush backBrush 
    = _isEditing ? Brushes.Blue : Brushes.Black;
                Paint(
                    g, bounds,
                    source,
                    rowNum,
                    coreBrush,
                    backBrush,
                    alignToRight);
            }



        }
    //使用方法
                DataGridTableStyle dgts = new DataGridTableStyle();
                dgts.MappingName 
    = this.dataSet11.Employees.TableName;
                
    this.dataGrid1.TableStyles.Add(dgts);

                GridColumnStylesCollection gcsc 
    = dataGrid1.TableStyles[0].GridColumnStyles;

                DataGridBoolColumn dgbc 
    = gcsc[gcsc.Count - 1as DataGridBoolColumn;

                DataGridCustomBoolColumnStyle dgcbc 
    = new DataGridCustomBoolColumnStyle();
                dgcbc.MappingName 
    = dgbc.MappingName;
                gcsc.Remove(dgbc);
                gcsc.Add(dgcbc);
                
    this.employeesTableAdapter.Fill(this.dataSet11.Employees);
                
    this.dataGrid1.DataSource = this.dataSet11.Employees;

    这个实现很简单,数据与显示之间的映射是固定的,既然简单的能实现,我们再来实现个复杂的,用ComboBox来表示一些固定值的选择,比如enum和bool,因为数据库中的数据并没有enum,所以,这个DataGridComboBoxColumnStyle提供两个委托,可以数据到ComboBox项和ComboBox项到数据之间做一个处理

    //可以灵活适应各种情况的ComboBox列样式
        public delegate string FormatValueToString(object value);
        
    public delegate object ParseStringToValue(string value);
        
    class DataGridComboBoxColumnStyle:DataGridColumnStyle
        
    {
            
    public FormatValueToString FormartDelegate;
            
    public ParseStringToValue ParseDelegate;
            
    private bool _isEditing = false;
            
    private ComboBox _combo = new ComboBox();
            
    public ComboBox InnerComboBox
            
    {
                
    get
                
    {
                    
    return _combo;
                }

            }

            
    public DataGridComboBoxColumnStyle()
            
    {
                _combo.SelectedIndexChanged 
    += new EventHandler(_combo_SelectedIndexChanged);
                _combo.Visible 
    = false;
            }


            
    void _combo_SelectedIndexChanged(object sender, EventArgs e)
            
    {
                
    this._isEditing = true;
                
    base.ColumnStartedEditing(_combo);
            }

            
    protected override void Abort(int rowNum)
            
    {
                _isEditing 
    = false;
                Invalidate();
            }



            
    protected override void SetDataGridInColumn(DataGrid value)
            
    {
                
    base.SetDataGridInColumn(value);
                
    if (_combo.Parent != null)
                
    {
                    _combo.Parent.Controls.Remove(_combo);
                }

                
    if (value != null)
                
    {
                    value.Controls.Add(_combo);
                }

            }


           

            
    protected override bool Commit(CurrencyManager dataSource, int rowNum)
            
    {
                _combo.Bounds 
    = Rectangle.Empty;
                _combo.Visible 
    = false;

                
    if (!_isEditing)
                    
    return true;
                _isEditing 
    = false;
                
                
    try
                
    {
                    
    string value = _combo.SelectedText;
                    SetColumnValueAtRow(dataSource, rowNum, value);
                }

                
    catch (Exception)
                
    {
                    Abort(rowNum);
                    
    return false;
                }


                Invalidate();
                
    return true;
            }

            
    protected override object GetColumnValueAtRow(CurrencyManager source, int rowNum)
            
    {
                
    object value = base.GetColumnValueAtRow(source, rowNum);
                
    if (Convert.IsDBNull(value))
                
    {
                    value 
    = this.NullText;
                }

                
    if (FormartDelegate != null)
                
    {
                    value 
    = FormartDelegate(value);
                }

                
    return value;
            }

            
    protected override void SetColumnValueAtRow(CurrencyManager source, int rowNum, object value)
            
    {
                
    if(ParseDelegate != null)
                
    {
                    value 
    = ParseDelegate((string)value);
                }

                
    base.SetColumnValueAtRow(source, rowNum, value);
            }

            
    protected override void Edit(CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string displayText, bool cellIsVisible)
            
    {
                
    string value = (string)GetColumnValueAtRow(source, rowNum);
                
    if (cellIsVisible)
                
    {
                    _combo.Bounds 
    = new Rectangle(bounds.X + 2, bounds.Y + 2, bounds.Width - 4, bounds.Height - 4);
                    _combo.Visible 
    = true;
                }

                
    else
                
    {
                    _combo.Visible 
    = false;
                }

                
    for (int i = 0; i < _combo.Items.Count; i++)
                
    {
                    
    if (value == (string)_combo.Items[i])
                    
    {
                        _combo.SelectedIndex 
    = i;
                        
    break;
                    }

                }


                
    if (_combo.Visible)
                
    {
                    DataGridTableStyle.DataGrid.Invalidate(bounds);
                }


            }


            
    protected override int GetMinimumHeight()
            
    {
                
    return _combo.PreferredHeight + 4;
            }


            
    protected override int GetPreferredHeight(System.Drawing.Graphics g, object value)
            
    {
                
    return _combo.PreferredHeight + 4;
            }


            
    protected override System.Drawing.Size GetPreferredSize(System.Drawing.Graphics g, object value)
            
    {
                
    return new Size(100, _combo.PreferredHeight + 4);
            }



            
    protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
            
    {
                
    string value = (string)GetColumnValueAtRow(source, rowNum);
                g.FillRectangle(backBrush, bounds);
                bounds.Offset(
    02);
                bounds.Height 
    -= 2;
                g.DrawString(value, DataGridTableStyle.DataGrid.Font, foreBrush, bounds);
            }


            
    protected override void Paint(Graphics g,Rectangle bounds,CurrencyManager source,int rowNum)
            
    {
                Paint(g, bounds, source, rowNum, 
    false);
            }


            
    protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source,int rowNum,bool alignToRight)
            
    {
                Brush coreBrush 
    = _isEditing ? Brushes.Red : Brushes.White;
                Brush backBrush 
    = _isEditing ? Brushes.Blue : Brushes.Black;
                Paint(
                    g, bounds,
                    source,
                    rowNum,
                    coreBrush,
                    backBrush,
                    alignToRight);

                
            }



        }
    //使用方法
                DataGridTableStyle dgts = new DataGridTableStyle();
                dgts.MappingName 
    = this.dataSet11.Employees.TableName;
                
    this.dataGrid1.TableStyles.Add(dgts);

                GridColumnStylesCollection gcsc 
    = dataGrid1.TableStyles[0].GridColumnStyles;

                DataGridBoolColumn dgbc 
    = gcsc[gcsc.Count - 1as DataGridBoolColumn;


                DataGridComboBoxColumnStyle dgcbc 
    = new DataGridComboBoxColumnStyle();
                dgcbc.MappingName 
    = dgbc.MappingName;
                
    //这里定义ComboBOx的样式和项,因为整个ComboBox都公开了,所以随你怎么设置都行
                dgcbc.InnerComboBox.Items.Add("");
                dgcbc.InnerComboBox.Items.Add(
    "");
                dgcbc.InnerComboBox.DropDownStyle 
    = ComboBoxStyle.DropDownList;
                
    //这里定义数据和ComboBOx项之间如何转换
                dgcbc.ParseDelegate = new ParseStringToValue(ParseStringToBool);
                dgcbc.FormartDelegate 
    = new FormatValueToString(FormatBoolToString);

                gcsc.Remove(dgbc);
                gcsc.Add(dgcbc);
                
    this.employeesTableAdapter.Fill(this.dataSet11.Employees);
                
    this.dataGrid1.DataSource = this.dataSet11.Employees;


    熟悉WinForms的设计思路之后,我们又可以像用ASP.NET的DataGird一样用DataGrid了。
    WinForms我是新手,请多指教。。。

  • 相关阅读:
    注册表编程初步
    内层位移换算到外层
    运算符重载
    按右手定则求已经知三点的法向量
    链接错误 2001、2019
    UML规则笔记
    关于动态链接库、静态链接库
    05 nfs、rsync、inotify综合案例
    rsync本地同步
    05 NFS基础知识
  • 原文地址:https://www.cnblogs.com/think/p/TemplateColumn.html
Copyright © 2020-2023  润新知