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