小菜的系統框架界面設計-數據的完美呈現(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旁出現的文字 
        }

總結

在研究一個新的東西時,我一般是先實現粗糙的功能,由淺入深漸漸細化這么一個演變的過程,畢竟灰姑娘一下要變成白雪公主也是要有個過程的。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM