问题的提出:
一个应用程序想要动态改变菜单项。使用CCmdUI::SetText("Menu Text")可以改变菜单文本,但是如何动态改变工具条和状态条的文本呢?
有几种策略,避免,欺骗,面对......
首先,避免:为什么你非要动态改变菜单项?一般说来,这是个坏主意,动态菜单容易把人搞糊涂。我正在使用你的产品,本来用得好好的突然菜单项变了。不管什么时候,每当我看到一个改变菜单的应用时,都要琢麽为什么他们需要这样的用户界面设计。
然而,每一个规则都有例外,许多例子的动态改变菜单项都很酷。例如,在大多数面向文档的应用程序中“文件”菜单的最后一项MRU(最近使用的文件列表)。但作为一个用户,面对动态菜单项的弊端是显而易见的。我把避免动态菜单提升为设计准则。即便是采用了动态菜单的设计,也要让用户注意不到菜单项是改变,否则,It''s
bad design。反之,如果用户注意不到菜单项的改变,It''s OK。
但是动态改变状态条提示又如何呢?在MRU菜单中,无论什么文件,状态条一般都提示“打开选择的文档”。这是另一个要避免的策略。只有特别本位或任性的程序员会操心实现一个动态提示的菜单,如:“打开某某文件”,而不去用完全可行并且有效的提示“打开这个文档”。你完全有权利不遵循这种惯例,也就是说,如果你非要改变状态条提示的话,那就请往下继续看吧,你会明白的。
使用动态菜单的另一场合是当你想设置某个布尔状态时。例如,隐藏或显示工具条,当工具条可见时显示“隐藏工具条”,反之显示“显示工具条”。更为普通的方法是用单个命令以校验标记来实现,当工具条可见时显示标记(如下图)。
--------------------next---------------------
GUI的高手们常常争论哪种方法更好。可能它没有什么差别,但是即使你决定使用动态提示(如隐藏/显示工具条),你也能使用单个的命令ID,ID_VIEW_TOOLBAR,和单个的提示“隐藏或显示工具条”。我认为没有必要去实现动态提示。
在所有建议中,你要做的第一件事情是好好重新考虑用户的界面。你确实需要动态菜单项吗?以及你确实需要菜单的动态提示吗?除非两个问题的答案都是“是”。否则就止住,别再浪费时间。
要改变菜单文本是容易的。只要实现ON_UPDATE_COMMAND_UI处理器并调用CCmdUI::SetText即可:
void CFrameWnd::OnUpdateToolbar(CCmdUI* pCmdUI)
{
BOOL bVisible = IsToolbarVisible(...);
// Note same mnemonic (&T) for both cmds!
pCmdUI->SetText(bVisible ? "Hide &Toolbar"
: "Show &Toolbar");
}
仅此而已。下一步是提示。当你创建了一个菜单提示,你给它一个ID号。MFC使用这个ID来查找资源串获取命令提示。例如:
STRINGTABLE DISCARDABLE
BEGIN
ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle
ToolBar"
END
如果你的菜单命令也有工具条按钮,MFC用“\n”(新行标记)后的文本作为工具条提示文本。因为MFC允许每个命令只能有一个串,如何动态改变提示呢?最简单的方法是编写一个提示在两种情况下都工作,象前面讨论的隐藏、显示工具条的例子。但这种方法显得很笨拙。
获得动态提示的一个方法是将命令分成几个命令-例如,ID_HIDE_ TOOLBAR 和ID_SHOW_TOOLBAR,只是一种欺骗策略。这些命令的命令处理器最终要做的事情是改变菜单项的ID为其它命令项的ID。具体实现细节我就不讲了,自己做吧。
使用两个ID可能是一种简单的方法,但它不适用于所有情况。例如在MRU文件菜单中,对于每个可能的文件名字你会需要不同的ID。
本文提供一个例子程序,DynPrompt,如下图,
--------------------next---------------------
状态条采用了动态提示,为了理解DynPrompt是如何工作的,你必须对MFC的菜单提示有一些研究。当用户的鼠标
移动到一个菜单项时,Windows发送WM_MENUSELECT和菜单项的ID。MFC的CFrameWnd处理如下:
// much simplified
void CFrameWnd::OnMenuSelect(UINT nItemID,
UINT nFlags, HMENU hSysMenu)
{
SendMessage(WM_SETMESSAGESTRING, nItemID);
}
我做了一些简化;函数的实际代码超过了60行,但基本的意思是框架发送WM_SETMESSAGESTRING消息到自身,用WPARAM传递命令ID。SETMESSAGESTRING 是MFC的一个私有消息,它在afxpriv.h中定义。这个消息在状态条窗格中设置
要显示的文本。你可以用WPARAM传递资源串的ID,或者用LPARAM传递实际的串。
// resource string ID
SendMessage(WM_SETMESSAGESTRING, ID_MYSTRING);
// string
SendMessage(WM_SETMESSAGESTRING, 0, (LPARAM)_T("Hello, world"));
所以,如果要实现动态菜单提示,必须重载CFrameWnd::OnMenuSelect和 用提示串发送WM_SETMESSAGESTRIN消息。
void CMainFrame::OnMenuSelect(UINT nItemID, UINT nFlags,
HMENU hSysMenu)
{
if (/* nItemID has a dynamic prompt */) {
CString sPrompt = // whatever
you want
SendMessage(WM_SETMESSAGESTRING,
0, (LPARAM)(LPCTSTR)sPrompt);
m_nIDTracking = nItemID;
} else {
CFrameWnd::OnMenuSelect(nItemID,
nFlags, hSysMenu);
}
}
MainFrm.cpp文件中的OnMenuSelect调用一连串函数从MRU菜单项来截获
文件名并建立所要的文本提示。别忘了还要调用CFrameWnd::OnMenuSelect来处理 未改变的提示的命令。
最后,对于如何动态改变工具提示文本的方法,CFrameWnd::OnToolTipText是MFC处理工具条通知的函数。其标准实现用匹配的命令ID加载资源串,截获“\n”后的文本并将它拷贝调用者的TOOLTIPTEXT结构。你的任务是编写自己的
代码重载这个处理器。我把这个作为家庭作业。
--------------------next---------------------
阅读(226) | 评论(0) | 转发(0) |