Windows Forms DataGrid 預設資料格編輯控制項功能有限。透過繼承 DataGridColumnStyle 並覆寫關鍵方法,可在資料格中嵌入任意控制項(如下拉清單、日期選擇器、自訂文字方塊),大幅擴展表格編輯體驗。本文以自訂 XP_TextBox 與 DataGridTextBoxColumnStyle 為例,示範完整實作流程。
繼承 TextBox,加入禁用貼上、禁用右鍵選單等功能,並覆寫 WndProc 攔截訊息:
public class XP_TextBox : TextBox {
private bool _enablePaste = true;
private bool _enableContextMenu = true;
public bool EnablePaste {
get { return _enablePaste; }
set { _enablePaste = value; Invalidate(); }
}
public bool EnableContextMenu {
get { return _enableContextMenu; }
set { _enableContextMenu = value; Invalidate(); }
}
protected override void WndProc(ref Message m) {
const int WM_PASTE = 0x302;
const int WM_CONTEXTMENU = 0x7B;
switch (m.Msg) {
case WM_PASTE:
OnPasteEvent();
if (!_enablePaste) return;
break;
case WM_CONTEXTMENU:
if (!_enableContextMenu) return;
break;
}
base.WndProc(ref m);
}
public event EventHandler PasteEvent;
protected virtual void OnPasteEvent() {
PasteEvent?.Invoke(this, EventArgs.Empty);
}
}
繼承 DataGridColumnStyle,核心覆寫方法:
| 方法 | 用途 |
|---|---|
Edit | 進入編輯模式,顯示編輯控制項並設定初始值 |
Paint | 繪製非編輯狀態的資料格外觀 |
Commit | 認可編輯值回資料來源 |
GetPreferredSize / GetPreferredHeight / GetMinimumHeight | 計算資料格尺寸 |
Abort | 取消編輯,隱藏控制項 |
public class DataGridTextBoxColumnStyle : DataGridColumnStyle {
private readonly XP_TextBox _textBox = new XP_TextBox();
private bool _isEditing;
private int _editingRow = -1;
private string _oldValue;
public DataGridTextBoxColumnStyle() {
_textBox.Visible = false;
_textBox.BorderStyle = BorderStyle.None;
_textBox.KeyPress += OnKeyPress;
_textBox.Leave += OnLeave;
}
protected override void Edit(CurrencyManager source, int rowNum, Rectangle bounds, bool readOnly, string displayText, bool cellIsVisible) {
_editingRow = rowNum;
_oldValue = displayText;
_textBox.Text = displayText;
_textBox.Bounds = new Rectangle(bounds.X + 2, bounds.Y + 2, bounds.Width - 4, bounds.Height - 4);
_textBox.Visible = true;
_textBox.Focus();
_isEditing = true;
}
protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum) {
Paint(g, bounds, source, rowNum, false);
}
protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, bool alignToRight) {
string text = GetColumnValueAtRow(source, rowNum)?.ToString() ?? "";
Brush backBrush = new SolidBrush(DataGridTableStyle.BackColor);
Brush foreBrush = new SolidBrush(DataGridTableStyle.ForeColor);
g.FillRectangle(backBrush, bounds);
g.DrawString(text, DataGridTableStyle.DataGrid.Font, foreBrush, bounds.X + 2, bounds.Y + 2);
}
protected override bool Commit(CurrencyManager dataSource, int rowNum) {
if (!_isEditing) return false;
string newValue = _textBox.Text;
if (newValue != _oldValue) {
SetColumnValueAtRow(dataSource, rowNum, newValue);
_oldValue = newValue;
}
_isEditing = false;
_textBox.Visible = false;
return true;
}
protected override void Abort(int rowNum) {
_isEditing = false;
_textBox.Visible = false;
}
protected override Size GetPreferredSize(Graphics g, object value) {
return new Size(50, _textBox.PreferredHeight + 2);
}
// GetPreferredHeight, GetMinimumHeight 類似
}
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = "Customers";
var colStyle = new DataGridTextBoxColumnStyle();
colStyle.MappingName = "CompanyName";
colStyle.HeaderText = "公司名稱";
colStyle.Width = 200;
tableStyle.GridColumnStyles.Add(colStyle);
dataGrid.TableStyles.Add(tableStyle);
DataGridComboBoxColumnStyle 內嵌 ComboBox 實現下拉選擇。DataGridDateTimeColumnStyle 內嵌 DateTimePicker。DataGridCheckBoxColumnStyle 內嵌 CheckBox。IDataGridEditingControl 介面讓自訂控制項支援更豐富的編輯協定(.NET 2.0+)。重寫 DataGridColumnStyle 是自訂 DataGrid 資料格編輯體驗的核心技術。掌握 Edit、Paint、Commit、Abort 四大生命週期方法,即可將任意 Windows Forms 控制項無縫整合進資料格,滿足多樣化的業務編輯需求。