添加控制时钟的新菜单项
在菜单条添加一个Clock菜单,它有两个菜单项:IDC_START and IDC_STOP:
然后在UPDATE_UI_MAP宏中为每个菜单项添加一个入口:
class CMainFrame : public ... { public: // ... BEGIN_UPDATE_UI_MAP(CMainFrame) UPDATE_ELEMENT(IDC_START, UPDUI_MENUPOPUP) UPDATE_ELEMENT(IDC_STOP, UPDUI_MENUPOPUP) END_UPDATE_UI_MAP() // ... }; |
我们只需要调用CUpdateUI::UIEnable()就可以改变这两个菜单项的任意一个的使能状态时。UIEnable()有两个参数,一个是界面元素的ID,另一个是标志界面元素是否可用的bool型变量(true表示可用,false表示不可用)。
这套体系比MFC的ON_UPDATE_COMMAND_UI体系笨拙一些,在MFC中我们只需编写处理函数,由MFC选择界面元素的显示状态,在WTL中我们需要告诉WTL界面元素的状态在何时改变。当然,这两个库都是在菜单将要显示的时候才应用菜单状态的改变。
调用 UIEnable()
现在返回到OnCreate()函数看看是如何设置Clock菜单的初始状态。
LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { m_hWndClient = m_view.Create(...);
// register object for message filtering and idle updates // [omitted for clarity]
// Set the initial state of the Clock menu items: UIEnable ( IDC_START, false ); UIEnable ( IDC_STOP, true );
return 0; } |
我们的程序开始时Clock菜单是这样的:
CMainFrame现在需要处理两个新菜单项,在视图类调用它们开始和停止时钟时处理函数需要翻转这两个菜单项的状态。这是MFC的内建消息处理无法想象的地方之一。在MFC的程序中,所有的界面更新和命令消息处理必须完整的放在视图类中,但是在WTL中,主窗口类和视图类通过某种方式沟通;菜单由主窗口拥有,主窗口获得这些菜单消息并做相应的处理,要么响应这些消息,要么发送给视图类。
这种沟通是通过PreTranslateMessage()完成的,当然CMainFrame仍然要调用UIEnable()。CMainFrame可以将this指针传递给视图类,这样视图类也可以通过这个指针调用UIEnable()。在这个例子中我选择的这种解决方案导致主窗口和视图成为紧密耦合体,但是我发现这很容易理解(和解释!)。
class CMainFrame : public ... { public: BEGIN_MSG_MAP_EX(CMainFrame) // ... COMMAND_ID_HANDLER_EX(IDC_START, OnStart) COMMAND_ID_HANDLER_EX(IDC_STOP, OnStop) END_MSG_MAP()
// ... void OnStart(UINT uCode, int nID, HWND hwndCtrl); void OnStop(UINT uCode, int nID, HWND hwndCtrl); };
void CMainFrame::OnStart(UINT uCode, int nID, HWND hwndCtrl) { // Enable Stop and disable Start UIEnable ( IDC_START, false ); UIEnable ( IDC_STOP, true );
// Tell the view to start its clock. m_view.StartClock(); }
void CMainFrame::OnStop(UINT uCode, int nID, HWND hwndCtrl) { // Enable Start and disable Stop UIEnable ( IDC_START, true ); UIEnable ( IDC_STOP, false );
// Tell the view to stop its clock. m_view.StopClock(); } |
每个处理函数都更新Clock菜单,然后在视图类中调用一个方法,选择在视图类中使用是因为时钟是由视图类控制得。StartClock() 和 StopClock()得代码没有列出,但可以在这个工程得例子代码中找到它们。
消息映射链中最后需要注意的地方
如果你使用VC++(www.cppentry.com)6.0,你会注意到将BEGIN_MSG_MAP改为BEGIN_MSG_MAP_EX后ClassView显得有些杂乱无章:
出现这种情况是因为ClassView不能解释BEGIN_MSG_MAP_EX宏,它以为所有得WTL消息映射宏是函数定义。你可以将宏改回为BEGIN_MSG_MAP并在stdafx.h文件得结尾处添加这两行代码来解决这个问题:
#undef BEGIN_MSG_MAP #define BEGIN_MSG_MAP(x) BEGIN_MSG_MAP_EX(x) |
下一站,古老的1995
我们现在只是掀起了WTL的一角,在下一篇文章我会为我们的时钟程序添加一些Windows 95的界面标准,比如工具条和状态条,同时体验一下CUpdateUI的新东西。例如试着用UISetCheck()代替UIEnable(),看看菜单项会有什么变化。 |