把該筆記 [C#] 停用 Windows Form DataGridView 控制項按鈕資料行中的按鈕 功能應用在實務環境上,發現還有些小地方還可以改善
該筆記重點
- 重新繪製 Button 時間點:之前是在 AP 端設定完成後要手動觸發 InvalidateColumn(),現在把繪製觸發點改在 Property 內,只要設定屬性就會立刻重新繪製,且使用 InvalidateCell() 只重新繪製該 cell,縮小繪製範圍
- 滑鼠外觀強化:當屬性為 false 時,滑鼠指標要變成禁止符號,增強使用者體驗
- Disable 繪製時 TextFormatFlags 設定:根據 DataGridViewContentAlignment 來產生對應 TextFormatFlags
public class DataGridViewDisableButtonCell : DataGridViewButtonCell
{
private bool _cellEnabled = true;
private bool _cellVisible = true;
public bool CellEnabled
{
get => _cellEnabled;
set
{
if (_cellEnabled != value)
{
_cellEnabled = value;
DataGridView?.InvalidateCell(ColumnIndex, RowIndex);
}
}
}
public bool CellVisible
{
get => _cellVisible;
set
{
if (_cellVisible != value)
{
_cellVisible = value;
DataGridView?.InvalidateCell(ColumnIndex, RowIndex);
}
}
}
public DataGridViewDisableButtonCell()
{
_cellEnabled = true;
_cellVisible = true;
}
public override object Clone()
{
var cell = (DataGridViewDisableButtonCell)base.Clone();
cell.CellEnabled = this.CellEnabled;
cell.CellVisible = this.CellVisible;
return cell;
}
// --- 游標指標 ---
protected override void OnMouseEnter(int rowIndex)
{
base.OnMouseEnter(rowIndex);
if ((!_cellEnabled || !_cellVisible) && DataGridView != null)
{
DataGridView.Cursor = Cursors.No;
}
}
protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
{
base.OnMouseMove(e);
if ((!_cellEnabled || !_cellVisible) && DataGridView != null && DataGridView.Cursor != Cursors.No)
{
DataGridView.Cursor = Cursors.No;
}
}
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object? value, object? formattedValue, string? errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
{
// Visible = false 只繪製背景
if (!_cellVisible)
{
if (paintParts.HasFlag(DataGridViewPaintParts.Background))
{
using var backBrush = new SolidBrush(cellStyle.BackColor);
graphics.FillRectangle(backBrush, cellBounds);
}
return;
}
// Enabled = true 使用預設繪製方式,確保樣式一致
if (_cellEnabled)
{
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
return;
}
// --- 自定義 Disabled 繪圖邏輯 ---
// 背景
if (paintParts.HasFlag(DataGridViewPaintParts.Background))
{
using var backBrush = new SolidBrush(cellStyle.BackColor);
graphics.FillRectangle(backBrush, cellBounds);
}
// 邊框
if (paintParts.HasFlag(DataGridViewPaintParts.Border))
{
PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
}
// 計算按鈕區域
Rectangle buttonArea = cellBounds;
Rectangle buttonAdjustment = BorderWidths(advancedBorderStyle);
buttonArea.X += buttonAdjustment.X;
buttonArea.Y += buttonAdjustment.Y;
buttonArea.Height -= buttonAdjustment.Height;
buttonArea.Width -= buttonAdjustment.Width;
// 按鈕本身 (Disabled 樣式)
ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled);
// 文字
if (formattedValue is string text && !string.IsNullOrEmpty(text))
{
TextFormatFlags flags = GetTextFormatFlags(cellStyle.Alignment);
TextRenderer.DrawText(graphics,
text,
cellStyle.Font,
buttonArea,
SystemColors.GrayText,
flags);
}
}
private TextFormatFlags GetTextFormatFlags(DataGridViewContentAlignment alignment)
{
// WordBreak: 如果文字太長,允許自動換行
// PreserveGraphicsClipping: 確保文字不會畫到格子外面去 (效能與安全性)
TextFormatFlags flags = TextFormatFlags.WordBreak | TextFormatFlags.PreserveGraphicsClipping;
// 水平位置
if (alignment.ToString().Contains("Center"))
flags |= TextFormatFlags.HorizontalCenter; // 加上「水平置中」標籤
else if (alignment.ToString().Contains("Right"))
flags |= TextFormatFlags.Right; // 加上「靠右」標籤
else
flags |= TextFormatFlags.Left; // 預設「靠左」
// 垂直位置
if (alignment.ToString().Contains("Top"))
flags |= TextFormatFlags.Top; // 加上「靠上」標籤
else if (alignment.ToString().Contains("Bottom"))
flags |= TextFormatFlags.Bottom; // 加上「靠下」標籤
else
flags |= TextFormatFlags.VerticalCenter; // 預設「垂直置中」
return flags;
}
}
DataGridViewDisableButtonColumn
當希望 ButtonColumn.Text 文字可以全部顯示在按鈕文字上,要開啟 UseColumnTextForButtonValue 屬性,官方文件說明
By default, the DataGridViewCell.FormattedValue of a button cell is displayed as the text on the button. The UseColumnTextForButtonValue property allows you to either set the button text for each cell, or to use the Text property value for all of the button cells.
Getting or setting this property gets or sets the UseColumnTextForButtonValue property of the object returned by the CellTemplate property. Setting this property also sets the UseColumnTextForButtonValue property of every cell in the column and refreshes the column display. To override the specified value for individual cells, set the cell values after you set the column value.
namespace DataGridViewDisableButtonColumnSample
{
public class DataGridViewDisableButtonColumn : DataGridViewButtonColumn
{
public DataGridViewDisableButtonColumn()
{
CellTemplate = new DataGridViewDisableButtonCell();
// 預設值為 false,當希望 DataGridViewColumn.Text 設定值,直接顯示在 Button.Value 上時要開啟
UseColumnTextForButtonValue = false;
}
}
}
主程式
原本是透過 CellClick 事件來顯示,現在改為使用 CellContentClick 事件,縮小事件觸發範圍
namespace DataGridViewDisableButtonColumnSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView1.AutoSize = true;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
dataGridView1.RowCount = 8;
for (int i = 0; i < dataGridView1.RowCount; i++)
dataGridView1.Rows[i].Cells[ColButtonEnabled.Index].Value = $"Button{i}";
dataGridView1.CellContentClick += DataGridView1_CellContentClick;
dataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
dataGridView1.CurrentCellDirtyStateChanged += DataGridView1_CurrentCellDirtyStateChanged;
}
private void DataGridView1_CellContentClick(object? sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex != ColButtonEnabled.Index)
return;
DataGridViewDisableButtonCell buttonEnabled = (DataGridViewDisableButtonCell)dataGridView1.Rows[e.RowIndex].Cells[ColButtonEnabled.Index];
if (buttonEnabled.CellEnabled == false)
return;
MessageBox.Show($"{buttonEnabled.Value} 是 Enabled");
}
private void DataGridView1_CurrentCellDirtyStateChanged(object? sender, EventArgs e)
{
if (dataGridView1.IsCurrentCellDirty &&
dataGridView1.CurrentCell.ColumnIndex == ColChecked.Index)
// CommitEdit() 會維持在編輯模式,並立即觸發 CellValueChanged 事件
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
private void DataGridView1_CellValueChanged(object? sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex != ColChecked.Index)
return;
bool value = (bool)dataGridView1.Rows[e.RowIndex].Cells[ColChecked.Index].Value;
DataGridViewDisableButtonCell buttonEnabled = (DataGridViewDisableButtonCell)dataGridView1.Rows[e.RowIndex].Cells[ColButtonEnabled.Index];
buttonEnabled.CellEnabled = !value;
DataGridViewDisableButtonCell buttonVisible = (DataGridViewDisableButtonCell)dataGridView1.Rows[e.RowIndex].Cells[ColButtonVisible.Index];
buttonVisible.CellVisible = !value;
}
}
}
沒有留言:
張貼留言