星期四, 8月 01, 2024

[C#] 停用 Windows Form DataGridView 控制項按鈕資料行中的按鈕

根據官方文章 - 停用 Windows Form DataGridView 控制項按鈕資料行中的按鈕 的練習筆記,文章內是把自訂 DataGridViewButtonCell 有 Disable 功能,則是再把 Visible 功能加進去

DataGridViewDisableButtonCell

要讓 DataGridViewButtonColumn 有無法使用或是隱藏效果,其實是透過 Paint 繪製 DataGridViewButtonColumn 外觀去變化,DataGridViewButtonCell 內會自訂 CellEnabled 和 CellVisible 來開放控制,Paint 事件內根據這兩個 Property 去判斷如何繪製外觀,另外因為是從繼承 DataGridViewButtonCell,所以要覆寫 Clone 並把 CellEnabled 和 CellVisible 塞進去,文件上也有特別說明該點
When you derive from DataGridViewCell or DataGridViewColumn and add new properties to the derived class, be sure to override the Clone method to copy the new properties during cloning operations. You should also call the base class's Clone method so that the properties of the base class are copied to the new cell or column.
using System.Windows.Forms.VisualStyles;

namespace DataGridViewDisableButtonColumnSample
{
    public class DataGridViewDisableButtonCell : DataGridViewButtonCell
    {
        public bool CellEnabled { get; set; } = true;

        public bool CellVisible { get; set; } = true;

        public DataGridViewDisableButtonCell()
        {
            // 預設值
            CellEnabled = true;
            CellVisible = true;
        }

        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)
        {
            // 計算 Cell 內繪製 Button 區域
            Rectangle buttonArea = cellBounds;
            Rectangle buttonAdjustment = BorderWidths(advancedBorderStyle);
            buttonArea.X += buttonAdjustment.X;
            buttonArea.Y += buttonAdjustment.Y;
            buttonArea.Height -= buttonAdjustment.Height;
            buttonArea.Width -= buttonAdjustment.Width;

            using SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor);

            if (CellVisible == false)
            {
                // 把整個背景 Cell 都繪製為背景色,讓使用者以為沒有 Button 控件
                graphics.FillRectangle(cellBackground, buttonArea);
            }
            else if (CellEnabled == false)
            {
                if (paintParts.HasFlag(DataGridViewPaintParts.Background))
                    graphics.FillRectangle(cellBackground, cellBounds);

                if (paintParts.HasFlag(DataGridViewPaintParts.Border))
                    PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);

                ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled);

                if (FormattedValue is string)
                {
                    TextRenderer.DrawText(graphics,
                        (string)FormattedValue,
                        DataGridView.Font,
                        buttonArea,
                        SystemColors.GrayText);
                }
            }
            else
            {
                base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
            }
        }

        public override object Clone()
        {
            DataGridViewDisableButtonCell cell = (DataGridViewDisableButtonCell)base.Clone();
            cell.CellEnabled = CellEnabled;
            cell.CellVisible = CellVisible;
            return cell;
        }
    }
}

DataGridViewDisableButtonColumn

很單純把 CellTemplate 指定使用 DataGridViewDisableButtonCell 而已       
namespace DataGridViewDisableButtonColumnSample
{
    public class DataGridViewDisableButtonColumn : DataGridViewButtonColumn
    {
        public DataGridViewDisableButtonColumn()
        {
            CellTemplate = new DataGridViewDisableButtonCell();
        }
    }
}  

主程式

觸發 CellValueChanged 事件,要特別指定重新繪製 DataGridViewButtonColumn 外觀,可以使用
  • Invalidate()
  • InvalidateColumn()
  • Refresh()
以上都可以達到重新繪製 DataGridViewButtonColumn 目的,該筆記是從最小單位繪製去思考,所以使用 InvalidateColumn()
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.CellValueChanged += DataGridView1_CellValueChanged;
            dataGridView1.CurrentCellDirtyStateChanged += DataGridView1_CurrentCellDirtyStateChanged;
            dataGridView1.CellClick += DataGridView1_CellClick;
        }

        private void DataGridView1_CellClick(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;

            dataGridView1.InvalidateColumn(ColButtonEnabled.Index);
            dataGridView1.InvalidateColumn(ColButtonVisible.Index);
        }
    }
}

沒有留言:

張貼留言