【问题标题】:WxWidget ID issueWxWidget ID 问题
【发布时间】:2013-05-27 12:44:22
【问题描述】:

简而言之:如何确保“静态”声明的事件和/或小部件的 ID 在大中型项目中不会发生冲突?

...还有很长的:

在 WxWidgets 中处理 ID 的一种方法是通过每个编译单元的枚举来声明它们,即在小部件的源文件中。但是,当 ID 用于 WxWidgets 的事件处理时,它们应该或必须是全局唯一的。有些不需要是全局唯一的,但我认为如果它们都是唯一的,它会更安全,否则可能会出现运行时 - “冲突”,不会触发任何警告 - 事件只是由另一个小部件处理。不幸的是,我遇到过几次这个问题(继承的代码库)——它们很难追踪而且很烦人,除非我遗漏了一些东西。无论如何,我想一劳永逸地解决这个问题:-)

我知道,WxWidgets 也支持动态绑定,我们/我在某些地方使用它。但是,更改所有“静态”声明的 ID 的代码将是一个很大的工作量。所以基本上我拥有的是许多单独的源文件(> 60),其中有从 wxID_HIGHEST 开始的枚举,带有一些(看似)任意偏移量。我确实认为偏移量是任意的,可能是为了解决上述 ID 冲突问题的单个情况而添加的。无论如何,将所有枚举放在一个文件/枚举中似乎有点难看,因为这将是一个非常长的列表。

这就是我一直在想的:我使用单个枚举在单个头文件中维护不同小部件的 MIN/MAX-ID,并提供两个宏来开始和结束给定的 ID 枚举。 END 宏还应检查声明的 MAX-ID 是否未超过。这听起来合理吗?解决 ID 冲突问题的常用方法有哪些?

我的预期解决方案如下所示。我计划添加一个处理动态绑定 ID 的函数。我知道 WxWidgets 提供了与 ID 引用计数相关的功能,但是,当我使用它时,我想...它旨在尽可能独立于平台。

#include <wx/defs.h>
#define NUM_OF_DYNAMIC_AUTO_IDS 500

namespace WxWidgetIdHelper {

  enum WxWidgetIDsEnum {
    MIN_DYNAMIC_AUTO_ID = wxID_HIGHEST + 1,
    MAX_DYNAMIC_AUTO_ID = MIN_DYNAMIC_AUTO_ID + NUM_OF_DYNAMIC_AUTO_IDS,
    STATIC_ID_OFFSET = MAX_DYNAMIC_AUTO_ID + 1,

    Widget1_MIN_ID = STATIC_ID_OFFSET + 1,
    Widget1_MAX_ID = Widget1_MIN_ID + 20,

    Widget2_MIN_ID = Widget1_MAX_ID + 1,
    Widget2_MAX_ID = Widget2_MIN_ID + 20,
    ...
  };

  template<bool> struct MaxIdCheck;
  template<> struct MaxIdCheck<false> {};
}

#define BEGIN_WXWIDGET_ID_ENUM(CLASSNAME) \
  enum CLASSNAME##_##WXWIDGET_IDs { \
    CLASSNAME##_##MIN_ID = WxWidgetIdHelper##::##CLASSNAME##_##MIN_ID,

#define END_WXWIDGET_ID_ENUM(CLASSNAME) CLASSNAME##_##MAX_ID }; \
  WxWidgetIdHelper::MaxIdCheck< \
  ( CLASSNAME##_##MAX_ID>WxWidgetIdHelper::CLASSNAME##_##MAX_ID ) > \
  INCREASE##_##CLASSNAME##_##MAX_ID_in_WxWidgetIdHelper_h;

【问题讨论】:

    标签: c++ macros wxwidgets


    【解决方案1】:

    没有通用的答案,但我认为您应该牢记以下几点:

    • ID 不需要是全局唯一的,它们只需要在每个顶级窗口内都是唯一的。在典型的程序中,每个对话框都由其自己的类表示,因此其中的所有 ID 都仅受该类控制,您完全不必关心程序的其余部分做什么。
    • 您可以,而且恕我直言,应该完全避免对所有控件使用 ID。 ID 太方便保留它们的唯一地方是菜单项,因为没有与它们关联的对象(您不能绑定到wxMenuItem)。但是对于所有控件,最好直接调用control-&gt;Bind(),在这种情况下,您根本不需要指定 ID,因为此控件只会为自己获取事件。虽然用Bind() 的调用替换事件表宏非常简单,但您甚至不必为现有的事件表执行此操作,因为您可能没有任何 ID 冲突,所以您只需要要做的是将Bind() 用于新的事件处理程序。
    • 如果您使用 XRC - 恕我直言,没有理由不使用 - 那么您的所有 ID 都是在 XRC 文件中指定的字符串,并且保持字符串不同当然比保持整数 ID 不同要简单得多,所以这个问题根本不会出现。值得注意的是,在 XRC 中定义菜单会处理一种使用 ID 非常方便的情况。

    TL;DR除了菜单项之外,根本不要使用 ID,如果必须使用它们,请在 XRC 中定义它们。

    【讨论】:

    • 非常感谢。虽然我需要一种相当快速和优雅的方式来解决我正在处理的(继承的)混乱。一切都“生活”在一个顶级窗口下,因此,全球唯一的 ID 会更安全,恕我直言。这一切都挂在一起的方式有点次优,部分考虑不周,严重“成长”,维护得不是特别好 - 我现在没有时间进行适当的清理和重构。 IDs-To-Bind() 很简单:如果 ID 用于 OnCommandEvent() 中的 switch 语句或类似方法以及用于查找控件,则不那么简单,或者我错过了什么(?)。
    • 如果您需要在一个方法中处理来自多个控件的事件,那么您可以使用wxEvent::GetEventObject() 来区分它们。这基本上将(必须唯一的)控制指针作为 ID 重用。
    • 我在寻找不使用 ID 绑定到菜单项的方法时找到了这个答案(我也不喜欢它们)。对我来说最合乎逻辑的解决方案是能够(1)从wxCommantEvent对象获得指向事件处理程序内菜单项的指针,或者(2)直接绑定到wxMenuItem。我想知道为什么两者似乎都不可能,至少在 wxWidgets 3.1 中是不可能的。还是我错了?
    • 主要原因是我们没有Windows下产生事件的item的信息,只有它的ID。当然,我们可以通过 ID 来查找项目,但这似乎没有多大意义,而且像这样我们甚至根本不需要保留 wxMenuItem 对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-02
    • 1970-01-01
    • 2022-01-10
    • 2013-10-01
    • 1970-01-01
    • 2015-06-01
    • 2019-10-19
    相关资源
    最近更新 更多