MyDGVCheckBoxColumnHeaderCell.cs
- 建立 CheckBoxAll Event
- 在 Paint 內繪出 CheckBox 並把相關資訊紀錄在 Property
- OnMouseClick 中根據 Property 資訊,判斷使用者是否有點擊到 CheckBox 並觸發 CheckBoxAll Event
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace DGVCheckBoxAll
{
public class MyDGVCheckBoxColumnHeaderCell : DataGridViewColumnHeaderCell
{
private Point _headerCellCheckBoxLocation;
private Size _headerCellCheckBoxSize;
private Point _headerCellLocation;
public bool CheckBoxAllCheckedState { get; set; }
public MyDGVCheckBoxColumnHeaderCell(bool defaultValue)
{
CheckBoxAllCheckedState = defaultValue;
}
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)
{
base.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
// HeaderCellCheckBox 大小
Size checkBoxSize = CheckBoxRenderer.GetGlyphSize(graphics, CheckBoxState.UncheckedNormal);
// HeaderCellCheckBox 位置
Point checkBoxLocation = new Point();
checkBoxLocation.X = cellBounds.Location.X + (cellBounds.Width / 2) - (checkBoxSize.Width / 2);
checkBoxLocation.Y = cellBounds.Location.Y + (cellBounds.Height / 2) - (checkBoxSize.Height / 2);
// 把相關資訊記錄到 Property 上,方便後續使用
_headerCellLocation = cellBounds.Location;
_headerCellCheckBoxLocation = checkBoxLocation;
_headerCellCheckBoxSize = checkBoxSize;
CheckBoxState checkBoxState = CheckBoxAllCheckedState ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal;
CheckBoxRenderer.DrawCheckBox(graphics, _headerCellCheckBoxLocation, checkBoxState);
}
protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
// 變數 p 是指 Click 位置
Point p = new Point(e.X + _headerCellLocation.X, e.Y + _headerCellLocation.Y);
if (p.X >= _headerCellCheckBoxLocation.X &&
p.X <= _headerCellCheckBoxLocation.X + _headerCellCheckBoxSize.Width &&
p.Y >= _headerCellCheckBoxLocation.Y &&
p.Y <= _headerCellCheckBoxLocation.Y + _headerCellCheckBoxSize.Height)
{
CheckBoxAllCheckedState = !CheckBoxAllCheckedState;
OnCheckBoxAll(new CheckBoxAllEventArgs(CheckBoxAllCheckedState));
}
base.OnMouseClick(e);
}
public event CheckBoxAllEventHandler CheckBoxAll;
protected virtual void OnCheckBoxAll(CheckBoxAllEventArgs e)
{
if (CheckBoxAll != null)
CheckBoxAll(this, e);
}
}
public class CheckBoxAllEventArgs : EventArgs
{
public bool CheckedState { get; set; }
public CheckBoxAllEventArgs(bool checkedState = false)
{
CheckedState = checkedState;
}
}
public delegate void CheckBoxAllEventHandler(object sender, CheckBoxAllEventArgs e);
}
MyDGVCheckBoxColumn.cs
- 註冊 MyDGVCheckBoxColumnHeaderCell 的 CheckBoxAll Event 並對外開放
- 資料筆數和 ColumnCheckBox 為 true 資料比數是否相等,相等必須把 CheckBoxAll 設為 true
using System.Linq;
using System.Windows.Forms;
namespace DGVCheckBoxAll
{
public class MyDGVCheckBoxColumn : DataGridViewCheckBoxColumn
{
private MyDGVCheckBoxColumnHeaderCell _headerCell;
// 把 MyDGVCheckBoxColumnHeaderCell.CheckBBoxAll 事件對外開放
public event CheckBoxAllEventHandler CheckBoxAll
{
add
{
_headerCell.CheckBoxAll += value;
}
remove
{
_headerCell.CheckBoxAll -= value;
}
}
public MyDGVCheckBoxColumn(bool defaultValue = false)
{
_headerCell = new MyDGVCheckBoxColumnHeaderCell(defaultValue);
_headerCell.CheckBoxAll += HeaderCell_CheckBoxAll;
HeaderCell = _headerCell;
// 不可能在 MyDGVCheckBoxColumn 建構子內去設定 ColumnCheckBox 內的值,
// MyDGVCheckBoxColumn 產生時,DataGridView 還是 null,牽扯到控件生命週期
// CheckBoxAllSetValue(DefaultValue);
}
private void HeaderCell_CheckBoxAll(object sender, CheckBoxAllEventArgs e)
{
CheckBoxAllSetColumnCheckedState(e.CheckedState);
}
/// <summary>
/// 計算欄位 CheckBox 為 true 的資料筆數
/// </summary>
/// <returns>true 資料筆數</returns>
private int CheckedStateCount()
{
int result = 0;
if (DataGridView == null)
return result;
result = DataGridView.Rows
.OfType<DataGridViewRow>()
.Where(r => (bool)r.Cells[Index].EditedFormattedValue == true)
.Count();
return result;
}
/// <summary>
/// 判斷 DataGridView 資料筆數 和 Column CheckBox 為 true 資料筆數,是否一致
/// </summary>
/// <returns>資料筆數是否一致</returns>
private bool IsCheckState()
{
bool result = false;
if (DataGridView == null)
return result;
if (DataGridView.Rows.Count == CheckedStateCount())
result = true;
return result;
}
/// <summary>
/// 設定 CheckBoxAll 狀態
/// </summary>
public void SetCheckedState()
{
if ((HeaderCell is MyDGVCheckBoxColumnHeaderCell headerCell) == false)
return;
headerCell.CheckBoxAllCheckedState = IsCheckState();
// 以前是使用 DataGridView.Refresh() 來達到重繪目的,
// 後來發現有 DataGridView.InvalidateCell(),
// DataGridView 只需要重繪 HeaderCell 就行
DataGridView.InvalidateCell(_headerCell);
}
/// <summary>
/// 依據 CheckBoxAll 狀態,設定 Column CheckBox 狀態
/// <param name="checkedState">true 或 false</param>
/// </summary>
public void CheckBoxAllSetColumnCheckedState(bool checkedState)
{
// 避免 Focus 的資料,因為楚於 EditMode 狀態,而在 UI 沒有正確顯示是否勾選
DataGridView.EndEdit();
foreach (DataGridViewRow row in DataGridView.Rows)
row.Cells[Index].Value = checkedState;
}
}
}
MyDGV.csusing System.Windows.Forms;
namespace DGVCheckBoxAll
{
public class MyDGV : DataGridView
{
public MyDGV()
{
AllowUserToAddRows = false;
AutoGenerateColumns = false;
CellContentClick += MyDGV_CellContentClick;
}
private void MyDGV_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if ((Columns[e.ColumnIndex] is MyDGVCheckBoxColumn col) == false)
return;
col.SetCheckedState();
}
}
}
測試 Code,在 MyDGV 上顯示自訂的 MyDGVCheckBoxColumn using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
namespace DGVCheckBoxAll
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private bool _checkBoxAll1 = false;
private bool _checkBoxAll2 = true;
private void Form1_Load(object sender, EventArgs e)
{
// DataGridView
MyDGV dgv = new MyDGV();
dgv.Size = new Size(362, 176);
Controls.Add(dgv);
dgv.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = "ID",
Name = "ColID",
HeaderText = "編號",
Width = 100,
Visible = true
});
// 自訂欄位設定
MyDGVCheckBoxColumn Col1 = new MyDGVCheckBoxColumn(_checkBoxAll1)
{
DataPropertyName = "",
Name = "ColChecked1",
HeaderText = "",
Width = 100,
Visible = true
};
MyDGVCheckBoxColumn Col2 = new MyDGVCheckBoxColumn(_checkBoxAll2)
{
DataPropertyName = "",
Name = "ColChecked2",
HeaderText = "",
Width = 100,
Visible = true
};
// 建立自訂欄位,並指定欄位位置
dgv.Columns.Insert(0, Col1);
dgv.Columns.Insert(2, Col2);
// 資料來源
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Rows.Add(1);
dt.Rows.Add(2);
dt.Rows.Add(3);
dt.Rows.Add(4);
dt.Rows.Add(5);
dgv.DataSource = dt;
Col1.CheckBoxAll += Col1_CheckBoxAll;
// 設定初始值
Col2.CheckBoxAllSetColumnCheckedState(_checkBoxAll2);
}
private void Col1_CheckBoxAll(object sender, CheckBoxAllEventArgs e)
{
// DO Something
}
}
}
執行結果MyDGVCheckBoxColumn.CheckBoxAllSetColumnCheckedState() 沒有 EndEdit() 的話,點選 CheckBoxAll 會變成下圖,Focus 所在 cell 在 UI 上不會顯示
- 參考資料
- CheckBoxRenderer.GetGlyphSize 方法
- CheckBoxRenderer.DrawCheckBox 方法 (Graphics, Point, CheckBoxState)
- CheckBoxState 列舉類型
- DataGridView.RefreshEdit 方法 ()
- DataGridView.EndEdit 方法 ()
- DataGridViewCell.EditedFormattedValue 屬性
- [C#][WinForm] GridView 中 CheckBox 全選
- [DataGridView] CheckBox Select All of the Datagridview
- CheckBox Header Column For DataGridView
- StackOverFlow 討論
- 技術論壇討論
沒有留言:
張貼留言