上一页 下一页 返回

3.7 更新命令用户接口(UI)消息

  一般情况下,菜单项和工具条按钮都不止一种状态,我们经常需要根据应用的内部状态来对菜单项和工具条按钮作相应的改变。例如,在我们没有选择任何内容时,编辑菜单下的“复制”、“剪切”等菜单是无效的(灰色显示)。有时,我们还会看到,在菜单项旁边可能还会有检查标记,表示它是选中的还是不选中的。比如,在Word的视图菜单下,根据用户所选的显示模式,在“普通”、“大纲”、“页面”“主控文档”前会出现一个点符号,标识当前所选的视图方式。工具条也有类似的情形,如果按钮不可用也可以被置成无效,或者可以被选中。
  如果我们采用SDK来编程,那么我们就要跟踪与这些状态相关的变量所有可能发生变化的地方,并根据可能发生的变化作相应的处理。这样的工作非常复杂且容易遗漏。为此,MFC应用程序框架引入了更新命令用户接口消息来简化这一工作。
  在ClassWizard的Message Map页中,如果我们选择一个菜单ID,在Messages列表框中就会出现两项:

COMMAND

UPDATE_COMMAND_UI

  其中UPDATE_COMMAND_UI就是更新命令用户接口消息,专门用于处理菜单项和工具条按钮的更新。每一个菜单命令都对应于一个更新命令用户接口消息。可以为更新命令用户接口消息编写消息处理函数来处理用户接口(包括菜单和工具条按钮)的更新。如果一条命令有多个用户接口对象(比如一个菜单项和一个工具条按钮),两者都被发送给同一个处理函数。这样,对于所有等价的用户接口对象来说,可以把用户接口更新代码封装在同一地方。

3.7.1用户接口更新原理

  为了理解用户接口更新机制,我们来看一下应用框架是如何实现用户接口更新的。当我们选择Edit菜单时,将产生一条WM_INITMENUPOPUP消息。框架的更新机制将在菜单拉下之前集体更新所有的项,然后再显示该菜单。
  为了更新所有的菜单项,应用框架按标准的命令发送路线把该弹出式菜单中的所有菜单项的更新命令都发送出去。通过匹配命令和适当的消息映射条目(形式为ON_UPDATE_COMMAND_UI),并调用相应的更新处理器函数,就可以更新任何菜单项。比如,Edit菜单下有Undo、Cut、Copy、Paste等四个菜单项,就要发送四条用户接口更新命令。如果菜单项的命令ID有一个更新处理器,它就会被调用进行更新;如果不存在,则框架检查该命令ID的处理函数是否存在,并根据需要使菜单有效或无效。
  如果在命令发送期间找不到对应于该命令的ON_UPDATE_COMMAND_UI项,那么框架就检查是否存在一个命令的ON_COMMAND项,如果存在,则使该菜单有效,否则就使该菜单无效(灰化)。这种更新机制仅适用于弹出式菜单,对于顶层菜单象File和Edit菜单,就不能使用这种更新机制。
  按钮的命令更新机制与菜单的命令接口更新机制类似,只是工具条按钮的命令接口更新在空闲循环时完成。有关工具条按钮的接口更新机制,我们将在下一章“工具条和状态栏”中作详细介绍。

3.7.2 用户接口更新机制编程

  当框架给处理函数发送更新命令时,它给处理函数传递一个指向CCmdUI对象的指针。这个对象包含了相应的菜单项或工具条按钮的指针。更新处理函数利用该指针调用菜单项或工具条的命令接口函数来更新用户接口对象(包括灰化,使,使能,选中菜单项和工具条按钮等)。下面我们使用前面的例子演示如何使用用户接口更新机制:

1.按Ctrl+W激活ClassWizard,选择Message Map选项页。
2.在Object IDs列表中选择 ID_SELECT_RED,在Messages列表中双击ON_UPDATE _COMMAND_UI条目,弹出Add Member Function对话框,缺省函数名为OnUpdateSelect Red,按OK按钮接收此函数名。OnUpdateSelectRed成员函数名就出现在Member Functions列表中。依次给ID_SELECT_BLUE、ID_SELECT_YELLOW增加OnUpdateSelectBlue和OnUpdateSelectYellow命令接口更新成员函数。
3.现在手工编辑刚才生成的成员函数,修改后形式如清单3.5所示:

清单3.5

void CMainFrame::OnUpdateSelectBlue(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->SetCheck(m_nColor==BLUE);

}

void CMainFrame::OnUpdateSelectRed(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->SetCheck(m_nColor==RED);

}

void CMainFrame::OnUpdateSelectYellow(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->SetCheck(m_nColor==YELLOW);

}

  现在编译运行Hello程序。当我们打开Test菜单时,如图3-14所示,在Red菜单项前已经有了一个对号。因为前面在CMainFrame构造函数中,我们已经将颜色初始化为RED。如果选择Blue,下次打开Test菜单时,在Blue菜单前就会有一个对号,而Red前面的对号则没有了。

T3_14.tif (268858 bytes)

图3-14

  类似的,要根据某个状态开关菜单,也可以为菜单生成命令接口更新成员函数。比如,在Edit菜单中,如果当前剪贴板没有内容,Paste(粘贴)菜单应当设为无效,程序可以这么写:

void CMainFrame::OnUpdateEditPaste(CCmdUI* pCmdUI)

{

pCmdUI->Enable(!IsClipboardEmpty());

}

  其中IsClipboardEmtpy()是读者自己编写的函数,用于判断剪贴板中是否有内容