【问题标题】:What is the best way to mask an UI Event?屏蔽 UI 事件的最佳方法是什么?
【发布时间】:2013-02-18 14:04:10
【问题描述】:

我正在使用 DataGridView,并使用 CellValueChanged 事件。

当我通过代码更改单元格值时,我不希望触发此事件。但是,我希望在用户编辑它时触发它。

这就是为什么我用以下代码附上我的单元格值更改操作:

void changeCellOperation()
{
    dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged);

    ...
    cell.Value = myNewCellValue
    ...

    dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged);
}

我最终拥有了几个不同的功能,其中我的 DataGridView 单元格以这种方式更新。

因为这些函数是从不同的地方调用的并且可以嵌套,所以我不能保留这段代码以避免事件不需要的事件重新激活。

所以我就这样结束了:

int valueChangedEventMask = 0;

void changeCellOperation()
{
    valueChangedEventMask++;

    ...
    cell.Value = myNewCellValue
    ...

    valueChangedEventMask--;
}

void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (valueChangedEventMask > 0)
        return

    ...
}

这很好用。此外,当调用嵌套时,包括在事件本身内部。

CellValueChanged 事件现在无缘无故触发了太多次

因为我经常不得不处理这种模式,所以我正在寻找一种可以普遍适用于 UI 中的事件的解决方案,而不仅仅是 DataGridView。

所以我的问题是:

正确屏蔽 UI 事件并避免不必要的事件触发的最佳技巧是什么?

【问题讨论】:

标签: c# winforms events user-interface


【解决方案1】:

CellValueChanged 不是 UI 事件,而是属性更改事件。这意味着您不能使用它来区分用户输入和程序更改。您始终可以使用订阅者/取消订阅或标记+/- 或 BeginEdit/EndEdit 类似的技术,但也许您必须找到另一种(更好的)方法。例如,在复选框的情况下,您可以使用Click 事件而不是Changed,因为(惊喜!)它会告诉您用户何时单击它,否则会以编程方式安全地更改Checked 的值。

如果是DataGridView,最简单的方法是使用带有一些标志的Changed(将在编辑开始时设置并在结束时重置 - 请参阅CellBeginEdit/CellEndEdit)。

【讨论】:

    【解决方案2】:

    您可以使用CellEndEdit 代替CellValueChange。我不知道您的方法dgv_CellValueChanged 做了什么,请注意每次退出单元格的编辑模式时都会触发CellEndEdit,即使它的值没有更改。这意味着如果您不希望在值不变时执行该方法,则必须跟踪单元格的当前值。

    我会避免与鼠标相关的事件,例如 CellClick,因为您的用户可以只使用键盘。

    无论如何,我通常通过将逻辑与用户界面分离来避免此类问题,即我编写了一个绑定到表单的单独类。看看MVVM(如果你愿意,你可以在WinForms中实现你自己的版本)或旧的MVC

    【讨论】:

    • 谢谢。这是一个完全“手动”的 DataGridView。没有绑定到它的数据源或数据绑定机制。我会尝试你对 CellEndEdit 的建议。
    • 我的意思是,如果我没有数据绑定机制,我就自己构建它。需要编写更多代码,但我认为值得付出努力,特别是如果您开发自己的框架,可以在许多不同的项目中使用
    • 同意。我的要求是某些单元格需要根据其值的特定颜色,某些单元格从/到只读模式切换,其他单元格是计算值,加上网格本身被转置,因此列是我的数据模型中的一行......逻辑非常复杂,我不得不手动设计和填充我的网格。我不得不承认它比看起来更愉快,并且可以非常干净地实现。我什至发现了一些可重复使用的代码。我不认为 3d 派对组件需要更少的工作。以这种方式重新发现 DataGridView 让我感觉像在 Excel 中编码 :)
    【解决方案3】:

    我最终将这两种解决方案混合成一个非常简单的解决方案。我使用计数器,并且只挂钩/取消挂钩我想要屏蔽的事件。

    EventMask valueChangedEventMask;
    
    // In the class constructor
    valueChangedEventMask = new EventMask(
        () => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); },
        () => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); }
    );
    
    // The value change operation I want to hide from the event
    void changeCellOperation()
    {
        valueChangedEventMask.Push();
    
        ...
        cell.Value = myNewCellValue
        ...
    
        valueChangedEventMask.Pop();
    }
    
    // The class
    public class EventMask
    {
        Action hook;
        Action unHook;
    
        int count = 0;
    
        public EventMask(Action hook, Action unHook)
        {
            this.hook = hook;
            this.unHook = unHook;
        }
    
        public void Push()
        {
            count++;
            if (count == 1)
                unHook();
        }
    
        public void Pop()
        {
            count--;
            if (count == 0)
                hook();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-09-14
      • 1970-01-01
      • 2012-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 2019-12-02
      相关资源
      最近更新 更多