話說昨天解決了MFC跨線程操作控件的問題,我滿以為今天可以free一回,玩玩Linux、學(xué)學(xué)Vim、再準(zhǔn)備一下畢業(yè)論文的事情,但還是有事情要做,然后又是“被”MFC郁悶了一天。
先介紹一下總體的情況。我們項(xiàng)目客戶端的開發(fā)環(huán)境是VS2008+SP1,用的是MFC類庫(kù),里面居然用到了CMFCToolBar、CMFCMenuBar以及Appearance變化等的SPI新特性。說“居然”是因?yàn)檫@些東西不是項(xiàng)目必要的,當(dāng)時(shí)可能也以為只是名字變了用法沒變,估計(jì)在工程創(chuàng)建的時(shí)候根本就沒有考慮這些,直接按著單文檔工程默認(rèn)配置,next、next直接創(chuàng)建完的,囧!當(dāng)時(shí)做的時(shí)候也只是當(dāng)作測(cè)試Demo來用,也沒太在意,畢竟我們項(xiàng)目的重點(diǎn)在服務(wù)器而非這個(gè)MFC客戶端。
后來由于項(xiàng)目原因,甲方要求我們把這個(gè)客戶端盡快修改成一個(gè)可以發(fā)布版本。不改不知道,一改嚇一跳,當(dāng)準(zhǔn)備動(dòng)手修改工具欄時(shí)才發(fā)現(xiàn)與以前慣的CToolBar真實(shí)差距甚大。CToolBar可以用CImageList把自定義的BMP圖片放到工具欄的按鈕,詳細(xì)可看
這里,CMFCToolBar根本就不是這樣的一個(gè)玩法。直接放一個(gè)CToolBar上來,在DockControlBar()的時(shí)候會(huì)出現(xiàn)斷言錯(cuò)誤(缺少DockBar,貌似是這個(gè)名字,汗?。6ㄎ淮a到MainFrm的EnableDocking(),現(xiàn)在的MainFrm的繼承關(guān)系是CMainFrm->CFrameWndEx->CFrameWnd,而以前是CMainFrm->CFrameWnd,CFrameWndEx::EnableDocking()是為DockPane()服務(wù)的,而DockControlBar()需要的DockBar并不會(huì)被初始化。調(diào)用基類的CFrameWnd::EnableDocking()后再DockControlBar()不會(huì)出現(xiàn)斷言,但是那個(gè)工具欄沒有顯示。而且現(xiàn)在新特性下在工具欄位置能夠按出右鍵菜單,但右鍵菜單中根本不可能有關(guān)于該CToolBar的信息,乍看起來很不和諧~
最后,求助本地MSDN無果,貌似SP1沒有包含對(duì)MSDN文檔的更新;求助MSDN官網(wǎng),那個(gè)真是“言簡(jiǎn)意賅”。只能說,MS你這次真的“亮”了!
以下為google + vs2008 sp1 sample + 看代碼的成果:
- 創(chuàng)建默認(rèn)ToolBar外的第二個(gè)ToolBar
1 //默認(rèn)工具欄
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時(shí)沒有帶ID,但第二個(gè)工具欄Create時(shí)最好要帶ID。加了ID之后,在工具欄右鍵菜單才會(huì)出現(xiàn)第二個(gè)工具欄的CheckBox。否則,不良后果有:1、右鍵菜單沒有該工具欄Checkbox;2、把默認(rèn)工具欄和該工具欄拖出來(浮動(dòng)),可以看到名字都是一樣的(英文版為Standard);3、后面要提到的UserImage不能作為按鈕圖標(biāo)顯示。
我們先來看看CMFCToolBar加載工具欄的函數(shù)原型:
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);
可以看出,uiResID代表要加載的工具欄資源,理論上只需要這一個(gè)參數(shù)就能完成工具欄的加載。但是VS的Toolbar Editor只能編輯4bit的工具欄圖標(biāo),以前CToolBar是用CImagList來加載更多bits的圖標(biāo)的,現(xiàn)在應(yīng)該怎么做呢?多虧了Explore sample的例子,我發(fā)現(xiàn)后面的幾個(gè)UINT參數(shù)就是BMP的資源,最主要的是最后一個(gè)uiHotResID,即便其他用默認(rèn)值,這項(xiàng)賦BMP ID就能按預(yù)期的圖標(biāo)顯示。Cold、Disable表示的是不同狀態(tài)下的圖標(biāo)樣式,帶Menu的是Menu有關(guān)的圖標(biāo),具體可看SP1 Feature的sample。
我的Demo里自定義工具欄的總創(chuàng)建過程:
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"));
最后的SetWindowText()設(shè)置工具欄的名稱。
CMFCToolBar有LoadBitmap的方法,但是測(cè)試發(fā)現(xiàn),用LoadToolBar只加載工具欄資源,再用LoadBitmap加載BMP資源,雖然返回值是TRUE,但顯示圖標(biāo)為空白,沒有實(shí)際效果。
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);
與默認(rèn)工具欄無異。
CMFCToolBar可以讓用戶自定義工具欄圖標(biāo),使用靜態(tài)成員函數(shù)SetUserImages()將一個(gè)CMFCToolBarImages對(duì)象設(shè)置進(jìn)去,由所有CMFCToolBar對(duì)象共享。Wizard自動(dòng)生成代碼中有這樣的例子:
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 }
這個(gè)例子加載了工程路徑下的一個(gè)BMP,其他方法可以查看MSDN,與CImageList有點(diǎn)點(diǎn)類似。
使用CMFCToolBar::ReplaceButton()可以替換已有的工具欄按鈕,以下是我的Demo中的代碼:
1 m_mybar.ReplaceButton( ID_QTLOGO, CMFCToolBarButton(ID_QTLOGO, 0, _T("123"), TRUE) );
第一個(gè)參數(shù)ID_QTLOGO為自定義工具欄上的一個(gè)按鈕,后面是一個(gè)
CMFCToolBarButton的臨時(shí)對(duì)象。CMFCToolBarButton構(gòu)造函數(shù)第一個(gè)參數(shù)為替換后的ID,第三個(gè)參數(shù)為名稱,第二個(gè)參數(shù)為圖標(biāo)的索引(zero-based),第四個(gè)參數(shù)為m_bUserButton,指明第二個(gè)參數(shù)是索引工具欄已加載圖標(biāo)(LoadToolBar或LoadBitmap)還是用戶自定義圖標(biāo)(SetuserImages),TRUE指用戶自定義圖標(biāo)。這里的結(jié)果是將ID_QTLOGO上的圖標(biāo)替換為
UserImages.bmp上的第一個(gè)圖標(biāo)。
GetCmdMgr()->GetCmdImage()可以根據(jù)工具欄上圖標(biāo)的ID獲取出已加載圖標(biāo)的索引值:
1 m_mybar.ReplaceButton( ID_QTLOGO, CMFCToolBarButton(ID_QTLOGO, GetCmdMgr()->GetCmdImage(ID_PLUS), _T("123")) );
這里將工具欄上ID_QTLOGO的圖標(biāo)替換為ID_PLUS按鈕對(duì)應(yīng)的圖標(biāo)。
特別地,如果在你將這些工具欄改來改去但顯示結(jié)果卻沒有改變的時(shí)候,你可以嘗試刪除
HKEY_CURRENT_USER\Software\Local AppWizard-Generated Applications\$(你的程序名) 這個(gè)鍵值,當(dāng)你重啟程序后工具欄應(yīng)該會(huì)按你的預(yù)想變化的。這是我在查資料時(shí)看到的,當(dāng)時(shí)沒注意但后來發(fā)現(xiàn)挺有用的,出處沒有記錄下來。
最后,ReplaceButton還可以將按鈕替換為其他控件。
我在自定義工具欄上做了一個(gè)有效響應(yīng),里面使用靜態(tài)成員函數(shù)CMFCToolBar::ResetAllImages()將所有圖標(biāo)都清空了,此時(shí)會(huì)發(fā)現(xiàn)默認(rèn)工具欄、自定義工具欄的圖標(biāo)都為空。
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 }
更奇妙的是,后面我對(duì)兩個(gè)工具欄重新加載了BMP,而且加載的BMP資源是反了的,此時(shí)默認(rèn)工具欄上出現(xiàn)了原來自定義工具欄的4個(gè)圖標(biāo),余下部分及自定義工具欄則為原來默認(rèn)工具欄圖標(biāo)。可以想象,RestAllImages只是將圖標(biāo)資源都釋放了,工具欄資源依然健在,重新加載BMP的時(shí)候,工具欄圖標(biāo)就像一個(gè)個(gè)順序排好的空間,加載進(jìn)來的BMP圖標(biāo)會(huì)出現(xiàn)從前往后補(bǔ)位的現(xiàn)象。
注意代碼中,默認(rèn)工具欄圖標(biāo)重新加載時(shí)使用的資源是IDR_MAINFRAME_256,是默認(rèn)的工具欄資源。也就是說,這里用LoadBitmap加載工具欄資源也是有效果的。這樣應(yīng)該可以說明工具欄在創(chuàng)建時(shí)LoadToolBar、LoadBitmap分別成功地加載了工具欄、BMP資源,實(shí)際上是加載了兩套圖標(biāo)資源,這兩者是順序而非重合的,所以只顯示原來的工具欄資源。要想指定兩者的重合關(guān)系,只有在LoadToolBar的時(shí)候同時(shí)傳入工具欄資源及BMP資源的ID。
Demo下載————————————————————————————————————————————————————————————————
好吧,終于寫完了!寫得很倉(cāng)促,不足的地方也很多,歡迎指教!