• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            道。道。道

            安全特性不等于安全的特性

               :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理

            常用鏈接

            搜索

            •  

            最新評(píng)論

            緒論

            ???? WTL 最終來(lái)了 , 而且 提供了我所希望的功能 . 我在 WTL Bytesize (譯文)的文章列出 WTL 主要特征 . 在本文中 , 我將描述一下 WTL 的體系結(jié)構(gòu) , 同時(shí)我會(huì)給出一些簡(jiǎn)單的例子來(lái)演示如何使用它的那些特征 . 希望能夠?qū)δ?span lang="zh-cn">所幫助 . ?

            WTL 應(yīng)用程序的類(lèi)型

            ???? WTL 有好幾種應(yīng)用程序類(lèi)型 , 供您在 AppWizard 選取 .



            ???
            下表對(duì)這些應(yīng)用程序進(jìn)行了描述
            . 這種彈性構(gòu)成了 WTL 體系結(jié)構(gòu)的一部分 .

            應(yīng)用程序類(lèi)型 描述
            SDI Application 單文本界面 只有一個(gè)窗口
            Multiple Threads SDI 單個(gè)進(jìn)程擁有一個(gè)或多個(gè)窗口
            MDI Application 多文本界面 在框架內(nèi) , 您可以有零個(gè)或多個(gè)子窗口
            Dialog Based 基于對(duì)話(huà)框模版

            ??? 你可能還是首次聽(tīng)說(shuō)多線程SDI應(yīng)用程序,但是不用擔(dān)心,它的概念很容易理解.一個(gè)多線程SDI程序啟動(dòng)后它會(huì)有一個(gè)窗口, 窗口顯示了一個(gè)文檔. 當(dāng)你想要程序要再創(chuàng)建一個(gè)文檔時(shí),問(wèn)題就出現(xiàn)了--SDI程序只能顯示一個(gè)文檔.為了解決這個(gè)問(wèn)題,多線程SDI創(chuàng)建了另一個(gè)SDI窗口.看起來(lái)是一個(gè)新的實(shí)例在運(yùn)行,實(shí)際上它不過(guò)是原來(lái)的進(jìn)程創(chuàng)建了一個(gè)新的窗口,并把它依附到進(jìn)程的一個(gè)新線程. IE的新建窗口就是這樣做的. ?

            ??? 除了多線程SDI,所有這些應(yīng)用程序都可以作為COM服務(wù)器, 并且應(yīng)用程序向?qū)?AppWizard)為此提供了一個(gè)選項(xiàng).另外應(yīng)用程序向?qū)н€可以讓你指定該程序是否主持ActiveX控件.令人費(fèi)解的是,不同的程序類(lèi)型,選取"Host ActiveX Controls"的地方不同.除對(duì)話(huà)框應(yīng)用程序外的其他類(lèi)型在第一頁(yè)上選取,而對(duì)話(huà)框類(lèi)型卻放到第二頁(yè). ?

            ??? 第二頁(yè)的其他選項(xiàng),對(duì)對(duì)話(huà)框程序以外的類(lèi)型都是可用的.它們讓你指定程序是否需要工具條(toolbar),狀態(tài)條(status bar)和視窗口(View Window).



            ??? 如果選取了"Toolbar"選項(xiàng),你可以通過(guò)"Rebar"選擇是否將工具條放入IE Rebar控件中. 如果你選取了Rebar, 你就可以通過(guò)框架窗口(frame window)的成員m_hWndToolBar(后邊會(huì)有詳細(xì)的描述)來(lái)訪問(wèn)它.你可以按照你的意愿,在里邊加入其他的工具條. 選取了"Rebar"后, 你可以決定是否選取"Command Bar".
            Command bar很像CE的command bar控件.只是WTL是用一個(gè)類(lèi)來(lái)實(shí)現(xiàn),而在CE, command bar是一個(gè)系統(tǒng)窗口類(lèi)(system window class). Command bar非常有用,它能夠把窗口也加入到工具條中去. 如果你選取了這個(gè)選項(xiàng), 工具條和菜單都將被當(dāng)做toolbar來(lái)實(shí)現(xiàn).這使菜單項(xiàng)也可以有關(guān)聯(lián)的圖標(biāo),并且當(dāng)你移動(dòng)鼠標(biāo)到一個(gè)菜單項(xiàng)上時(shí),該菜單項(xiàng)會(huì)被置成高亮.從Office 97以來(lái), Office軟件的菜單都具有上述特征. ?

            ??? 第二頁(yè)還有指定程序是否使用視的選項(xiàng)(多半你想要使用), 同時(shí)你可以決定這些視如何實(shí)現(xiàn). 下表列出了所有可選的視. ?

            描述
            Generic Window 一個(gè)簡(jiǎn)單的窗口 . 此類(lèi)窗口允許程序員編寫(xiě) WM_PAINT 消息的處理函數(shù) . 適用于需要直接進(jìn)行 paint 的文檔 .
            Form 這類(lèi)視具有一個(gè)對(duì)話(huà)框模版 . 適用于帶 ActiveX 控件的窗口 . 應(yīng)用程序來(lái)操作這些控件 .
            List Box 這個(gè)視是個(gè) list box. 它最簡(jiǎn)單的形式意味著可以通過(guò)調(diào)用 AddString() 方法來(lái)添加字符串 .
            Edit 這個(gè)視是個(gè) edit control. 本質(zhì)上 , 它提供了一個(gè)像 Notepad 一樣的程序 .
            List View 這個(gè)視是個(gè) list view 通用控件 . 用這個(gè)控件來(lái)顯示相關(guān)的項(xiàng) ( 比如 , 控制面板是一個(gè) Explorer 主持的 List View, 所有的項(xiàng)都是控制面板 applet).
            Tree View 這個(gè)視是個(gè) tree view 通用控件 . 這個(gè)適用于具有層次關(guān)系的數(shù)據(jù) , 比如 , 可以用它來(lái)顯示數(shù)據(jù)庫(kù)的 schema. 頂層分支為表和存儲(chǔ)過(guò)程 , 次級(jí)的分支為表中的字段 .
            Rich Edit 這個(gè)視是個(gè) rich edit 控件 , WordPad.
            HTML Page 這個(gè)視主持了一個(gè) IE Web Browser 控件 . 它把主持的一個(gè) web page 當(dāng)成一個(gè)視 .

            ??? 本文的例子需要一個(gè)對(duì)話(huà)框模版 , 同時(shí)還需要菜單 , 因此 Form view 是個(gè)理想的選擇 .

            程序線程

            ??? 跟ATL一樣,WTL程序也需要一個(gè)_Module全局變量來(lái)保存全局?jǐn)?shù)據(jù),方便應(yīng)用級(jí)代碼訪問(wèn).在WTL中,這個(gè)變量是CAppModuleCServerAppModule的實(shí)例,后者在程序同時(shí)作為一個(gè)COM服務(wù)器時(shí)用到.每個(gè)應(yīng)用程序具有一個(gè)或者多個(gè)UI線程.WTL使用兩種方式來(lái)管理這些線程.

            ??? 如果應(yīng)用程序只有一個(gè)UI線程(除了多線程SDI以外,其他程序類(lèi)型默認(rèn)只有一個(gè)UI線程),線程調(diào)用全局函數(shù)run():

            int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
            {
            ??? CMessageLoop theLoop;
            ??? _Module.AddMessageLoop(&theLoop);
            ??? CMainFrame wndMain;
            ??? if (wndMain.CreateEx() == NULL)
            ??? {
            ??????? ATLTRACE(_T("Main window creation failed!\n"));
            ??????? return 0;
            ??? }
            ??? wndMain.ShowWindow(nCmdShow);
            ??? int nRet = theLoop.Run();
            ??? _Module.RemoveMessageLoop();
            ??? return nRet;
            }

            ??? 線程的消息循環(huán)包含在CMessageLoop內(nèi)部.函數(shù)創(chuàng)建了一個(gè)CMessageLoop實(shí)例, 把它放入全局的消息循環(huán)映射(message loop map)數(shù)組. 以線程ID為索引,線程中運(yùn)行的其他的代碼可以訪問(wèn)到這個(gè)實(shí)例. 消息循環(huán)對(duì)象包含了message filter和idle handler. 運(yùn)行在這個(gè)UI線程的UI元件(UI element)可以有它自己的idle handler,在線程的消息隊(duì)列為空時(shí)運(yùn)行譯注:通過(guò)CMessageLoop::AddIdleHandler()把這個(gè)UI元件加入到CMessageLoop的idle handler 數(shù)組中. CMessageLoop::Run()包含了UI線程的主消息映射(main message map).下邊是它的偽代碼:

            MSG m_msg;
            int CMessageLoop::Run()
            {
            ??? for (;;)
            ??? {
            ??????? while (!::PeekMessage(&m_msg, NULL, 0, 0, PM_NOREMOVE))
            ??????????? DoIdleHandlers();
            ??????? bRet = ::GetMessage(&m_msg, NULL, 0, 0);
            ??????? if(bRet == -1)
            ??????????? continue;
            ??????? else if(!bRet)
            ??????????? break;
            ??????? if (!DoMessageFilters(&m_msg))
            ??????? {
            ??????????? ::TranslateMessage(&m_msg);
            ??????????? ::DispatchMessage(&m_msg);
            ??????? }
            ??? }
            ??? return (int)m_msg.wParam;
            }


            ??? 可以看到,這個(gè)函數(shù)推動(dòng)著消息隊(duì)列. 沒(méi)有消息時(shí), 運(yùn)行注冊(cè)到線程的idle hander. 如果在隊(duì)列中檢測(cè)到消息,把它取出來(lái),傳給每個(gè)message filter. 如果消息沒(méi)有被這些函數(shù)處理,它將按照通常的方式,發(fā)送到目標(biāo)窗口.

            ??? 如果程序有超過(guò)一個(gè)的UI線程,可以用WTL的線程管理器,多線程SDI就是這樣做的. 主線程作為一個(gè)管理者線程,它會(huì)為每個(gè)新窗口創(chuàng)建一個(gè)新的線程. 主要流程如下:

            int nRet = m_dwCount;
            DWORD dwRet;
            while(m_dwCount > 0)
            {
            ??? dwRet = ::MsgWaitForMultipleObjects(m_dwCount, m_arrThreadHandles,
            ??????? FALSE, INFINITE, QS_ALLINPUT);
            ??? if(dwRet >= WAIT_OBJECT_0 && dwRet <= (WAIT_OBJECT_0 + m_dwCount - 1))
            ??????? RemoveThread(dwRet - WAIT_OBJECT_0);
            ??? else if(dwRet == (WAIT_OBJECT_0 + m_dwCount))
            ??? {
            ??????? ::GetMessage(&msg, NULL, 0, 0);
            ??????? if(msg.message == WM_USER)
            ??????????? AddThread(_T(""), SW_SHOWNORMAL);
            ??? }
            }


            那些線程句柄放在一個(gè)數(shù)組中. 線程通過(guò)AddThread()加入到數(shù)組(同時(shí)啟動(dòng)線程), RemoveThread()從數(shù)組移走. wait語(yǔ)句在兩種情況下會(huì)被打斷: 線程死亡(將線程從數(shù)組中移出) 或線程收到了WM_USER消息(一個(gè)線程在一個(gè)新線程里新建了一個(gè)窗口). 線程管理者為程序中的一個(gè)類(lèi),因此可以在循環(huán)中加入自己的message handler, 比如,當(dāng)程序有不止一種窗口類(lèi)型時(shí). 創(chuàng)建一個(gè)新的窗口非常簡(jiǎn)單,只需在任意一個(gè)窗口中調(diào)用:

            ::PostThreadMessage(_Module.m_dwMainThreadID, WM_USER, 0, 0L);

            這個(gè)循環(huán)會(huì)一直運(yùn)行下去,直到所有的UI線程都關(guān)閉了. UI線程具有一個(gè)thread procedure,它跟單UI線程的Run()方法一樣.不過(guò),由于線程管理者使用了MsgWaitForMultipleObjects(), 這意味者最多只能有MAXIMUM_WAIT_OBJECTS-1個(gè)UI線程,這也意味著最多只能創(chuàng)建63個(gè)窗口.

            框架

            ??? WTL實(shí)際上是兩類(lèi)窗口: 框架窗口和視圖窗口. 正如名字所暗示的那樣, 框架窗口為窗口提供標(biāo)題欄(caption bar)和邊框,你的代碼用它來(lái)處理工具條(tool bar)和菜單項(xiàng)命令.你看到的程序窗口實(shí)際上是視圖窗口, 視圖覆蓋了框架窗口的客戶(hù)區(qū).客戶(hù)區(qū)是指框架窗口沒(méi)有被諸如狀態(tài)條,工具條之類(lèi)的修飾部件所遮擋的部分.

            ??? 線程會(huì)創(chuàng)建主框架窗口的一個(gè)實(shí)例,創(chuàng)建視圖的工作由主框架窗口的WM_CREATE消息處理函數(shù)完成. 對(duì)于SDI程序來(lái)說(shuō),這個(gè)過(guò)程很簡(jiǎn)單. 把視圖類(lèi)的一個(gè)實(shí)例作為主框架類(lèi)的一個(gè)成員,調(diào)用視圖類(lèi)的Create()方法即可.MDI程序稍微有些不同, MDI主框架窗口通過(guò)CMDIFrameWindowImpl<>::CreateMDIClient()建立一個(gè)名為MDICLIENT的窗口. 這個(gè)客戶(hù)窗口將CMDIChildWindowImpl<>窗口當(dāng)做它的子窗口,子窗口有一個(gè)視圖.這也反映了這么一個(gè)事實(shí),MDI程序可以具有零個(gè)或者多個(gè)子窗口,每個(gè)都有邊框和標(biāo)題欄.

            框架窗口的OnCreate()很有意思,讓我看看:

            LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
            {
            ??? // create command bar window
            ??? HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault,
            ??????? NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
            ??? // attach menu
            ??? m_CmdBar.AttachMenu(GetMenu());
            ??? // load command bar images
            ??? m_CmdBar.LoadImages(IDR_MAINFRAME);
            ??? // remove old menu
            ??? SetMenu(NULL);
            ??? HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME,
            ??????? FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);
            ??? CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
            ??? AddSimpleReBarBand(hWndCmdBar);
            ??? AddSimpleReBarBand(hWndToolBar, NULL, TRUE);
            ??? CreateSimpleStatusBar();
            ??? m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL,
            ??????? WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
            ??????? WS_EX_CLIENTEDGE);
            ??? UIAddToolBar(hWndToolBar);
            ??? UISetCheck(ID_VIEW_TOOLBAR, 1);
            ??? UISetCheck(ID_VIEW_STATUS_BAR, 1);
            ??? CMessageLoop* pLoop = _Module.GetMessageLoop();
            ??? pLoop->AddMessageFilter(this);
            ??? pLoop->AddIdleHandler(this);
            ??? return 0;
            }

            ??? 這是從一個(gè)SDI程序拿來(lái)的一段代碼,該程序有一個(gè)基于command bar的工具條和一個(gè)狀態(tài)條. 函數(shù)的第一行創(chuàng)建了一個(gè)command bar實(shí)例,然后對(duì)它進(jìn)行初始化,在其中加入框架窗口的菜單和工具條位圖. 這段代碼先將菜單取出,把所有的下拉菜單轉(zhuǎn)換為工具條按鈕,并將菜單保存在一個(gè)變量中,以備后用. 給人的感覺(jué)是菜單是由工具條實(shí)現(xiàn)的-那我們就把它叫做工具條菜單(menu toolbar)吧. 然后Command Bar將程序工具條的圖標(biāo)裝入image list 并將它們的ID保存在數(shù)組中. 當(dāng)點(diǎn)擊工具條菜單的按鈕時(shí),commandbar會(huì)找到對(duì)應(yīng)的子菜單,創(chuàng)建一個(gè)彈出菜單. Command bar將子菜單項(xiàng)的ID和它保存的ID進(jìn)行比較,這些ID跟image list中的工具條按鈕圖標(biāo)是相關(guān)聯(lián)的. 如果比較成功, 則將關(guān)聯(lián)的圖標(biāo)加到菜單項(xiàng)上去. 這意味著相同ID的菜單項(xiàng)和工具條按鈕具有相同的圖標(biāo).

            接下來(lái), 創(chuàng)建工具條并把它關(guān)聯(lián)到commandbar, 然后創(chuàng)建狀態(tài)條和視圖.可以看到視圖的HWND存放在框架窗口的m_hWndClient變量中. 這個(gè)窗口句柄在框架窗口的WM_SIZE handler中會(huì)用到.當(dāng)框架窗口改變大小時(shí),它告知視圖改變自身,于此同時(shí)也要考慮狀態(tài)條和command bar.

            在下來(lái)的三行(從調(diào)用UIAddToolBar()開(kāi)始) 用來(lái)顯示在運(yùn)行時(shí)會(huì)改變狀態(tài)的UI項(xiàng)(UI item).文章后面還會(huì)重提這個(gè)話(huà)題. 最后,訪問(wèn)消息循環(huán)(message loop), 你應(yīng)該還記得該消息循環(huán)存放在一全局?jǐn)?shù)組中.GetMessageLoop() 取得當(dāng)前線程的消息循環(huán),加入框架窗口的message filter和idle handler, 分別默認(rèn)是PreTranslateMessage()OnIdle().

            框架窗口繼承于以下類(lèi):

            class CMainFrame :
            ??? public CFrameWindowImpl<CMainFrame>,
            ??? public CUpdateUI<CMainFrame>,
            ??? public CMessageFilter,
            ??? public CIdleHandler


            后兩個(gè)抽象類(lèi)宣稱(chēng)了框架窗口類(lèi)實(shí)現(xiàn)了PreTranslateMessage()OnIdle(). 從CUpdateUI<>繼承表示框架類(lèi)支持UI update map.

            視圖

            視圖窗口看起來(lái)顯得很簡(jiǎn)單:

            class CMyView : public CWindowImpl<CMyView>
            {
            public:
            ??? DECLARE_WND_CLASS(NULL)
            ??? BOOL PreTranslateMessage(MSG* pMsg)
            ??? {
            ??? ??? pMsg;
            ??????? return FALSE;
            ??? }
            ??? BEGIN_MSG_MAP(CMyView)
            ??????? MESSAGE_HANDLER(WM_PAINT, OnPaint)
            ??? END_MSG_MAP()
            ??? LRESULT OnPaint(UINT, WPARAM, LPARAM, BOOL&)
            ??? {
            ??????? CPaintDC dc(m_hWnd);
            ??????? //TODO: Add your drawing code here
            ??????? return 0;
            ??? }
            };


            上面一個(gè)SDI程序的視圖類(lèi). 多線程SDI和MDI的視圖類(lèi)在本質(zhì)上也跟這個(gè)一樣,但他們沒(méi)有PreTranslateMessage()方法. SDI程序就是使用這個(gè)函數(shù),趕在框架類(lèi)處理消息之前把消息抓住. PreTranslateMessage()在SDI的框架類(lèi)中的實(shí)現(xiàn)是直接消息轉(zhuǎn)發(fā)給視圖類(lèi).

            這里顯示的視圖實(shí)際上沒(méi)有做什么工作.你應(yīng)該自己OnPaint()函數(shù)中加入畫(huà)出文檔內(nèi)容的代碼.如果要支持輸入,如鼠標(biāo)的點(diǎn)擊和鍵盤(pán)的按鍵,你應(yīng)該加入相應(yīng)消息處理函數(shù)到類(lèi)和映射中. 可以看這個(gè)窗口是從CWindowImpl<>繼承下來(lái)的,如果你想讓它基于一個(gè)Win32控件的話(huà),就應(yīng)該從定義在AtlCtrls.h文件某個(gè)WTL類(lèi)繼承.

            如果想在基于CWindowImpl<>的類(lèi)里加上滾動(dòng)條,那么你應(yīng)該把基類(lèi)換成CScrollWindowImpl<>,同時(shí)把消息鏈給它:

            class CMyView : public CScrollWindowImpl<CMyView>
            {
            public:
            ??? typedef CScrollWindowImpl<CMyView> parent;
            ??? BEGIN_MSG_MAP(CMyView)
            ??????? CHAIN_MSG_MAP(parent)
            ??? END_MSG_MAP()
            ??? void DoPaint(CDCHandle dc)
            ??? {
            ??? }
            }

            基類(lèi)保證窗口具滾動(dòng)條,并提供滾動(dòng)條消息的默認(rèn)處理.視圖類(lèi)不再有WM_PAINT的處理函數(shù),因?yàn)樗?span lang="zh-cn">已被CScrollWindowImpl<>處理.根據(jù)滾動(dòng)條的位置,CScrollWindowImpl<>畫(huà)出視圖相對(duì)應(yīng)的部分. 取而代之在你的類(lèi)里實(shí)現(xiàn)DoPaint(),在這里你需要畫(huà)出個(gè)視圖.如果你指定滾動(dòng)的范圍,大小或起點(diǎn),你需要加上處理WM_CREATE消息的函數(shù),把這些初始化代碼放到里邊.

            正如我先前提到的,框架窗口會(huì)改變視圖窗口的大小,以使它客戶(hù)區(qū)未被狀態(tài)條和工具條覆蓋的部分為視圖所填充. 在大多數(shù)情況下,這樣就夠了.但是當(dāng)你想要一個(gè)具有Windows Explorer樣子的程序時(shí),該怎么辦呢? Windows Explorer的窗口包含了一個(gè)tree view 和一個(gè)list view,還有兩者之間的分割條. WTL的解決方案很簡(jiǎn)單:使用splitter窗口!

            為此你需要改變一下框架窗口,讓它創(chuàng)建splitter窗口的一個(gè)實(shí)例作為它的視圖. 例如, 在你的框架類(lèi)里有如下的數(shù)據(jù)成員:

            CSplitterWindow m_view;
            CTreeViewCtrl m_tree;
            CListViewCtrl m_list;


            你可以在OnCreate()創(chuàng)建一個(gè)splitter窗口:

            // get the frame client rect, so that we set the splitter initial size
            // and we can get the splitter bar in the centre

            RECT rect;
            GetClientRect(&rect);
            m_hWndClient = m_view.Create(m_hWnd, rect,
            ??? NULL, WS_CHILD | WS_VISIBLE);
            m_tree.Create(m_view, rcDefault, NULL,
            ??? WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT,
            ??? WS_EX_CLIENTEDGE);
            m_list.Create(m_view, rcDefault,
            ??? NULL, WS_CHILD | WS_VISIBLE | LVS_REPORT, WS_EX_CLIENTEDGE);
            m_view.SetSplitterPanes(m_tree, m_list);
            m_view.SetSplitterPos();


            Splitter窗口如同一個(gè)視圖,將框架窗口作為它的父窗口. 在這段代碼里,我將框架窗口客戶(hù)區(qū)的實(shí)際大小傳給splitter窗口. 我可以在這里使用 rcDefault,因?yàn)橐坏┛蚣艽翱?span lang="zh-cn">創(chuàng)建完成,框架窗口會(huì)轉(zhuǎn)發(fā)WM_SIZE消息給splitter. 這樣splitter可以馬上改變自身的大小來(lái)填充框架. 然而,當(dāng)我準(zhǔn)備使用不參數(shù)的SetSplitterPos(),把分割條設(shè)置于窗口中線時(shí),出現(xiàn)了問(wèn)題.Splitter窗口使用它的大小來(lái)決定中線的位置,由于rcDefault告訴窗口它的大小是0(因此中線的位置也是0),從而意味著分割條將出現(xiàn)在z最左邊,左窗口隱藏了起來(lái).

            創(chuàng)建了splitter窗口后,你需要?jiǎng)?chuàng)建那些你想要分割的窗口.它們將作為splitter窗口的窗口被創(chuàng)建.最后你將這些子窗口通過(guò)SetSplitterPanes()加到splitter窗口中去,并確定分割條的位置所在.

            UI Update

            菜單項(xiàng)可以被設(shè)置為有效或無(wú)效,可以check記號(hào)或著像radio按鈕一樣,一組菜單項(xiàng)中同時(shí)有且只有一個(gè)能被check.此外,菜單項(xiàng)還可以帶圖標(biāo)和文字. 所有的這些狀態(tài)都可以在運(yùn)行時(shí)根據(jù)程序中的某個(gè)值進(jìn)行改變.工具條在某種程度上可以看做是菜單的易見(jiàn)形態(tài),因?yàn)樗鼈兊陌粹o可以個(gè)別地,或者作為一組的一部被置成有效或無(wú)效,推入推出. UI update機(jī)制允許你指定哪些UI元件(UI element)的狀態(tài)可以在運(yùn)行時(shí)改變. WTL使用如下的UI update映射來(lái)實(shí)現(xiàn)這一功能:


            BEGIN_UPDATE_UI_MAP(CMainFrame)
            ??? UPDATE_ELEMENT(ID_FILE_SAVERESULTS, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
            ??? UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
            ??? UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
            END_UPDATE_UI_MAP()

            這個(gè)例子指出三個(gè)菜單項(xiàng)在運(yùn)行時(shí)有一個(gè)狀態(tài)需要顯示,其中的一個(gè), ID_FILE_SAVERESULTS,有一個(gè)工具條按鈕跟它相關(guān)聯(lián). WTL通過(guò)建立一個(gè)數(shù)組來(lái)保存這些信息.為此你需要完成兩方面的工作:

            首先是UI元件的狀態(tài). 如果是菜單項(xiàng), 你可以使用UIEnable()使能該菜單項(xiàng), UISetCheck()設(shè)置check記號(hào), UISetText()改變菜單的文字.如果是工具條按鈕,那么你使用UIEnable()使能該按鈕, UISetCheck()或者UISetRadio()決定按鈕是推入還是推出.下邊的代碼根據(jù)是否有文本被選中,來(lái)使能Cut菜單項(xiàng)和工具條按鈕:

            BOOL bSelected = GetSelected();
            UIEnable(ID_EDIT_CUT, bSelected);


            你可以把這樣的代碼放入相應(yīng)處理函數(shù)中(如一個(gè)菜單項(xiàng)的狀態(tài)依賴(lài)于另一個(gè)菜單項(xiàng)的動(dòng)作,將它放入后者的處理函數(shù)中),或者放入OnIdle()方法,通過(guò)檢查某個(gè)類(lèi)變量來(lái)決定元件的狀態(tài).

            其次是確定各個(gè)UI元件是否都被更新了,為此你需要調(diào)用CUpdateUI<>的某個(gè)方法UI元件加入到列表中.主菜單已自動(dòng)加入,但是其他的任何菜單和所有的工具條必須分別通過(guò)調(diào)用UIAddMenuBar()UIAddToolBar()手動(dòng)加入.

            其他還有一堆事情要注意. 首先,設(shè)置了工具條的狀態(tài)后,使用UIUpdateToolBar()以使工具條狀態(tài)更新. 對(duì)于菜單,你不需如此,因?yàn)樽硬藛问莿?dòng)態(tài)生成的.UIUpdateMenuBar()這個(gè)方法也存在,但是它的作用是把菜單恢復(fù)到初始狀態(tài),如果你改變過(guò)某些項(xiàng)的文字,調(diào)用UIUpdateMenuBar()的結(jié)果可能不是你所期望的(因?yàn)椴藛雾?xiàng)的文字會(huì)變成的).

            盡管還有一個(gè)方法UISetRadio(),但是沒(méi)有一個(gè)把幾個(gè)菜單項(xiàng)或者工具條按鈕當(dāng)做radio按鈕組(也就是說(shuō),有一個(gè)而且只有一個(gè)被選中)的機(jī)制.如果你希望得到這樣效果,你必須自己編碼,不過(guò)它并不難.

            對(duì)話(huà)框

            ATL的對(duì)話(huà)框支持一向很好,對(duì)此WTL新增了通用對(duì)話(huà)框的封裝. 本質(zhì)上是為對(duì)話(huà)框加入了輸入驗(yàn)證和回調(diào)函數(shù). 比如, 你想用戶(hù)改變年Open對(duì)話(huà)框中的文件夾時(shí)有所動(dòng)作,那么你應(yīng)該從CFileDialogImpl<>繼承一個(gè)類(lèi),實(shí)現(xiàn)OnFolderChange():

            class CMyFileDialog : public CFileDialogImpl<CMyFileDialog>
            {
            public:
            ??? CMyFileDialog(BOOL b)
            ??????? : CFileDialogImpl<CMyFileDialog>(b) { }
            ??? void OnFolderChange(LPOFNOTIFY lpon)
            ??? {
            ??????? char strFolder[MAX_PATH];
            ??????? if (GetFolderPath(strFolder, sizeof(strFolder)) > 0)
            ??????? {
            ??????????? MessageBox(strFolder);
            ??????? }
            ??? }
            };

            當(dāng)文件夾的路徑改變時(shí),CFileDialogImpl<>調(diào)用OnFolderChange().該函數(shù)使用基類(lèi)的GetFolderPath(),來(lái)取得新路徑.

            控件

            WTL為所有的Win32和通用控件提供了封裝類(lèi),包括Windows 2000新加入的. 雖然只是簡(jiǎn)單的包裝,但是它們使這些控件更加容易訪問(wèn).譬如,你能記清楚從List View讀出當(dāng)前選定項(xiàng)的文字的消息和需要傳的參數(shù)嗎?(實(shí)際上, 你需要發(fā)送兩個(gè)消息, 一個(gè)是得到選定項(xiàng)的索引,另一個(gè)是讀出它的文字.) WTL的作者為你完成了這些煩人的工作, 提供了一個(gè)簡(jiǎn)單的封裝函數(shù)供你使用.

            使用這些控件類(lèi)有兩種方法. 如果你的對(duì)話(huà)框里有一個(gè)控件, 你可以將控件的HWND依附到一個(gè)封裝對(duì)象,使用封裝類(lèi)的方法來(lái)訪問(wèn)控件.這種方法簡(jiǎn)化了你讀寫(xiě)控件數(shù)據(jù)和處理notification消息的代碼.

            另外的用法是把這些類(lèi)加到你的視圖類(lèi)的繼承層次中去:

            class CMyView : public CWindowImpl<CMyView, CListBox>

            這表示CWindowImpl<>CListBox繼承而來(lái),因此創(chuàng)建的窗口將是一個(gè)list box (因?yàn)榇翱陬?lèi)的名字是通過(guò)調(diào)用
            CListBox::GetWndClassName()得到). 另外, ATL的窗口機(jī)制會(huì)子類(lèi)化這個(gè)窗口,將發(fā)給它的消息路由到你的消息映射中去. 它保留了老的窗口函數(shù),這樣,你沒(méi)有處理的消息將由老的窗口函數(shù)來(lái)處理.當(dāng)你的視圖類(lèi)從控件類(lèi)繼承時(shí),WTL就會(huì)使用這一技術(shù).

            在notification消息和子類(lèi)化這個(gè)主題上,有一點(diǎn)很值得指出,那就是當(dāng)事件發(fā)生時(shí),絕大多數(shù)窗口控件都會(huì)發(fā)送notification消息給它們的父窗口.讓你窗口來(lái)處理這些notification消息要比子類(lèi)化一個(gè)已存在控件窗口(或子類(lèi)化一個(gè)已存在的類(lèi),然后建立一個(gè)實(shí)例),從而在控件之前取得消息好得多. 譬如, 你想處理按鈕的click事件,你所需要做的只是處理BN_CLICKEDnotification.它將由按鈕發(fā)送給你的窗口類(lèi).另外的一種方法是從CContainedWindow<>子類(lèi)化BUTTON窗口來(lái)處理click消息.

            之所以說(shuō)這個(gè)是因?yàn)橐粋€(gè)知名的ATL鼓吹者給我一份代碼里就是這么做的.他的代碼取得一個(gè)簡(jiǎn)單的按鈕click事件所花的時(shí)間是別人的3到4倍,因?yàn)樗宇?lèi)化了按鈕控件,而不是簡(jiǎn)單的處理BN_CLICKEDnotification.

            WTL還提供了一些新的控件,在win32中沒(méi)有對(duì)等者. 你已經(jīng)看到過(guò)一個(gè) -- command bar, 實(shí)際上還有其他一些非常有用類(lèi):

            類(lèi) ?? 描述
            CBitmapButton 這是一個(gè)用位圖替代標(biāo)題的按鈕.你可以提供一個(gè)image list,里邊包含按鈕在正常狀態(tài),失效, 推入和鼠標(biāo)落在按鈕上的圖表.
            CHyperLink 讓你建立一個(gè)static控件,它代表一個(gè)hyperlink,這樣當(dāng)用戶(hù)點(diǎn)擊它時(shí),默認(rèn)的web瀏覽器打開(kāi)該鏈接.
            CWaitCursor 這不過(guò)是在它的構(gòu)造函數(shù)中把鼠標(biāo)圖標(biāo)改成等待狀態(tài),而在析構(gòu)函數(shù)中還原.
            CCheckListViewCtrl 在每一項(xiàng)邊上都有一個(gè)check box的list box.
            CMultiPaneStatusBarCtrl 具有多個(gè)pane的狀態(tài)條

            ?

            posted on 2007-01-18 09:49 獨(dú)孤九劍 閱讀(1514) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): C/C++/STL/ATL/WTL
            久久亚洲精品人成综合网| 一本久久a久久精品亚洲| 久久夜色撩人精品国产小说| 亚洲国产精品综合久久网络| 久久久久免费精品国产| 精品久久久久久久久久久久久久久| 色婷婷久久久SWAG精品| 成人久久精品一区二区三区| 日本高清无卡码一区二区久久| 久久人人爽人人爽人人片AV不| 精品久久人人爽天天玩人人妻 | 少妇内射兰兰久久| 久久久久久免费视频| 久久99精品久久久久久不卡| 久久无码av三级| 亚洲乱亚洲乱淫久久| 精品久久久久久国产免费了| 久久精品国产精品青草| 国产精品久久久久影院色| 久久精品国产网红主播| 日产精品99久久久久久| 7777久久亚洲中文字幕| 国产成人无码精品久久久免费| 久久最新精品国产| 亚洲午夜精品久久久久久app| 欧美日韩精品久久免费| 囯产精品久久久久久久久蜜桃| 色欲综合久久躁天天躁蜜桃| 国产麻豆精品久久一二三| 久久亚洲天堂| 高清免费久久午夜精品| 国产欧美久久久精品影院| 人妻久久久一区二区三区| 久久国产成人| 天天综合久久久网| 777午夜精品久久av蜜臀| 久久久久九九精品影院| 97热久久免费频精品99| 久久精品国产亚洲AV不卡| 91久久国产视频| 国产精品欧美亚洲韩国日本久久|