WinForms,无法从自定义类订阅事件

问题描述 投票:0回答:1

我有一个自定义类

CheckboxHeaderCell
继承自
DataGridViewColumnHeaderCell
,可以将复选框添加到任何数据网格视图中。复选框添加成功,但在复选框选中事件上仅自定义类事件触发,但我添加此复选框的表单(方法
HeaderCell_CheckedChanged
)中的事件永远不会触发。

类:CheckboxHeaderCell

using System;
using System.Drawing;
using System.Windows.Forms;

namespace test.CustomControls
{
    public class CheckboxHeaderCell : DataGridViewColumnHeaderCell
    {
        public event EventHandler CheckedChanged;

        public CheckBox checkBox;
        public bool IsChecked = false;

        public CheckboxHeaderCell()
        {
            checkBox = new CheckBox();
            checkBox.BackColor = SystemColors.Control;
            checkBox.Margin = new Padding(0);
            checkBox.Padding = new Padding(0);
            checkBox.Size = new Size(14, 14);
            checkBox.UseVisualStyleBackColor = true;
            checkBox.CheckedChanged += CheckBox_CheckedChanged;
            CheckedChanged = new EventHandler(doNothing);
        }

        public void CheckBox_CheckedChanged(object sender, EventArgs e)
        {
            IsChecked = checkBox.Checked;
            CheckedChanged?.Invoke(this, EventArgs.Empty);
            if (DataGridView != null)
            {
                DataGridView.InvalidateCell(this);
            }
        }

        public void doNothing(object sender, EventArgs e)
        {

        }
        //other methods, paint etc
    }
}

我使用此类通过以下方法将自定义复选框添加到我的 dgv 中:

private void AddCheckBoxDGV(DataGridView dgv, string columnName)
    {
        if (dgv.Columns.Contains(columnName))
        {
            CustomControls.CheckboxHeaderCell checkboxHeaderCell = new CustomControls.CheckboxHeaderCell();
            checkboxHeaderCell.CheckedChanged += HeaderCell_CheckedChanged;

            dgv.Columns[columnName].HeaderCell = checkboxHeaderCell;
        }
    }
private void HeaderCell_CheckedChanged(object sender, EventArgs e)
    {
       //do smthing
    }
c# .net winforms events event-handling
1个回答
0
投票

您的自定义

HeaderCell
不会执行任何操作,只会创建一个您永远不会作为 UI 元素看到或访问的
CheckBox
。既不在
HeaderCell
上,也不在网格上的其他任何地方。所以,我不明白如何:

复选框添加成功...

您必须将 Control 添加到某个

Parent.Controls
集合,并且
Parent
必须直接(本身)或间接(其父级)添加到顶级控件。例如,
Form
HeaderCell
不是这样使用的控件。

为了证明这个概念,将

CheckBox
添加到
yourGrid.Controls
,您将在左上角看到它,勾选它,您将引发
CheckedChanged
事件。


我知道您需要一个自定义的

DataGridViewCheckBoxColumn
,并在
HeaderCell
上有一个主复选框来选中/取消选中所有内容。这是一个例子。

标题单元格

在第一部分中,从

DataGridViewColumnHeaderCell

派生一个新类
  • 添加一个属性来获取/设置主 CheckBox 的
    CheckState
    。如果检查了所有单元格,则为
    Checked
    ;如果未检查所有单元格,则为
    Unchecked
    ;如果未检查所有单元格,则为
    Indeterminate
  • 重写
    OnMouseClick
    方法来切换状态并设置属性,
  • setter 调用一个方法将所属列单元格的
    Value
    属性设置为
    true
    false
public class CheckAllColumnHeaderCell : DataGridViewColumnHeaderCell
{
    enum MouseState : int
    {
        Normal,
        Hot,
        Down
    }
        
    CheckState _checkState = CheckState.Unchecked;
    MouseState _mouseState = MouseState.Normal; // See the mouse methods...
    Rectangle _checkBoxRect; // See the Paint method...

    public CheckState CheckState
    {
        get => _checkState;
        set
        {
            if (_checkState != value)
            {
                if (DataGridView.RowCount == 0 ||
                    DataGridView.Rows[0].IsNewRow) return;

                _checkState = value;
                DataGridView?.InvalidateCell(this);
                SetCellsCheckState();
            }
        }
    }

    private void SetCellsCheckState()
    {
        if (CheckState != CheckState.Indeterminate)
        {
            bool state = CheckState == CheckState.Checked;
            foreach (var row in DataGridView.Rows.Cast<DataGridViewRow>())
            {
                if (!row.IsNewRow) row.Cells[ColumnIndex].Value = state;
            }
        }
    }

    protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
    {
        base.OnMouseClick(e);

        if (e.Button == MouseButtons.Left && 
            DataGridView.RowCount > 0 && !DataGridView.Rows[0].IsNewRow &&
            _checkBoxRect.Contains(DataGridView.PointToClient(Cursor.Position)))
        {
            CheckState = CheckState == CheckState.Checked || 
                CheckState == CheckState.Indeterminate
                ? CheckState.Unchecked
                : CheckState.Checked;                
        }
    }

第二部分是当

DataGridView
的一些相关事件发生时更新主CheckBox的状态。例如,当用户勾选/取消勾选所属列的单元格时、添加或删除行时...

    protected override void OnDataGridViewChanged()
    {
        base.OnDataGridViewChanged();

        if (DataGridView != null)
        {
            DataGridView.CurrentCellDirtyStateChanged -= OnCurrentCellDirtyStateChanged;
            DataGridView.CellValueChanged -= OnDataGridViewOnCellValueChanged;
            DataGridView.RowsAdded -= OnDataGridViewRowsAdded;
            DataGridView.RowsRemoved -= OnDataGridViewRowsRemoved;

            DataGridView.CurrentCellDirtyStateChanged += OnCurrentCellDirtyStateChanged;
            DataGridView.CellValueChanged += OnDataGridViewOnCellValueChanged;
            DataGridView.RowsAdded += OnDataGridViewRowsAdded;
            DataGridView.RowsRemoved += OnDataGridViewRowsRemoved;
        }
    }

    private void OnDataGridViewOnCellValueChanged(object sender,
        DataGridViewCellEventArgs e)
    {
        if (e.RowIndex >= 0 && e.ColumnIndex == ColumnIndex)
        {
            if (DataGridView.NewRowIndex == e.RowIndex)
                DataGridView.NotifyCurrentCellDirty(true);
            else
                UpdateCheckState();
        }
    }

    private void OnDataGridViewRowsAdded(object sender,
        DataGridViewRowsAddedEventArgs e) => UpdateCheckState();

    private void OnDataGridViewRowsRemoved(object sender,
        DataGridViewRowsRemovedEventArgs e) => UpdateCheckState();

    private void OnCurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        if (DataGridView.CurrentCell?.ColumnIndex == ColumnIndex)
            UpdateCheckState();
    }

    private void UpdateCheckState()
    {
        DataGridView.EndEdit();
        int total = DataGridView.RowCount;
        if (DataGridView.AllowUserToAddRows) total--;
        int chkCount = DataGridView.Rows.Cast<DataGridViewRow>()
            .Aggregate(0, (count, row) =>
            {
                count += (bool)row.Cells[ColumnIndex].EditedFormattedValue ? 1 : 0;
                return count;
            });

        _checkState = chkCount == 0
            ? CheckState.Unchecked
            : chkCount == total
            ? CheckState.Checked
            : CheckState.Indeterminate;

            DataGridView.InvalidateCell(this);
    }

最后一部分,绘制主复选框。重写鼠标事件以更新 mouse state,该状态在

Paint
方法中使用,以确定要绘制哪个
CheckBoxState
。在本例中,我将使用
CheckBoxRenderer
来绘制
CheckBox
。您可以尝试使用
ControlPaint.DrawCheckBox
或按您想要的方式绘制。

    protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
    {
        base.OnMouseMove(e);

        if (Control.MouseButtons != MouseButtons.None) return;

        if (_checkBoxRect.Contains(DataGridView.PointToClient(Cursor.Position)))
        {
            if (_mouseState != MouseState.Hot)
            {
                _mouseState = MouseState.Hot;
                DataGridView.InvalidateCell(this);
            }
        }
        else if (_mouseState != MouseState.Normal)
        {
            _mouseState = MouseState.Normal;
            DataGridView.InvalidateCell(this);
        }
    }

    protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
    {
        base.OnMouseDown(e);

        if (e.Button == MouseButtons.Left &&
            _checkBoxRect.Contains(DataGridView.PointToClient(Cursor.Position)))
        {
            _mouseState = MouseState.Down;
            DataGridView.InvalidateCell(this);
        }
    }

    protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
    {
        base.OnMouseUp(e);

        if (e.Button == MouseButtons.Left && _checkBoxRect
            .Contains(DataGridView.PointToClient(Cursor.Position)))
        {
            _mouseState = MouseState.Normal;
            DataGridView.InvalidateCell(this);
        }
    }

    protected override void OnMouseLeave(int rowIndex)
    {
        base.OnMouseLeave(rowIndex);

        if (_mouseState != MouseState.Normal)
        {
            _mouseState = MouseState.Normal;
            DataGridView.InvalidateCell(this);
        }
    }

    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)
    {
        paintParts &= ~DataGridViewPaintParts.ContentForeground;

        base.Paint(graphics,
            clipBounds,
            cellBounds,
            rowIndex,
            dataGridViewElementState,
            value,
            formattedValue,
            errorText,
            cellStyle,
            advancedBorderStyle,
            paintParts);

        bool enabled = DataGridView.Enabled;
        CheckBoxState state = CheckBoxState.UncheckedNormal;

        switch (CheckState)
        {
            case CheckState.Checked:
                state = !enabled ? CheckBoxState.CheckedDisabled
                    : _mouseState == MouseState.Down
                    ? CheckBoxState.CheckedPressed
                    : _mouseState == MouseState.Hot
                    ? CheckBoxState.CheckedHot
                    : CheckBoxState.CheckedNormal;
                break;
            case CheckState.Indeterminate:
                state = !enabled ? CheckBoxState.MixedDisabled
                    : _mouseState == MouseState.Down
                    ? CheckBoxState.MixedPressed
                    : _mouseState == MouseState.Hot
                    ? CheckBoxState.MixedHot
                    : CheckBoxState.MixedNormal;
                break;
            case CheckState.Unchecked:
                state = !enabled ? CheckBoxState.UncheckedDisabled
                    : _mouseState == MouseState.Down
                    ? CheckBoxState.UncheckedPressed
                    : _mouseState == MouseState.Hot
                    ? CheckBoxState.UncheckedHot
                    : CheckBoxState.UncheckedNormal;
                break;
        }

        Size sz = CheckBoxRenderer.GetGlyphSize(graphics, state);
        Point pt = new Point(
                cellBounds.X + (cellBounds.Width - sz.Width) / 2,
                cellBounds.Y + (cellBounds.Height - sz.Height) / 2);
        _checkBoxRect = new Rectangle(pt, sz);

        CheckBoxRenderer.DrawCheckBox(graphics, pt, state);
    }
}

一些想法,请参阅:

色谱柱和单元格

DataGridViewCheckBoxCell
派生一个新类。除非你想重写
Paint
方法来自己绘制 CheckBox。您不需要在这里做任何事情。主要用于设置列类的
CellTemplate

public class CheckAlCheckBoxlCell : DataGridViewCheckBoxCell
{
    public CheckAlCheckBoxlCell() { }
}

...并从

DataGridViewCheckBoxColumn
导出另一个。

public class CheckAllCheckBoxColumn : DataGridViewCheckBoxColumn
{
    private readonly CheckAllColumnHeaderCell _headerCell;

    public CheckAllCheckBoxColumn()
    {
        CellTemplate = new CheckAlCheckBoxlCell();
        _headerCell = new CheckAllColumnHeaderCell();
        HeaderCell = _headerCell;
    }

    public override DataGridViewCell CellTemplate
    {
        get => base.CellTemplate;
        set
        {
            if (value != null &&
                !value.GetType().IsAssignableFrom(typeof(CheckAlCheckBoxlCell)))
                throw new InvalidCastException("Must be a CheckAlCheckBoxlCell.");

            base.CellTemplate = value;
        }
    }

    [Browsable(false)]
    public CheckState CheckState
    {
        get => _headerCell.CheckState;
        set
        {
            switch (value)
            {
                case CheckState.Checked:
                case CheckState.Indeterminate:
                    _headerCell.CheckState = CheckState.Checked;
                    break;
                default:
                    _headerCell.CheckState = CheckState.Unchecked;
                    break;
            }
        }
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.