基于策略设计的通用 CUpdateUI






4.27/5 (5投票s)
2002年1月5日
4分钟阅读

75280

448
一种基于策略的 UI 更新实现,用于累积和执行 UI 更改。
引言
类型列表是 C++ 中最令人惊叹的事物之一,非常奇特!这就是为什么我更喜欢 C++ 而不是任何其他语言。你熟悉类型列表吗?如果不熟悉,那么你必须点击这里 [1] 阅读。Andrei 会更好地向你解释,你绝不会后悔。我已彻底改造了使用类型列表的旧版本,并对其简化了许多事情感到兴奋。
WTL 中有一个名为 CUpdateUI
的类模板,用于管理 UI 元素的更新,我很喜欢它。在大多数情况下,这就是我们需要的。让我思考另一种实现的因素是 CUIUpdate
可以扩展以获得更多功能的方面。你可以选择继承 CUpdateUI
或 CUpdateUIBase
。这似乎会很混乱和笨拙。我认为拥有一个基于策略设计的 CUpdateUI
通用版本会更好。这种模型的好处是
- 它可扩展;要添加新功能,我们永远不必完全重写它;
- 它可调整;通过在策略集中放置另一个策略,我们可以获得更多功能;
- 它具有高性能。
我尝试过这样做。
实现
我认为它看起来是这样的
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUIStates<StatePolicy1, StatePolicy2, ...>, public CUIUpdate<CMainFrame, UIPolicy1, UIPolicy2, ...> { ... };
CUIStates
是一个类模板,用于累积 CUIUpdate
需要应用的 UI 更改。每个 StatePolicyNs
支持特定类型的更改。 CUIUpdate
显示 CUIStates
中所做的更改。它拥有一组 UIPolicyNs
;每个都支持特定类型的用户界面。 CUIStates
和 CUIUpdate
都可以拥有任意数量的策略。你可以指定多达 32 个策略。如果你查看 CUIStates
的定义,你会看到类似这样的内容
struct nil {} template < typename S01 = nil, typename S02 = nil, typename S03 = nil, typename S04 = nil, typename S05 = nil, ... typename S31 = nil, typename S32 = nil > class CUIStates : ...
最后的省略号 (...) 是什么?它是一种派生 CUIStates 的东西,其参数 SXX 非空。这就是类型列表工作的地方,它是一种魔法。这可能是另一篇文章的主题。嗯,编译器有很多工作要做,以便在运行时排除未使用的代码。CUIUpdate 也是以同样的方式完成的。
这是 WTL 向导等效代码,用于更新“Toolbar”和“Status Bar”菜单项
class CMainFrame : public CUIStates<CStyle>, public CUIUpdate<CMainFrame, CUICommand<CMainFrame> >, ... { ... virtual BOOL OnIdle() { UIUpdate(); // CUIUpdate method return FALSE; } BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate) // Does the same as CHAIN_MSG_MAP(CUpdateUI<CMainFrame>) // Updates popup menus and checks accelerators CHAIN_MSG_MAP(CUICommand<CMainFrame>) COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar) COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar) ... END_MSG_MAP() LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { ... // CStyle methods UIAddStyle(ID_VIEW_TOOLBAR, CStyle::Enable|CStyle::Visible|CStyle::Checked); UIAddStyle(ID_VIEW_STATUS_BAR, CStyle::Enable|CStyle::Visible|CStyle::Checked); ... return 0; } LRESULT OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { bool bVisible = !::IsWindowVisible(m_hWndToolBar); ::ShowWindow(m_hWndToolBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE); UISetCheck(ID_VIEW_TOOLBAR, bVisible); // CStyle method UpdateLayout(); return 0; } LRESULT OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { bool bVisible = !::IsWindowVisible(m_hWndStatusBar); ::ShowWindow(m_hWndStatusBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE); UISetCheck(ID_VIEW_STATUS_BAR, bVisible); // CStyle method UpdateLayout(); return 0; } ... };
CStyle
策略支持命令 ID 的标志,如启用/禁用、可见/隐藏、选中/未选中。 CUICommand
支持弹出菜单的更新并处理加速键。因此, CUICommand
要求 CUIState
具有 CStyle
;但如果没有会怎样?没事, CUICommand
只是不做任何事情,我向你保证 CUICommand
没有像这样丑陋的代码
if(Does_CUIState_Have_CStyle()) { // translates accelerators } else { // does nothing }
所有的工作都是由编译器在编译时完成的,没有任何“if”。你认为这是不对的吗?那么你就来错地方了,请关闭你的浏览器。
如果我们想管理项目的文本标签,我们应该添加一个 CText
状态策略,并使用它的方法来控制 UI 更改。
class CMainFrame : public CUIStates<CStyle, CText>, public CUIUpdate<CMainFrame, CUICommand<CMainFrame> >, ...
要管理工具栏按钮,只需重写
class CMainFrame : public CUIStates<CStyle, CText>, public CUIUpdate<CMainFrame, CUICommand<CMainFrame>, CUIToolBar<CMainFrame> >, ...
管理状态栏:CUIStatusBar
,重吧:CUIReBar
。
如果你有自己的控件,你可以实现 CUIUpdate
策略,从而将控件添加到该模型中。此外,我们可以指定自己的状态策略,并扩大现有 CUIToolBar
、CUIStatusBar
的功能。
如何添加新的状态和 UI 策略
示例项目演示了如何实现名为 CParts 的状态策略,该策略管理状态栏部分的数量和宽度,以及如何扩展 CUIStatusBar 以根据 CParts 的变化来更改状态栏。
状态策略应该有三个函数
bool dirty(ui_type id) const void clean(ui_type id) void set_default_value()
dirty 和 clean 支持 dirty 标志;与 WTL 的 CUpdateUI
相同。set_default_value 将状态的值设置为默认值(如果状态有的话);这使得 MDI 界面支持更加容易。这足以与 CUIStates
编译。其他方法由你自行决定,它们将在 UI 元素更改应用代码时使用。
UI 策略的要求是它应该有一个以下模板化函数
template<class State> void update_state(HWND hwnd, const State& state) {}
因此,如果 UI 策略不支持指定类型的状态策略,那么它什么也不做;如果我们希望它支持,例如,CText,那么它应该有如下函数
void update_state(HWND hwnd, CText& state) {
...
}
有关详细信息,您应该查看源代码。
关于效率的几句话。 CUIStates
和 CUIUpdate
都通过积极使用泛型编程技术来实现;它们没有虚函数。因此性能非常高。相比之下,WTL 向导生成的二进制文件的发布大小是相同的。有两种实现方式,在 #include <map> 之前 #include <wtlx/ui_update.hpp>,我们将从 CSimpleMaps
和 CStrings
切换到 STL 库。
我认为使用这些技术将造就 C++ 库的未来。WTL 将会是什么?让我们拭目以待,我只希望它不是 MFC2。为什么会有 CString
这样的东西?是为了让 MFC 程序员感到舒适吗?好吧,好吧,好吧...
致谢
[1] Andrei Alexandrescu. Generic<Programming>: Typelists and Applications.
[2] Andrei Alexandrescu. Generic<Programming>: Mappings between Types and Values.