小菜的系統框架界面設計-數據的完美呈現(DataGridView擴展)

背景

今天在作系統報表的過程當中,我想實現批量操做DataGridView中的數據,在列中加複選框,經過一個事件觸發進行全選或取消,但是在外面添加按鈕,這種模式雖然可以實現,可是從系統界面設計的角度,美觀和靈活性就差不少了,可否在DataGridView頭標題欄上呈現複選框,經過這個頭標題複選框來對這一列的複選框,這樣是否是更靈活,也美觀一點?ide

問題

但是,找了半天,發現微軟原始的DataGridView頭標題欄沒有CheckBox的功能,鬱悶了~~哭泣的臉this

我在伍兄的博客找到有關於擴展DataGridVew在頭標題欄添加全選功能按鈕的功能(不是源碼開源),並且他的這個程序集也不能知足個人需求,怎麼辦?spa

只有靠我自已去探索了,我在傳統的DataGridView中實現了這個功能,可是整合進個人換膚組件中不能實現,Why? 我找了好友Strong一塊兒研究,自已摸不清方向,最後仍是他找到了問題點,但是卻沒法入手?(no any solution)debug

最後,通過自已的努力一步一步debug, 終於解決了問題,並整合進自已的換膚組件中,我的以爲有必要總結一下。設計

在總結技術點前,先展現一下個人成果,而後再作擴展說明,以下:3d

(圖一)Office2007Blue效果code

1

(圖二) Office2007Silver效果orm

2

 

傳統的解決方案

在傳統的DataGridView上,實現基本上沒有什麼難處,只要按以下的步驟去操做就能夠了。blog

添加一個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旁出現的文字 
        }

總結

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

元芳,你怎麼看?鬨堂大笑

相關文章
相關標籤/搜索