MFC 程序员的 WTL 教程 ( 3 ) — 工具栏和状态栏
第三部分 – 工具栏和状态栏
内容
第三部分介绍从在 Windows 95 中被作为通用控件开始,使用工具栏和状态栏已经成为了平常事。MFC 对多浮动工具栏的支持也对他们的流行起了推波助澜的作用。在后来的通用控件升级中,复用栏(Rebar,或者最初被称为的酷栏,coolbar)又为如何呈现工具栏增添了新的途径。在这一部分里,我会涵盖以下知识,WTL 如何对这些种类的栏进行支持以及如何在自己的应用中使用它们。
记住,如果你安装 WTL 或者编译示例代码时遇到了任何问题,请在张贴你的问题之前阅读第一部分的 ReadMe 一节。 框架中的工具栏和状态栏
如果你要求 AppWizard 给你的框架窗口工具栏和状态栏,那么向导会向 AppWizard 为工具栏和状态栏生成的代码我们来开始一个新工程并让向导为框架窗口生成工具栏和状态栏。创建一个名为 WTLClock2 的 WTL 工程。在 AppWizard 的第一页,选择 SDI 应用,并选中 Generate CPP files: 在下一页,不要选 Rebar 以使向导生成普通的工具栏: 从第二部分的应用中把时钟相关的代码复制过来之后,这个新的应用看起来应该是这样: CMainFrame 如何创建栏在这个工程里,AppWizard 向 LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { CreateSimpleToolBar(); CreateSimpleStatusBar(); m_hWndClient = m_view.Create(...); // ... // register object for message filtering and idle updates CMessageLoop* pLoop = _Module.GetMessageLoop(); ATLASSERT(pLoop != NULL); pLoop->AddMessageFilter(this); pLoop->AddIdleHandler(this); return 0; } 新代码在函数的开头。 BOOL CFrameWindowImpl::CreateSimpleToolBar(
UINT nResourceID = 0,
DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE,
UINT nID = ATL_IDW_TOOLBAR)
{
ATLASSERT(!::IsWindow(m_hWndToolBar));
if(nResourceID == 0)
nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
m_hWndToolBar = T::CreateSimpleToolBarCtrl(m_hWnd, nResourceID, TRUE, dwStyle, nID);
return (m_hWndToolBar != NULL);
}
参数:
BOOL CFrameWindowImpl::CreateSimpleStatusBar(
UINT nTextID = ATL_IDS_IDLEMESSAGE,
DWORD dwStyle = ... SBARS_SIZEGRIP,
UINT nID = ATL_IDW_STATUS_BAR)
{
TCHAR szText[128]; // max text lentgth is 127 for status bars
szText[0] = 0;
::LoadString(_Module.GetResourceInstance(), nTextID, szText, 128);
return CreateSimpleStatusBar(szText, dwStyle, nID);
}
这会从字符串表中加载一个字符串,它会被显示到状态栏中。参数:
BOOL CFrameWindowImpl::CreateSimpleStatusBar(
LPCTSTR lpstrText,
DWORD dwStyle = ... SBARS_SIZEGRIP,
UINT nID = ATL_IDW_STATUS_BAR)
{
ATLASSERT(!::IsWindow(m_hWndStatusBar));
m_hWndStatusBar = ::CreateStatusWindow(dwStyle, lpstrText, m_hWnd, nID);
return (m_hWndStatusBar != NULL);
}
这个版本的函数如果检查到状态栏尚未创建,则会调用 显示或者隐藏栏
LRESULT CMainFrame::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); UpdateLayout(); return 0; } 此函数切换指定栏的可视状态,切换 View|Toolbar 菜单项旁边的选中标志,然后再调用 栏的内建特性MFC 为其工具栏和状态栏提供了一些很好的特性,比如作用于工具栏按钮的工具提示和菜单项的动态帮助。WTL 在
使用不同的风格创建工具栏你可以向 CreateSimpleToolBar ( 0, ATL_SIMPLE_TOOLBAR_STYLE |
TBSTYLE_FLAT | TBSTYLE_LIST );
注意,如果你在应用里使用了第六版通用控件的 manifest 文件,那么当运行在 Windows XP 以及后续的系统上时,你对按钮就没有选择余地了 – 工具栏总是使用平面按钮,即使在创建工具栏时没有指定 工具栏编辑器正如在前面所看到的,AppWizard 会创建好几个缺省的按钮,不过只有 About 按钮会被处理。你可以像在 MFC 工程中一样编辑工具栏,编辑器可以修改 对我们的时钟应用来说,我们要添加两个按钮,用来改变视图窗口的颜色,另外两个按钮来显示或者隐藏工具栏以及状态栏。下面是我们的新工具栏: 按钮:
前两个按钮在 View 菜单上也有其对应的菜单项。它们都会调用视图类的一个新函数,名为 工具栏按钮的 UI 更新AppWizard 生成的 BEGIN_UPDATE_UI_MAP(CMainFrame)
UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
END_UPDATE_UI_MAP()
由于我们的时钟应用的工具栏有 ID 相同的按钮,所以第一步就是为每个宏添加 BEGIN_UPDATE_UI_MAP(CMainFrame)
UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
END_UPDATE_UI_MAP()
要处理工具栏按钮的更新,还要调用另外的两个函数,不过幸好 AppWizard 生成的代码里应经有了。所以,如果你现在就编译工程,菜单项和工具栏按钮都可以更新。 启用工具栏 UI 更新如果你看 LRESULT CMainFrame::OnCreate( ... )
{
// ...
m_hWndClient = m_view.Create(...);
UIAddToolBar(m_hWndToolBar);
UISetCheck(ID_VIEW_TOOLBAR, 1);
UISetCheck(ID_VIEW_STATUS_BAR, 1);
// ...
}
BOOL CMainFrame::OnIdle()
{
UIUpdateToolBar();
return FALSE;
}
当消息队列里没有等待的消息时, 如果你查看示例工程的代码,你还会看到 UI 更新是如何更新顶级菜单项的。有一个菜单项,是执行 Start 和 Stop 命令来开始或停止时钟的。当然,这不是常见的做法(也不推荐这样做) — 菜单栏上的项应该都是弹出菜单 — 我之所以这么做是出于完整演示 使用复用栏替代平实的工具栏
我们的第二个例子,WTLClock3,就选中了这一选项。如果你要一路追踪示例代码,那现在就打开 WTLClock3 工程。 你注意到的第一个不同之处应该是创建工具栏的代码。这是有道理的,因为我们是要在应用中使用复用栏。下面是相关的代码: LRESULT CMainFrame::OnCreate(...)
{
HWND hWndToolBar = CreateSimpleToolBarCtrl ( m_hWnd,
IDR_MAINFRAME, FALSE,
ATL_SIMPLE_TOOLBAR_PANE_STYLE );
CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
AddSimpleReBarBand(hWndToolBar);
// ...
}
代码是以创建工具栏开始的,但是使用了不同的风格 下一行是对
如果需要多个工具栏,你可以在 多窗格状态栏WTL 还有另外一个状态栏类,实现了在一个栏内包括多个窗格,就像 MFC 里那种有 CAPS LOCK 和 NUM LOCK 指示器的默认状态栏那样。这个类叫做 第一步在 class CMainFrame : public ... { //... protected: CMultiPaneStatusBarCtrl m_wndStatusBar; }; 然后在 m_hWndStatusBar = m_wndStatusBar.Create ( *this );
UIAddStatusBar ( m_hWndStatusBar );
注意,和 下一步则是通过调用 BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true); 参数:
窗格 ID 既可以是
下面是调用 // Create the status bar panes. int anPanes[] = { ID_DEFAULT_PANE, IDPANE_STATUS, IDPANE_CAPS_INDICATOR }; m_wndStatusBar.SetPanes ( anPanes, 3, false ); 字符串 窗格的 UI 更新为了能够更新窗格的文本,我们要在 UI 更新表中新添入口: BEGIN_UPDATE_UI_MAP(CMainFrame)
//...
UPDATE_ELEMENT(1, UPDUI_STATUSBAR) // clock status
UPDATE_ELEMENT(2, UPDUI_STATUSBAR) // CAPS indicator
END_UPDATE_UI_MAP()
宏的第一个参数是窗格的索引,而非 ID。如果你要重新排列窗格,那你就要关注此事,如果在映射里并非每个窗格都有入口的话,你需要更新 由于我们把 // Set the initial text for the clock status pane. UISetText ( 1, _T("Running") ); 和上面一样,第一个参数也是窗格的索引。 最后,我们还需要在 BOOL CMainFrame::OnIdle()
{
UIUpdateToolBar();
UIUpdateStatusBar();
return FALSE;
}
当你使用 最后,我们需要检查 CAPS LOCK 键的状态并相应更新第二个窗格。代码放到 BOOL CMainFrame::OnIdle()
{
// Check the current Caps Lock state, and if it is on, show the
// CAPS indicator in pane 2 of the status bar.
if ( GetKeyState(VK_CAPITAL) & 1 )
UISetText ( 2, CString(LPCTSTR(IDPANE_CAPS_INDICATOR)) );
else
UISetText ( 2, _T("") );
UIUpdateToolBar();
UIUpdateStatusBar();
return FALSE;
}
第一次调用 完成了这些代码后,状态栏看起来应该是这样: 下一步:对话框的一切在第四部分里,我会介绍对话框(包括 ATL 的类以及 WTL 的增强),控件封装类,以及更多的与对话框和控件相关的 WTL 消息处理的改进。 参考资料“How to use the WTL multipane status bar control” ,Ed Gadziemski,其中有 修订历史2003 年 4 月 11 日:首次发布 |

近期评论