先介绍一下总体的情况。我们项目客户端的开发环境是VS2008+SP1,用的是MFC类库,里面居然用到了CMFCToolBar、CMFCMenuBar以及Appearance变化等的SPI新特性。说“居然”是因为这些东西不是项目必要的,当时可能也以为只是名字变了用法没变,估计在工程创建的时候根本就没有考虑这些,直接按着单文档工程默认配置,next、next直接创建完的,囧!当时做的时候也只是当作测试Demo来用,也没太在意,毕竟我们项目的重点在服务器而非这个MFC客户端。
后来由于项目原因,甲方要求我们把这个客户端尽快修改成一个可以发布版本。不改不知道,一改吓一跳,当准备动手修改工具栏时才发现与以前惯的CToolBar真实差距甚大。CToolBar可以用CImageList把自定义的BMP图片放到工具栏的按钮,详细可看 这里,CMFCToolBar根本就不是这样的一个玩法。直接放一个CToolBar上来,在DockControlBar()的时候会出现断言错误(缺少DockBar,貌似是这个名字,汗!)。定位代码到MainFrm的EnableDocking(),现在的MainFrm的继承关系是CMainFrm->CFrameWndEx->CFrameWnd,而以前是CMainFrm->CFrameWnd,CFrameWndEx::EnableDocking()是为DockPane()服务的,而DockControlBar()需要的DockBar并不会被初始化。调用基类的CFrameWnd::EnableDocking()后再DockControlBar()不会出现断言,但是那个工具栏没有显示。而且现在新特性下在工具栏位置能够按出右键菜单,但右键菜单中根本不可能有关于该CToolBar的信息,乍看起来很不和谐~
最后,求助本地MSDN无果,貌似SP1没有包含对MSDN文档的更新;求助MSDN官网,那个真是“言简意赅”。只能说,MS你这次真的“亮”了!
以下为google + vs2008 sp1 sample + 看代码的成果:
- 创建默认ToolBar外的第二个ToolBar
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 //默认工具栏
2 m_wndToolBar.CreateEx(this, TBSTYLE_FLAT,
3 WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
4 //自定义工具栏
5 m_mybar.CreateEx( this , TBSTYLE_FLAT,
6 WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC,
7 CRect( 1 , 1 , 1 , 1 ), ID_MYBAR);
注意,Wizard生成的工具栏Create时没有带ID,但第二个工具栏Create时最好要带ID。加了ID之后,在工具栏右键菜单才会出现第二个工具栏的CheckBox。否则,不良后果有:1、右键菜单没有该工具栏Checkbox;2、把默认工具栏和该工具栏拖出来(浮动),可以看到名字都是一样的(英文版为Standard);3、后面要提到的UserImage不能作为按钮图标显示。
- 加载工具栏资源
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 virtual BOOL LoadToolBar(UINT uiResID, UINT uiColdResID = 0 , UINT uiMenuResID = 0 , BOOL bLocked = FALSE,
2 UINT uiDisabledResID = 0 , UINT uiMenuDisabledResID = 0 , UINT uiHotResID = 0 );
我的Demo里自定义工具栏的总创建过程:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 if ( ! m_mybar.CreateEx( this , TBSTYLE_FLAT,
2 WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC,
3 CRect( 1 , 1 , 1 , 1 ), ID_MYBAR) ||
4 ! m_mybar.LoadToolBar( IDR_TOOLBAR1, 0 , 0 , FALSE, 0 , 0 , theApp.m_bHiColorIcons ? IDB_BITMAP1: 0 ) )
5 {
6 TRACE0( " Failed to create toolbar\n " );
7 return - 1 ; // fail to create
8 }
9 m_mybar.SetWindowText(_T( " abc " ));
CMFCToolBar有LoadBitmap的方法,但是测试发现,用LoadToolBar只加载工具栏资源,再用LoadBitmap加载BMP资源,虽然返回值是TRUE,但显示图标为空白,没有实际效果。
- 工具栏停靠
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 // TODO: Delete these five lines if you don't want the toolbar and menubar to be dockable
2 m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
3 m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
4 m_mybar.EnableDocking(CBRS_ALIGN_ANY);
5 EnableDocking(CBRS_ALIGN_ANY);
6 DockPane( & m_wndMenuBar);
7 DockPane( & m_wndToolBar);
8 DockPane( &m_mybar);
- 用户自定义图标
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 if (CMFCToolBar::GetUserImages() == NULL)
2 {
3 // load user-defined toolbar images
4 if (m_UserImages.Load(_T( " .\\UserImages.bmp " )))
5 {
6 m_UserImages.SetImageSize(CSize( 16 , 16 ), FALSE);
7 CMFCToolBar::SetSizes(CSize( 16 , 16 ), CSize( 16 , 16 ));
8 CMFCToolBar::SetUserImages( & m_UserImages);
9 }
10 }
使用CMFCToolBar::ReplaceButton()可以替换已有的工具栏按钮,以下是我的Demo中的代码:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 m_mybar.ReplaceButton( ID_QTLOGO, CMFCToolBarButton(ID_QTLOGO, 0 , _T( " 123 " ), TRUE) );
GetCmdMgr() -> GetCmdImage()可以根据工具栏上图标的ID获取出已加载图标的索引值:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 m_mybar.ReplaceButton( ID_QTLOGO, CMFCToolBarButton(ID_QTLOGO, GetCmdMgr() -> GetCmdImage(ID_PLUS), _T( " 123 " )) );
特别地,如果在你将这些工具栏改来改去但显示结果却没有改变的时候,你可以尝试删除 HKEY_CURRENT_USER\Software\Local AppWizard-Generated Applications\$(你的程序名) 这个键值,当你重启程序后工具栏应该会按你的预想变化的。这是我在查资料时看到的,当时没注意但后来发现挺有用的,出处没有记录下来。
最后,ReplaceButton还可以将按钮替换为其他控件。
- 其他...
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> 1 void CMainFrame::OnQtLogo()
2 {
3 CMFCToolBar::ResetAllImages();
4
5 // CMFCToolBar::AddToolBarForImageCollection(IDR_MENU_IMAGES, theApp.m_bHiColorIcons ? IDB_MENU_IMAGES_24 : 0);
6
7 m_wndToolBar.LoadBitmap(IDB_BITMAP1);
8 m_mybar.LoadBitmap(IDR_MAINFRAME_256);
9 m_wndToolBar.RedrawWindow();
10 m_mybar.RedrawWindow();
11 }
更奇妙的是,后面我对两个工具栏重新加载了BMP,而且加载的BMP资源是反了的,此时默认工具栏上出现了原来自定义工具栏的4个图标,余下部分及自定义工具栏则为原来默认工具栏图标。可以想象,RestAllImages只是将图标资源都释放了,工具栏资源依然健在,重新加载BMP的时候,工具栏图标就像一个个顺序排好的空间,加载进来的BMP图标会出现从前往后补位的现象。
注意代码中,默认工具栏图标重新加载时使用的资源是IDR_MAINFRAME_256,是默认的工具栏资源。也就是说,这里用LoadBitmap加载工具栏资源也是有效果的。这样应该可以说明工具栏在创建时LoadToolBar、LoadBitmap分别成功地加载了工具栏、BMP资源,实际上是加载了两套图标资源,这两者是顺序而非重合的,所以只显示原来的工具栏资源。要想指定两者的重合关系,只有在LoadToolBar的时候同时传入工具栏资源及BMP资源的ID。
Demo下载
————————————————————————————————————————————————————————————————
好吧,终于写完了!写得很仓促,不足的地方也很多,欢迎指教!
转载:http://www.360doc.com/content/11/1115/16/7992306_164560548.shtml