• 小菜的系统框架界面设计-数据的完美呈现(DataGridView扩展)


    背景

    今天在做系统报表的过程中,我想实现批量操作DataGridView中的数据,在列中加复选框,通过一个事件触发进行全选或取消,可是在外面添加按钮,这种模式虽然能够实现,但是从系统界面设计的角度,美观和灵活性就差很多了,能否在DataGridView头标题栏上呈现复选框,通过这个头标题复选框来对这一列的复选框,这样是不是更灵活,也美观一点?

    问题

    可是,找了半天,发现微软原始的DataGridView头标题栏没有CheckBox的功能,郁闷了~~哭泣的脸

    我在伍兄的博客找到有关于扩展DataGridVew在头标题栏添加全选功能按钮的功能(不是源码开源),而且他的这个程序集也不能满足我的需求,怎么办?

    只有靠我自已去探索了,我在传统的DataGridView中实现了这个功能,但是整合进我的换肤组件中不能实现,Why? 我找了好友Strong一起研究,自已摸不清方向,最后还是他找到了问题点,可是却无法入手?(no any solution)

    最后,经过自已的努力一步一步debug, 终于解决了问题,并整合进自已的换肤组件中,个人觉得有必要总结一下。

    在总结技术点前,先展示一下我的成果,然后再做扩展说明,如下:

    (图一)Office2007Blue效果

    1

    (图二) Office2007Silver效果

    2

    传统的解决方案

    在传统的DataGridView上,实现基本上没有什么难处,只要按如下的步骤去操作就可以了。

    添加一个DataGridViewColumnHeaderCellW.cs,继承DataGridViewColumnHeaderCell,

    源码如下:

    public class DataGridViewColumnHeaderCellW : DataGridViewColumnHeaderCell
        {
            public object HeaderTextDataSource { get; set; }
            private Type _dataSourceType = null;
            public Type DataSourceType
            {
                get
                {
                    if (HeaderTextDataSource != null && _dataSourceType == null)
                        _dataSourceType = HeaderTextDataSource.GetType();
                    return _dataSourceType;
                }
                set { _dataSourceType = value; }
            }
            public string FieldName { get; set; }
            public string Prefix { get; set; }
            public string Suffix { get; set; }
    
    
            Point checkBoxLocation;
            Size checkBoxSize;
            bool _checked = false;
            Point _cellLocation = new Point();
            System.Windows.Forms.VisualStyles.CheckBoxState _cbState =
                System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
            public event datagridviewcheckboxHeaderEventHander OnCheckBoxClicked; 
            //绘制列头checkbox 
            protected override void Paint(System.Drawing.Graphics graphics,
                                          System.Drawing.Rectangle clipBounds,
                                          System.Drawing.Rectangle cellBounds,
                                          int rowIndex,
                                          DataGridViewElementStates dataGridViewElementState,
                                          object value,
                                          object formattedValue,
                                          string errorText,
                                          DataGridViewCellStyle cellStyle,
                                          DataGridViewAdvancedBorderStyle advancedBorderStyle,
                                          DataGridViewPaintParts paintParts)
            {
                base.Paint(graphics, clipBounds, cellBounds, rowIndex,
                           dataGridViewElementState, value,
                           formattedValue, errorText, cellStyle,
                           advancedBorderStyle, paintParts);
                Point p = new Point();
                Size s = CheckBoxRenderer.GetGlyphSize(graphics,
                                                       System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
                p.X = cellBounds.Location.X +
                      (cellBounds.Width / 2) - (s.Width / 2) - 1; //列头checkbox的X坐标 
                p.Y = cellBounds.Location.Y +
                      (cellBounds.Height / 2) - (s.Height / 2); //列头checkbox的Y坐标 
                _cellLocation = cellBounds.Location;
                checkBoxLocation = p;
                checkBoxSize = s;
                if (_checked)
                    _cbState = System.Windows.Forms.VisualStyles.
                                      CheckBoxState.CheckedNormal;
                else
                    _cbState = System.Windows.Forms.VisualStyles.
                                      CheckBoxState.UncheckedNormal;
                CheckBoxRenderer.DrawCheckBox
                    (graphics, checkBoxLocation, _cbState);
            }
    
            /// <summary> 
            /// 点击列头checkbox单击事件 
            /// </summary> 
            protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
            {
    
                Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
                if (p.X >= checkBoxLocation.X && p.X <=
                    checkBoxLocation.X + checkBoxSize.Width
                && p.Y >= checkBoxLocation.Y && p.Y <=
                    checkBoxLocation.Y + checkBoxSize.Height)
                {
                    _checked = !_checked;
    
    
                    //获取列头checkbox的选择状态 
                    datagridviewCheckboxHeaderEventArgs ex = new datagridviewCheckboxHeaderEventArgs();
                    ex.CheckedState = _checked;
    
                    object sender = new object();//此处不代表选择的列头checkbox,只是作为参数传递。应该列头checkbox是绘制出来的,无法获得它的实例 
    
                    if (OnCheckBoxClicked != null)
                    {
                        OnCheckBoxClicked(sender, ex);//触发单击事件 
                        this.DataGridView.InvalidateCell(this);
    
                    }
    
                }
                base.OnMouseClick(e);
            }

    在DataGridViewColumnHeaderCellW.cs内部,添加一个委托和继承EventArgs事件数据的基类:

    public delegate void datagridviewcheckboxHeaderEventHander(object sender, datagridviewCheckboxHeaderEventArgs e);
    
        //定义包含列头checkbox选择状态的参数类 
        public class datagridviewCheckboxHeaderEventArgs : EventArgs
        {
            private bool checkedState = false;
    
            public bool CheckedState
            {
                get { return checkedState; }
                set { checkedState = value; }
            }
        }

    如何调用?

    先在界面上添加一个DataGridView,并添加一列,选类型为DataGridViewCheckBoxColumn,在Form_Load事件中添加如下代码:

    private void Form1_Load(object sender, EventArgs e)
            {
                DataGridViewColumnHeaderCellW ch = new DataGridViewColumnHeaderCellW();
                ch.OnCheckBoxClicked += new datagridviewcheckboxHeaderEventHander(OnCheckBoxClicked);
                //第三列为DataGridViewCheckBoxColumn 
                DataGridViewCheckBoxColumn checkboxCol = this.dataGridView1.Columns[0] as DataGridViewCheckBoxColumn;
                checkboxCol.HeaderCell = ch;
                checkboxCol.HeaderCell.Value = string.Empty;//消除列头checkbox旁出现的文字 
            }

    没题解决了!

    展示一下,这个复选框暂放在最后一列,如下图:

    QQ图片20130621184411

    这个真是灰头土脸,像是灰姑娘那么丑。

    于是,我整合进我的换肤中,可是怎么也不能实现,标题栏就是不出来,结果成了如下图这样子:

    3

    最后经过一系列努力,定位问题在CellPainting事件中,重绘的过程把标题栏的checkbox效果覆盖了。

    我在此事件中添加如下代码:

    if (e.RowIndex == -1)
                {
                    if (!(_columnHeaderUpColor == Color.Transparent) && !(_columnHeaderDownColor == Color.Transparent) &&
                        !_columnHeaderUpColor.IsEmpty && !_columnHeaderDownColor.IsEmpty)
                    {
                        DrawLinearGradient(e.CellBounds, e.Graphics, _columnHeaderUpColor, _columnHeaderDownColor);
                        if (ShowColumnHeaderCheckBox)
                        {
                            e.Paint(e.ClipBounds, (DataGridViewPaintParts.All & ~DataGridViewPaintParts.Background));
                        }
                        else
                        {
                            DrawText(e); 
                        }
                        e.Handled = true;
                    }
                }

    问题终于解决,但是在代码中为什么用ShowColumnHeaderCheckBox?

    并不是所有的数据呈现功能都要有这个头标题栏复选框的功能,为了更好的兼容性,我在添加了这个属性,开发人员可以通过此属性灵活选择,默认是false。

    public bool showColumnHeaderCheckBox;
    
            public bool ShowColumnHeaderCheckBox
            {
                get
                {
                    return showColumnHeaderCheckBox;
                }
                set { showColumnHeaderCheckBox = value; }
            }

    在客户端这样去设就可以了。

    private void Form1_Load(object sender, EventArgs e)
            {
                InitParameterList();
                dataGridView1.ShowColumnHeaderCheckBox = true;//此处设为true
                DataGridViewColumnHeaderCellW ch = new DataGridViewColumnHeaderCellW();
                ch.OnCheckBoxClicked += new datagridviewcheckboxHeaderEventHander(OnCheckBoxClicked);
                //第三列为DataGridViewCheckBoxColumn 
                DataGridViewCheckBoxColumn checkboxCol = this.dataGridView1.Columns[0] as DataGridViewCheckBoxColumn;
                checkboxCol.HeaderCell = ch;
                checkboxCol.HeaderCell.Value = string.Empty;//消除列头checkbox旁出现的文字 
            }

    总结

    在研究一个新的东西时,我一般是先实现粗糙的功能,由浅入深渐渐细化这么一个演变的过程,毕竟灰姑娘一下要变成白雪公主也是要有个过程的。

    元芳,你怎么看?捧腹大笑

  • 相关阅读:
    day15内置函数
    day14生成器进阶
    day13迭代器、生成器
    day12装饰器进阶
    day11装饰器
    day10函数进阶
    day9函数
    day8文件操作
    正则表达式
    初识递归
  • 原文地址:https://www.cnblogs.com/aganqin/p/3148893.html
Copyright © 2020-2023  润新知