源代码下载:http://download.csdn.net/source/3522797
CFrameWindowImpl包含三个HWND对象,其中一个m_hWndClient上节已经讲过,另外两个m_hWndToolBar用于表示工具条句柄,m_hWndStatusBar用于状态条句柄。
CFrameWindowImpl仅支持一个工具条,不能像MFC那样直接支持多个可停靠的工具条。如果想实现这样的效果,需要使用Rebar。
CFrameWindowImpl::OnSize()将调用UpdateLayout()做两件事情:1、定位bars;2、缩放view窗口以填充视图区域。UpdateLayout()调用UpdateBarsPosition()执行实际的工作,它仅仅发送WM_SIZE到已创建的工具条和状态条,默认处理是把工具条移动Frame窗口的顶端,把状态条移至Frame窗口的低端。
AppWizard可以自动生成工具条和状态条。
AppWizard会自动生成一些代码,用于创建Bars以及告诉CUpdateUI更新toolbar的buttons。
下面是CFrameWindowImpl::CreateSimpleToolBar()的源代码
nResourceID
DECLARE_FRAME_WND_CLASS
macro指定的资源。即AppWizard自动生成的
IDR_MAINFRAME.
dwStyle
ATL_SIMPLE_TOOLBAR_STYLE 表示普通可见的,鼠标放在按钮上时有toolTip。
nID
CreateSimpleToolBar()
检查这个工具条有没有已经被创建,然后调用 CreateSimpleToolBarCtrl()
创建控件,并返回控件句柄存入m_hWndToolBar。
CreateSimpleToolBarCtrl()
读取资源并逐个创建按钮,然后返回toolbar窗口句柄。
以下是CreateSimpleToolBar的实现代码:
检查状态条是否已经创建,调用CreateStatusWindow创建状态条,并把状态条句柄存入hWndStatusBar。
CMainFrame
在 “视图”菜单下有两个命令,用于显示 / 隐藏工具栏和状态栏。
其中工具栏的命令响应代码如下:
MFC为工具栏和状态栏提供了一些好的特性。WTL的CFrameWindowImpl
也实现了相应的特性,如tooltip和flyby help:
CFrameWindowImplBase
有两个消息处理用于实现这些特性。OnMenuSelect()
处理消息WM_MENUSELECT
,它像MFC一样查找flyby help字符串(从字符串资源中加载与当前选择的菜单项同ID的字符串,并搜索"\n"字符,用\n之前的字符串作为flyby help字符串)。OnToolTipTextA()和
一样的方法加载字符串,但是它用\n之后的文字作为ToolTip text。OnToolTipTextW()处理消息
TTN_GETDISPINFOA
和TTN_GETDISPINFOW
,分别为工具栏按钮提供Tooltip 文本,它用与OnMenuSelect
注意:在WTL7.0/7.1中,OnMenuSelect()
和 OnToolTipTextA()
不是DBCS(双字节字符集)安全的,在搜索\n字符时,它们不检查DBCS的lead/trail(前导/后继)字节。
你可以通过改变CreateSimpleToolBar ()中第二个参数的值,实现改变工具栏的风格。
AppWizard 会自动为我们生成一个工具条,只有帮助按钮是实现了的。我们可以向MFC工程一样修改工具条。此处略。
修改工具栏如下所示:
IDC_CP_COLORS
: 改变视图颜色为CodeProject颜色IDC_BW_COLORS
: 改变视图为白色背景,黑色前景的颜色ID_VIEW_STATUS_BAR
: 显示或隐藏状态栏ID_VIEW_TOOLBAR
: 显示或隐藏工具栏 前两个按钮的ID是新添的,绑定命令消息要使用宏COMMAND_ID_HANDLER_EX
。
AppWizard生成的CMainFrame已经包含了一部分的UI更新处理(选中和非选中工具栏和状态栏菜单按钮),如下:
我们的时钟程序在工具栏上也有这两个ID,因此我们需要添加宏UPDUI_TOOLBAR,以绑定工具栏对应按钮的更新:
另外,还有两个函数用于更新工具栏按钮,AppWizard生成的代码会调用它们的。
CMainFrame::OnCreate()函数里,你可以看到一块代码用于初始化工具栏和状态栏菜单状态:
其中UIAddToolBar()
把工具栏的句柄传递给CUpdateUI,因此当需要更新按钮状态时它知道往哪个窗口发送消息。另一个重要的调用是
:OnIdle()
当消息队列中没有消息时,CMessageLoop::Run()
将重复调用OnIdle()
。UIUpdateToolBar()
通过Update UI map,搜索带UPDUI_TOOLBAR
标志的那些调用UISetCheck()改变的元素,并且因此改变按钮状态。注意:我们仅仅更新了push menu元素的状态,不需要去做这两步,因为
CUpdateUI
处理消息WM_INITMENUPOPUP
并且当消息送达时直接更新了菜单状态。
对于菜单栏的更新,要使用UIAddMenuBar()
和 UIUpdateMenuBar()。
CFrameWindowImpl
支持使用Rebar使我们的程序看起来像Internet Explorer。(在用Appwizard创建新工程时,选择使用Rebar)
下面是AppWizard生成的创建Rebar的相关代码:
它首先使用一个不同的风格ATL_SIMPLE_TOOLBAR_PANE_STYLE
创建一个toolbar,与ATL_SIMPLE_TOOLBAR_STYLE
相比,ATL_SIMPLE_TOOLBAR_PANE_STYLE增加了
CCS_NOPARENTALIGN,它是必须的。
CreateSimpleReBar()创建一个Rebar,并把其句柄存入
m_hWndToolBar,然后调用
AddSimpleReBarBand()为这个Rebar添加一个Band,并且告诉Rebar,这个Band已经包含了这个工具栏。
CMainFrame::OnViewToolBar()
也是不一样的。它隐藏包含这个工具栏的Band,而不是这个Rebar(这样会隐藏整个rebar,而不仅仅是这一个Toolbar)。
如果你想有多个工具条,就像Appwizard生成的代码一样,在OnCreate()中调用
AddSimpleReBarBand()。既然
CFrameWindowImpl使用标准的Rebar控件,它不支持可停靠的工具栏。
WTL提供了一个实现多面板的状态栏类CMultiPaneStatusBarCtrl。它支持有限的UI更新,当PopUP 菜单显示时,默认面板会被拉伸到与状态栏同宽以显示flyby help文字。
首先要在CMainFrame类中声明一个变量
然后,在OnCreate()中创建bar并设置UI更新
与CreateSimpleStatusBar()一样,我们同样把句柄存到
m_hWndStatusBar。
下一步,调用CMultiPaneStatusBarCtrl::SetPanes()设置面板:
参数描述如下:
pPanes
:面板ID数组
nPanes :面板个数
bSetText
:如果为true, 所有盘子立即显示字符串. 解释如下.
面板IDs,既可以是ID_DEFAULT_PANE来创建用于显示flyby help 的面板,也可以是字符串表中的一组字符串的IDs
. 对于无默认面板的情况,WTL加载匹配ID的字符串并计算它们的宽度,然后为其对应的面板设置同样的宽度。与MFC使用的逻辑相同。
bSetText
控制面板是否立即显示字符串。 如果为true, SetPanes()
在每一个盘子里都显示字符串, 否则留空.
IDPANE_STATUS的字符串是“@@@@”,它用于使这个盘子有足够的宽度显示时钟状态:“Running”或“Stopped”。就像MFC一样,首先需要计算你的这个盘子需要多大的空间。IDPANE_CAPS_INDICATOR用于显示“CAPS”
为了更新状态栏文字,需要写入UI更新路由:
第一个参数是盘子的索引,不是ID。如果你需要重排列这些盘子,你需要更新设个参数以适应其对应的顺序。
我们在SetPanes的第三个参数传入的是false,因此所有盘子初始化均为空。下一步为指定盘子设置初始文字。
UISetText()
是唯一一个状态条UI更新函数。
最后,我们需要在CMainFrame::OnIdle()
中添加调用UIUpdateStatusBar()
,使状态栏能够在空闲时间更新。
当使用UIUpdateStatusBar()
时CUpdateUI
会出现一个问题:在使用UISetText()
后菜单文字并没有被更新。这个问题在WTL7.1中还存在。
最后,我们需要检查Caps Lock状态并更新2号盘的文字,在OnIdle()中实现,只要程序处在空闲时间就去检查此状态。
第一个UISetText()使用从字符表中加载的“CAPS”字符串,使用CString以实现多格式支持。
原文:WTL for MFC Programmers, Part III - Toolbars and Status Bars
转自:http://blog.csdn.net/wcyoot/article/details/6645176