在第二頁(yè),取消Rebar使向導僅僅創(chuàng )建一個(gè)普通的工具條: 從第二部分的程序中復制相應的代碼,新程序看起來(lái)是這樣的:
CMainFraCMainFrame 如何創(chuàng )建工具條和狀態(tài)條 在這個(gè)例子中,向導向CMainFrame::OnCreate()函數添加了更多的代碼,這些代碼的作用就是創(chuàng )建控制條并通知CUpdateUI更新工具條上的按鈕。
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;}這是新添加的代碼的開(kāi)始部分,CFrameWindowImpl::CreateSimpleToolBar()函數使用資源IDR_MAINFRAME創(chuàng )建工具條并將其句柄賦值給m_hWndToolBar,下面是CreateSimpleToolBar()函數的代碼: 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);}參數: - nResourceID
- 工具條資源得ID。如果使用默認值0作為參數,程序將使用DECLARE_FRAME_WND_CLASS宏指定得資源,這里使用的IDR_MAINFRAME是向導生成的代碼。
- dwStyle
- 工具條的類(lèi)型或樣式。默認值ATL_SIMPLE_TOOLBAR_STYLE被定義為T(mén)BSTYLE_TOOLTIPS,子窗口和可見(jiàn)三種風(fēng)格的結合,這使得鼠標移到按鈕上時(shí)工具條會(huì )彈出工具提示。
- nID
- 工具條的窗口ID,通常都會(huì )使用默認值。
CreateSimpleToolBar()首先檢查是否已經(jīng)創(chuàng )建了一個(gè)工具條,然后調用CreateSimpleToolBarCtrl()函數創(chuàng )建工具條控制,CreateSimpleToolBarCtrl()返回的工具條控制句柄保存在m_hWndToolBar中。CreateSimpleToolBarCtrl()負責讀出資源并創(chuàng )建相應的工具條按鈕,然后返回工具條窗口的句柄。這部分的代碼相當長(cháng),我不在這里做具體介紹,如果你對此感興趣得話(huà)何以在atlframe.h中找到這些代碼。 OnCreate()函數接下來(lái)會(huì )調用CFrameWindowImpl::CreateSimpleStatusBar()函數,此函數創(chuàng )建狀態(tài)條并將句柄存在m_hWndStatusBar,下面是該函數的代碼: 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);}顯示在狀態(tài)條的文字是從字符串資源中裝載的,這個(gè)函數的參數是: - nTextID
- 用于在狀態(tài)條上顯示的字符串的資源ID,向導生成的ATL_IDS_IDLEMESSAGE對應的字符串是“Ready”。
- dwStyle
- 狀態(tài)條的樣式。默認值包含了SBARS_SIZEGRIP風(fēng)格,這使得狀態(tài)條的右下角會(huì )顯示一個(gè)改變窗口大小的標志。
- nID
- 狀態(tài)條的窗口ID,通常都會(huì )使用默認值。
CreateSimpleStatusBar()調用另外一個(gè)重載函數創(chuàng )建狀態(tài)條: 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);}這個(gè)重載的版本首先檢查是否已經(jīng)創(chuàng )建了狀態(tài)條,然后調用CreateStatusWindow()創(chuàng )建狀態(tài)條,狀態(tài)條的句柄存放在m_hWndStatusBar中。 顯示和隱藏工具條和狀態(tài)條 CMainFrame類(lèi)也有一個(gè)視圖菜單,它有兩個(gè)命令:顯示/隱藏工具條和狀態(tài)條,它們的ID是ID_VIEW_TOOLBAR和ID_VIEW_STATUS_BAR。CMainFrame類(lèi)有這兩個(gè)命令的響應函數,分別顯示和隱藏相應的控制條,下面是OnViewToolBar()函數的代碼: 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;}這些代碼翻轉控制條的顯示狀態(tài),相應的翻轉View|Toolbar菜單上的檢查標記,然后調用UpdateLayout()重新定位控制條并改變視圖窗口的大小。 工具條和狀態(tài)條的內在特征 MFC的框架提供了很多好的特性,例如工具條按鈕的工具提示和菜單項的掠過(guò)式幫助。WTL中相對應的功能實(shí)現在CFrameWindowImpl類(lèi)中。下面的屏幕截圖顯示了工具提示和掠過(guò)式幫助。 CFrameWindowImplBase類(lèi)有兩個(gè)消息相應函數用來(lái)實(shí)現這些功能,OnMenuSelect()處理WM_MENUSELECT消息,它像MFC那樣查找掠過(guò)式幫助的字符串:首先裝載與菜單資源ID相同的字符串資源,在字符串中查找 \n 字符,使用\n之前的內容作為掠過(guò)幫助的內容。OnToolTipTextA() 和 OnToolTipTextW() 函數分別響應 TTN_GETDISPINFOA消息和TTN_GETDISPINFOW消息,提供工具條按鈕的工具提示。這兩個(gè)處理函數和OnMenuSelect()函數一樣裝載相應的字符串,只是使用\n后面的字符串。(邊注:OnMenuSelect()和OnToolTipTextA()函數對于DBCS字符是不安全的,因為它在查找\n字符時(shí)沒(méi)有檢查DBCS字符串的頭部和尾部)下面是工具條及其關(guān)聯(lián)的幫助字符串的例子: 創(chuàng )建不同樣式的工具條 如果你不喜歡在工具條上顯示3D按鈕(盡管從可用性觀(guān)點(diǎn)來(lái)看平面的界面元素是件糟糕的事情),你可以通過(guò)改變CreateSimpleToolBar()函數的參數來(lái)改變工具條的樣式。例如,你可以在CMainFrame::OnCreate()使用如下代碼創(chuàng )建一個(gè)IE風(fēng)格的工具條: CreateSimpleToolBar ( 0, ATL_SIMPLE_TOOLBAR_STYLE | TBSTYLE_FLAT | TBSTYLE_LIST ); 如果你使用向導為你的程序添加了manifest文件,它就會(huì )在Windows XP系統上使用6.0版的通用控件,你不能選擇按鈕的類(lèi)型,工具條會(huì )自動(dòng)使用平面按鈕即使你創(chuàng )建工具條時(shí)沒(méi)有添加TBSTYLE_FLAT風(fēng)格。 工具條編輯器 正如我們前面所見(jiàn),向導為我們的程序創(chuàng )建了幾個(gè)默認的按鈕,當然只有About按鈕有事件處理。你可以像在MFC的工程中一樣使用工具條編輯器修改工具條資源,CreateSimpleToolBarCtrl()用這個(gè)工具條資源創(chuàng )建工具條。下面是向導生成的工具條在編輯器中的樣子: 對于我們的時(shí)鐘程序,我們添加四個(gè)按鈕,兩個(gè)按鈕用來(lái)改變視圖窗口的顏色,另外兩個(gè)用來(lái)顯示/隱藏工具條和狀態(tài)條。下面是我們的新工具條: 這些按鈕是: - IDC_CP_COLORS: 將視圖窗口顏色改為CodeProject網(wǎng)站的顏色
- IDC_BW_COLORS: 將視圖窗口顏色改為黑白顏色
- ID_VIEW_STATUS_BAR: 顯示或隱藏狀態(tài)條
- ID_VIEW_TOOLBAR: 顯示或隱藏工具條
前兩個(gè)按鈕都有相應的菜單項,它們都調用視圖類(lèi)的一個(gè)新函數SetColor(),向這個(gè)函數傳遞前景顏色和背景顏色,視圖窗口用這兩個(gè)參數改變窗口的顯示。響應這兩個(gè)按鈕的處理函數與響應相應的菜單項的處理函數在使用COMMAND_ID_HANDLER_EX宏上沒(méi)有區別,你可以查看例子工程的代碼了解這些消息處理的細節。在下一節我將介紹狀態(tài)條和工具條按鈕的UI狀態(tài)更新,使它們能夠反映工具條或狀態(tài)條當前的狀態(tài)。 工具條按鈕的UI狀態(tài)更新 向導生成的代碼已經(jīng)為CMainFrame添加了對View|Toolbar和View|Status Bar兩個(gè)菜單項的Check和Uncheck的UI更新處理。這和第二章的程序一樣:對CMainFrame類(lèi)的兩個(gè)命令使用UI更新的宏: BEGIN_UPDATE_UI_MAP(CMainFrame) UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP) UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)END_UPDATE_UI_MAP() 我們的時(shí)鐘程序的工具條按鈕與對應的菜單項有相同的ID,所以第一步就是為每個(gè)宏添加UPDUI_TOOLBAR標志: 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() 還需要添加兩個(gè)函數響應工具條按鈕的更新,但幸運的是向導已經(jīng)為我們做了,所以如果此時(shí)編譯這個(gè)程序,菜單項和工具條按鈕都會(huì )更新。 使一個(gè)工具條支持UI狀態(tài)更新 如果查看CMainFrame::OnCreate()的代碼你就會(huì )發(fā)現一段新的代碼,這段代碼設置了兩個(gè)菜單項的初始狀態(tài): LRESULT CMainFrame::OnCreate( ... ){// ... m_hWndClient = m_view.Create(...); UIAddToolBar(m_hWndToolBar); UISetCheck(ID_VIEW_TOOLBAR, 1); UISetCheck(ID_VIEW_STATUS_BAR, 1);// ...}UIAddToolBar()將工具條的窗口句柄傳給CUpdateUI,所以當需要更新按鈕的狀態(tài)時(shí)CUpdateUI會(huì )向這個(gè)窗口發(fā)消息。另一個(gè)重要的調用位于OnIdle()中: BOOL CMainFrame::OnIdle(){ UIUpdateToolBar(); return FALSE;}當消息隊列中沒(méi)有消息等待時(shí)CMessageLoop::Run()就會(huì )調用OnIdle(),UIUpdateToolBar()遍歷UI更新表,尋找那些帶有UPDUI_TOOLBAR標志又被UISetCheck()之類(lèi)的函數改變了狀態(tài)的的界面元素(當然是工具條),相應的改變按鈕的狀態(tài)。需要注意得是如果更新彈出式菜單的狀態(tài)就不需要做以上兩步,因為CUpdateUI響應WM_INITMENUPOPUP消息,只有接到此消息時(shí)才更新菜單狀態(tài)。 如果查看例子代碼就會(huì )發(fā)現它也演示了如何更新框架窗口的菜單條上的頂級菜單項的狀態(tài)。有一個(gè)菜單項是執行Start和Stop命令,起到開(kāi)始和停止時(shí)鐘的作用,當然這需要做一些不平常的事情:菜單條上的菜單項總是處于彈出狀態(tài)。為了完整的介紹CUpdateUI我將它們也加進(jìn)例子代碼中,要了解它們可以查找對UIAddMenuBar()和UIUpdateMenuBar()兩個(gè)函數的調用。 使用Rebar代替簡(jiǎn)單的工具條 CFrameWindowImpl也支持使用Rebar控件,使你的程序看起來(lái)像IE,使用Rebar也是在程序中使用多個(gè)工具條的一個(gè)方法(譯者加:前面講過(guò),另一個(gè)方法就是修改WTL的源代碼)。要使用Rebar需要在向導的第二頁(yè)選上支持Rebar的檢查框,如下所示: 第二個(gè)例子工程WTLClock3就使用了Rebar控件,如果你正在跟著(zhù)例子代碼學(xué)習,那現在就打開(kāi)WTLClock3。 你首先會(huì )注意到創(chuàng )建工具條的代碼有些不同,出現這種感覺(jué)是因為我們在程序中使用了rebar。以下是相關(guān)的代碼: LRESULT CMainFrame::OnCreate(...){ HWND hWndToolBar = CreateSimpleToolBarCtrl ( m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE ); CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE); AddSimpleReBarBand(hWndToolBar);// ...}代碼從創(chuàng )建工具條開(kāi)始,只是使用了不同的風(fēng)格,也就是ATL_SIMPLE_TOOLBAR_PANE_STYLE,它定義在atlframe.h文件中,與ATL_SIMPLE_TOOLBAR_STYLE風(fēng)格相似,只是附加了一些諸如CCS_NOPARENTALIGN之類(lèi)的風(fēng)格,這是使工具條作為Rebar的子窗口能夠正常工作所必需的風(fēng)格。 下一行代碼是調用CreateSimpleReBar()函數,該函數創(chuàng )建Rebar控件并將句柄存到m_hWndToolBar中。接下來(lái)調用AddSimpleReBarBand()函數為Rebar創(chuàng )建一個(gè)條位并告訴Rebar這個(gè)條位上是一個(gè)工具條。
CMainFrame::OnViewToolBar()函數也有些不同,它只隱藏Rebar上工具條所在的條位而不是隱藏m_hWndToolBar(如果隱藏m_hWndToolBar將隱藏整個(gè)Rebar而不僅僅是工具條)。 如果你使用多個(gè)工具條,只需像向導為我們生成的關(guān)于第一個(gè)工具條的代碼那在OnCreate()創(chuàng )建它們并調用AddSimpleReBarBand()添加到Rebar就行了。CFrameWindowImpl使用標準的Rebar控件,不像MFC那樣支持可??康墓ぞ邨l,你所能作得就是排列這些工具條在Rebar中的位置。 多窗格的狀態(tài)條 WTL另有一個(gè)狀態(tài)條類(lèi)實(shí)現多窗格的狀態(tài)條,與MFC的默認的狀態(tài)條一樣有CAPS,LOCK和NUM LOCK指示器,這個(gè)類(lèi)就是CMultiPaneStatusBarCtrl,在WTLClock3例子工程中演示了如何使用這個(gè)類(lèi)。這個(gè)類(lèi)支持有限的UI更新,當彈出式菜單被顯示時(shí)有“Default”屬性的窗格會(huì )延伸到整個(gè)狀態(tài)條的寬度用于顯示菜單的掠過(guò)式幫助。 第一步就是在CMainFrame中聲明一個(gè)CMultiPaneStatusBarCtrl類(lèi)型的成員變量: class CMainFrame : public ...{//...protected: CMultiPaneStatusBarCtrl m_wndStatusBar;};接著(zhù)在OnCreate()中創(chuàng )建狀態(tài)條并這只UI更新: m_hWndStatusBar = m_wndStatusBar.Create ( *this ); UIAddStatusBar ( m_hWndStatusBar ); 就像CreateSimpleStatusBar()函數做得那樣,我們也將狀態(tài)條的句柄存放在m_hWndStatusBar中。 下一步就是調用CMultiPaneStatusBarCtrl::SetPanes()函數建立窗格: BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true); 參數: - pPanes
- 存放窗格ID的數組
- nPanes
- 窗格ID數組中元素的個(gè)數(譯者加:就是窗格數)
- bSetText
- 如果是true,所有的窗格被立即設置文字,這一點(diǎn)將在下面解釋。
窗格ID可以是ID_DEFAULT_PANE,此ID用于創(chuàng )建支持掠過(guò)式幫助的窗格,窗格ID也可以是字符串資源ID。對于非默認的窗格WTL裝載這個(gè)ID對應的字符串并計算寬度,并將窗格設置為相應的寬度,這和MFC使用的邏輯是一樣的。 bSetText控制著(zhù)窗格是否立即顯示相關(guān)的字符串,如果是true,SetPanes()顯示每個(gè)窗格的字符串,否則窗格就被置空。 下面是我們對SetPanes()的調用: // Create the status bar panes.int anPanes[] = { ID_DEFAULT_PANE, IDPANE_STATUS, IDPANE_CAPS_INDICATOR }; m_wndStatusBar.SetPanes ( anPanes, 3, false );IDPANE_STATUS對應的字符串是“@@@@”,這樣應該有足夠的寬度(希望是)顯示兩個(gè)時(shí)鐘狀態(tài)字符串“Running”和“Stopped”。和MFC一樣,你需要自己估算窗格的寬度,IDPANE_CAPS_INDICATOR對應的字符串是“CAPS”。 窗格的UI狀態(tài)更新 為了更新窗格上的文本,我們需要將相應的窗格添加到UI更新表: BEGIN_UPDATE_UI_MAP(CMainFrame) //... UPDATE_ELEMENT(1, UPDUI_STATUSBAR) // clock status UPDATE_ELEMENT(2, UPDUI_STATUSBAR) // CAPS indicator END_UPDATE_UI_MAP() 這個(gè)宏的第一個(gè)參數是窗格的索引而不是ID,這很不幸,因為如果你重新排列了窗格,你要記得更新UI更新表。 由于我們在調用SetPanes()是第三個(gè)參數是false,所以窗格初始是空的。我們下一步要做得就是將時(shí)鐘狀態(tài)窗格的初始文本設為“Running” // Set the initial text for the clock status pane. UISetText ( 1, _T("Running") );和前面一樣,第一個(gè)參數是窗格的索引。UISetText()是狀態(tài)條唯一支持的UI更新函數。 最后,在CMainFrame::OnIdle()中添加對UIUpdateStatusBar()函數的調用,使狀態(tài)條的窗格能夠在空閑時(shí)間被更新: BOOL CMainFrame::OnIdle(){ UIUpdateToolBar(); UIUpdateStatusBar(); return FALSE;}當你使用UIUpdateStatusBar()時(shí)CUpdateUI的一個(gè)問(wèn)題就暴露出來(lái)了--菜單項的文本在調用UISetText()后沒(méi)有改變!如果你在看WTLClock3工程的代碼,時(shí)鐘的開(kāi)始/停止菜單項被移到了Clock菜單,在菜單項命令的響應處理函數中設置菜單項的文本。無(wú)論如何,如果當前調用的是UIUpdateStatusBar(),那么對UISetText()的調用就不會(huì )起作用。我沒(méi)有研究這個(gè)問(wèn)題是否可以被修復,所以如果你打算改變菜單的文本,你需要留意這個(gè)地方。 最后,我們需要檢查CAPS LOCK鍵的狀態(tài),更新相應的兩個(gè)窗格。這些代碼是通過(guò)OnIdle()被調用的,所以程序會(huì )在每次空閑時(shí)間檢查它們的狀態(tài)。 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;}第一次調用UISetText()時(shí)將從字符串資源中裝載“CAPS”字符串,但是在CString的構造函數中使用了一個(gè)機靈的竅門(mén)(有充分的文檔說(shuō)明)。 在完成所有的代碼之后,狀態(tài)條看起來(lái)是這個(gè)樣子: 承上啟下:有關(guān)對話(huà)框的話(huà)題
|