【问题标题】:Breaking event cycles in GUIs打破 GUI 中的事件循环
【发布时间】:2008-09-24 00:25:42
【问题描述】:

在编写 GUI 时,我经常遇到以下问题:假设您有一个模型和一个控制器。控制器有一个小部件W,用于显示模型的属性X

因为模型可能会从控制器外部更改(可能有其他控制器使用相同的模型、撤消操作等),因此控制器会监听模型的更改。控制器还会监听小部件W 上的事件并相应地更新属性X

现在,发生了以下情况:

  1. W 中的值已更改
  2. 产生一个事件,调用控制器中的处理程序
  3. 控制器模型中为X设置新值
  4. 模型发出事件,因为它已被更改
  5. 控制器收到来自模型的更改事件
  6. 控制器获取X的值并将其设置在小部件中
  7. 转到 1。

有几种可能的解决方案:

  1. 修改控制器以在模型更新时设置一个标志,如果设置了此标志,则不对来自模型的任何事件作出反应。
  2. 暂时断开控制器(或告诉模型一段时间不要发送任何事件)
  3. 冻结小部件的所有更新

过去,我通常选择选项 1,因为这是最简单的事情。它的缺点是会使你的类被标志弄得杂乱无章,但其他方法也有它们的缺点。

为了记录,我在几个 GUI 工具包中遇到过这个问题,包括 GTK+、Qt 和 SWT,所以我认为它与工具包无关。

有什么最佳实践吗?还是我使用的架构完全错误?

@Shy:这在某些情况下是一种解决方案,但是如果从控制器外部更改X(例如,使用撤消/重做的命令模式时),您仍然会得到一轮多余的事件,因为那时值已更改,W 已更新并触发事件。为了防止对模型进行另一次(无用的)更新,必须吞下小部件生成的事件。
在其他情况下,模型可能更复杂,简单检查究竟发生了什么变化可能不可行,例如一个复杂的树视图。

【问题讨论】:

  • 我在 MFC 中的列表框一直有这个问题。

标签: user-interface events


【解决方案1】:

处理此问题的标准 QT 方法以及在他们非常有用的教程中建议的方法是,仅当新值与当前值不同时才更改控制器中的值。
这是信号具有valueChanged()语义的方式

see this tutorial

【讨论】:

    【解决方案2】:

    通常您应该响应小部件中的输入事件而不是更改事件。这样可以防止发生这种类型的循环。

    1. 用户更改小部件中的输入
    2. 小部件发出更改事件(滚动完成/单击输入/鼠标离开等)
    3. 控制器响应,转换为模型中的变化
    4. 模型发出事件
    5. 控制器响应,改变小部件中的值
    6. 已发出值更改事件,但控制器未侦听

    【讨论】:

    • 感谢您的建议。遗憾的是,这并不总是可行的,具体取决于 GUI 工具包。
    【解决方案3】:

    指示更新工作的标志。您可以将它们包装在 BeginUpdate 和 EndUpdate 等方法中。

    【讨论】:

    • 这是一种常见的做法,还是您只是做出有根据的猜测?
    • 我使用标志来指示 MFC Windows 开发中的更新状态,该开发有一个具有多视图架构的文档。最近在 .Net Windows 开发中,我使用标志来指示选择更改事件是否在初始化期间被触发并且可以被忽略。
    猜你喜欢
    • 2011-05-30
    • 1970-01-01
    • 2019-09-13
    • 1970-01-01
    • 2021-03-11
    • 2018-02-28
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多