【Winform】-DataGridView實作"選擇全部"按鈕

 Winform算是一個成熟且存在已久的應用程式框架,然而DataGridView的欄位類別卻總是少東少西的。像是一次將所有Checkbox資料列打勾選擇的"全選"按鈕居然就沒有,因此本文將分享該按鈕的實作。

實作重點

1.該需求於原生欄位中已有類似的(DataGridViewCheckBoxColumn),本次實作重點最上頭多出一個全選的按鈕,因此直接繼承後擴充即可。

2.擴充重點在於標題欄位點選後插入"改變所有同一欄位的資料"的事件,因此需要於擴充的標題欄位(HeaderCell)中增加對應觸發事件,以及於Column建置時加入實作邏輯。

3.由於標題欄位固定都會出現一個Checkbox,因此繪製必須要手動處理。

4.OwningColumn.Width改變時會觸發Paint事件,因此寬度調整時不需要寫繪製圖案的程式碼。

 

實作程式碼

 public class DataGridViewSelectAllColumn : DataGridViewCheckBoxColumn
    {
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new DataGridViewSelectAllColumnHeaderCell HeaderCell
        {
            get => base.HeaderCell as DataGridViewSelectAllColumnHeaderCell;
            set => base.HeaderCell = value;
        }


        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public new DataGridViewColumnSortMode SortMode { get; } = DataGridViewColumnSortMode.NotSortable;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public new DataGridViewAutoSizeColumnMode AutoSizeMode { get; } = DataGridViewAutoSizeColumnMode.None;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public new DataGridViewTriState Resizable { get; } = DataGridViewTriState.False;

        public DataGridViewSelectAllColumn() : base()
        {
            this.DefaultHeaderCellType = typeof(DataGridViewSelectAllColumnHeaderCell);
            base.SortMode = this.SortMode;
            base.AutoSizeMode = this.AutoSizeMode;
            base.Resizable = this.Resizable;
            HeaderCell.OnClickEvent += Header_OnClickEvent;
        }

        protected override void OnDataGridViewChanged()
        {
            if (DataGridView != null)
            {
                DataGridView.CellContentClick += DataGridView_CellContentClick;
            }
            base.OnDataGridViewChanged();
        }

        private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == HeaderCell.ColumnIndex && e.RowIndex >= 0)
            {
                HeaderCell.Checked = false;
                DataGridView.EndEdit();
                DataGridView.Invalidate();
            }
        }

        /// <summary>
        /// 點選狀態改變
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Header_OnClickEvent(object sender, EventArgs e)
        {
            DataGridView.EndEdit();
            foreach (DataGridViewRow row in this.DataGridView.Rows)
            {
                var cell = row.Cells[Index] as DataGridViewCheckBoxCell;
                cell.Value = HeaderCell.Checked;
            }
            DataGridView.RefreshEdit();
        }
    }


    public class DataGridViewSelectAllColumnHeaderCell : DataGridViewColumnHeaderCell
    {

        internal static int GetPredictWidth(string text, Font font, Padding padding)
            => TextRenderer.MeasureText("  " + text, font).Width + padding.Left + padding.Right;
        const int MinPaddingLeft = 5;
        public event EventHandler OnClickEvent;
        public bool Checked { get; set; }
        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex,
            DataGridViewElementStates dataGridViewElementState, object value, object formattedValue, string errorText,
            DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            var strHeadetText = " " + (formattedValue as string ?? "");
            var sizeText = TextRenderer.MeasureText(strHeadetText, cellStyle.Font);

            var intPaddingLeft = cellStyle.Padding.Left < MinPaddingLeft
                ? MinPaddingLeft
                : cellStyle.Padding.Left;

            var locationCheckBox = new Point(
                    (cellBounds.X + intPaddingLeft),
                    cellBounds.Y + (int)Math.Ceiling((double)(cellBounds.Height - sizeText.Height) / 2)
                );
            var predictWidth = GetPredictWidth(formattedValue as string ?? "", cellStyle.Font, cellStyle.Padding);
            if (OwningColumn.Width != predictWidth)
            {
                OwningColumn.Width = predictWidth;
            }
            else
            {
                base.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, strHeadetText, strHeadetText, errorText, cellStyle, advancedBorderStyle, paintParts);
                var checkboxRectangle = new Rectangle(locationCheckBox, new Size(sizeText.Height, sizeText.Height));
                ControlPaint.DrawCheckBox(graphics, checkboxRectangle, Checked ? ButtonState.Flat | ButtonState.Checked : ButtonState.Flat);
            }
        }
        protected override void OnClick(DataGridViewCellEventArgs e)
        {
            Checked = (!Checked);
            OnClickEvent?.Invoke(this, EventArgs.Empty);
            base.OnClick(e);
        }
        public void PerformClick()
        {
            OnClick(new DataGridViewCellEventArgs(ColumnIndex, RowIndex));
        }
    }

 

實作DEMO