• <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>

            笑看風(fēng)云淡

            寵辱不驚,看庭前花開花落;去留無(wú)意,望天空云卷云舒
            posts - 96, comments - 48, trackbacks - 0, articles - 0
              C++博客 :: 首頁(yè) :: 新隨筆 ::  :: 聚合  :: 管理
            四章 對(duì)話框和控件

            對(duì)于Win32 GUI的程序設(shè)計(jì)來(lái)說(shuō),其實(shí)大部分的情況下我們都不需要自己進(jìn)行窗口類的設(shè)計(jì),而是可以使用Win32中與用戶交互的標(biāo)準(zhǔn)方式——對(duì)話框(Dialog Box)。我們可以在VC IDE的資源設(shè)計(jì)器中設(shè)計(jì)對(duì)話框資源,并在其上放置各種控件資源——的確是非常方便。在本章里,李馬將要向諸位介紹如何利用ATL來(lái)操作對(duì)話框,以及如何操作對(duì)話框上的各種控件。

            題外話先

            ATL,是的,正是由于我所講的是“ATL的GUI程序設(shè)計(jì)”,所以我才可能將內(nèi)容直接經(jīng)由CWindowImpl過(guò)渡到CDialogImpl——而不是過(guò)渡到你先前所熟悉的CFrameWnd和Doc/View體系。況且,即使這之后我深入到了CDialogImpl之中,我也不會(huì)講到你所熟悉的DDX/DDV機(jī)制。再三考慮之下,我還是決定把這些東西在CDialogImpl前一并當(dāng)作題外話說(shuō)出來(lái),先。

            再來(lái)回顧一下ATL的性質(zhì)。它是一個(gè)被設(shè)計(jì)用來(lái)開發(fā)COM組件的Framework,所以對(duì)GUI部分的支持——套用一句2006年的流行語(yǔ)來(lái)說(shuō):那是相~~當(dāng)~~(加重且延長(zhǎng)聲音地)少。于是,它沒(méi)有“框架窗口”這個(gè)概念,更不會(huì)有Doc/View體系。其實(shí)我對(duì)MFC的這一設(shè)計(jì)特點(diǎn)感覺(jué)不錯(cuò),畢竟它可以通過(guò)一個(gè)簡(jiǎn)單的CFrameWnd類來(lái)實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)的SDI/MDI框架,而且其中帶有工具欄、狀態(tài)欄和一個(gè)用來(lái)容納視圖的標(biāo)準(zhǔn)的工作區(qū)域。我們可以通過(guò)控制框架窗口中的View及其相關(guān)的Doc類型來(lái)完成特定文檔類型的讀寫與顯示。——但是,很不幸,這一切都只屬于偉大的MFC;在ATL中,我們什么都沒(méi)有。

            另外,在對(duì)話框的技術(shù)領(lǐng)域中,使用ATL的我們也不會(huì)享有數(shù)據(jù)交換與驗(yàn)證(DDX/DDV)的支持。這一所謂的缺憾我并不想多加評(píng)價(jià),一是因?yàn)槲也⒉涣私釳FC中DDX/DDV的內(nèi)部機(jī)制,二是因?yàn)槲抑庇X(jué)上認(rèn)為這是影響MFC效率的罪魁之一。在MFC中,我們可以通過(guò)向?qū)У闹С州p易地為表單的輸入域加入輸入校驗(yàn)與限制,而且表現(xiàn)在源代碼上的僅僅是幾個(gè)宏而已——我自認(rèn)天下沒(méi)有免費(fèi)的午餐,這幾個(gè)簡(jiǎn)單的宏既然能為我們包辦一切,那我們勢(shì)必會(huì)相應(yīng)地失去些東西,要不然忒便宜了也就。

            題外話的最后不免落入俗套,我將會(huì)向諸位介紹解決以上缺憾的方法。——也許你猜到了,就是從WTL中尋找解決方案。WTL是對(duì)ATL的擴(kuò)展,所以它的很多代碼可以直接拿過(guò)來(lái)用(當(dāng)然可能需要一些小小的修改)。而且,不知道WTL的設(shè)計(jì)者是不是為了拉攏MFC的開發(fā)人員,總之它里面添加了很多與MFC相似的元素,例如以上所說(shuō)的框架窗口和DDX/DDV。

            CDialogImpl

            與ATL窗口類CWindowImpl相對(duì)應(yīng),ATL的對(duì)話框類名為CDialogImpl。它的定義如下:

            template <class T, class TBase = CWindow>
            class ATL_NO_VTABLE CDialogImpl : public CDialogImplBaseT< TBase >
            {
            // ...
            };

            你可以從上面的代碼看到,CDialogImpl與CWindowImpl類似,也經(jīng)歷了一系列的繼承鏈。不過(guò),它較之CWindowImpl的模板參數(shù)要簡(jiǎn)單得多——畢竟是標(biāo)準(zhǔn)對(duì)話框,有些東西是不用操心的。

            CDialogImpl的使用方法大致如下:

            class CYourDlg : public CDialogImpl< CYourDlg >
            {
            public:
            enum { IDD = IDD_YOUR_DLG };
            public:
            BEGIN_MSG_MAP( CYourDlg )
            // 消息映射
            END_MSG_MAP()
            public:
            // 消息響應(yīng)函數(shù)
            ///////////////////
            // 其余的部分...
            };

            和CWindowImpl不一樣,CDialogImpl不需要使用DECLARE_WND_CLASS來(lái)定義窗口類。在原來(lái)DECLARE_WND_CLASS的位置,一個(gè)枚舉代替了原來(lái)窗口類定義的部分。這里的枚舉列表必須有一個(gè)被命名為IDD,并且它的值要被設(shè)置為相應(yīng)的對(duì)話框資源ID。呃……寫到這里,我仿佛已經(jīng)感覺(jué)到了你的不快,但CDialogImpl的實(shí)現(xiàn)即是如此(以CDialogImpl::DoModal為例):

            // from CDialogImpl::DoModal
            return ::DialogBoxParam(_Module.GetResourceInstance(), MAKEINTRESOURCE(T::IDD),
            hWndParent, (DLGPROC)T::StartDialogProc, dwInitParam);

            當(dāng)然,如果你不喜歡這么做的話,也可以自己從CDialogImplBaseT派生出屬于你的對(duì)話框類。

            再回到CDialogImpl的話題上來(lái)。這個(gè)類主要有以下幾個(gè)常用的成員函數(shù):

            成員函數(shù) 說(shuō)明
            DoModal 顯示一個(gè)模態(tài)對(duì)話框
            EndDialog 銷毀一個(gè)模態(tài)對(duì)話框
            Create 創(chuàng)建一個(gè)非模態(tài)對(duì)話框
            DestroyWindow 銷毀一個(gè)非模態(tài)對(duì)話框

            這樣看來(lái)是不是和MFC十分相似?事實(shí)上,如果你已經(jīng)定義好了一個(gè)對(duì)話框類,那么它的使用和MFC的對(duì)話框類的確沒(méi)什么兩樣:

            CYourDlg dlg;
            dlg.DoModal();

            控件的使用

            從與用戶交互的角度來(lái)看,控件是對(duì)話框上必不可少的元素。在Win32 GUI程序設(shè)計(jì)中,對(duì)控件的操作大可歸為兩個(gè)方面:一是對(duì)控件進(jìn)行操作,二是響應(yīng)控件的事件。排除子類化的事件響應(yīng)(后面我會(huì)專門介紹如何在ATL中進(jìn)行控件的子類化),那么這兩方面的具體實(shí)現(xiàn)就是:

            • 使用窗口操作的API函數(shù)或發(fā)送消息來(lái)操作控件。
            • 處理WM_COMMAND或WM_NOTIFY來(lái)響應(yīng)控件的事件。

            根據(jù)順序,李馬來(lái)為大家介紹一下如何對(duì)控件進(jìn)行操作先。這通常可以經(jīng)由CWindow及其派生類實(shí)現(xiàn),以下代碼示范了如何禁用一個(gè)控件:

            CWindow ctrl = GetDlgItem( IDC_CONTROL );
            ctrl.EnableWindow( FALSE );

            如果你要操作的控件需要用到特定的特性(也就是通過(guò)發(fā)送消息來(lái)實(shí)現(xiàn)的特有行為),當(dāng)然你可以通過(guò)使用CWindow::SendMessage來(lái)實(shí)現(xiàn),不過(guò)我并不推薦你使用這種方法,因?yàn)镾endMessage是不會(huì)對(duì)消息參數(shù)進(jìn)行類型檢查的。而且,考慮到代碼的可復(fù)用性,你可以對(duì)CWindow進(jìn)行派生以達(dá)到目的。例如,對(duì)于列表控件的封裝可以是類似下面這個(gè)樣子:

            class CListBox : public CWindow
            {
            public:
            int AddString( LPCTSTR lpszString )
            {
            return ::SendMessage( m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszString );
            }
            };

            然后,這樣進(jìn)行調(diào)用:

            CListBox list;
            list.Attach( GetDlgItem( IDC_LIST ) );
            list.AddString( _T("This is a test line") );

            可能你會(huì)有所疑問(wèn):為什么CWindow的例子直接使用了“=”來(lái)進(jìn)行賦值,而CListBox則要使用Attach來(lái)初始化。當(dāng)然,其實(shí)這兩者并沒(méi)有實(shí)質(zhì)上的區(qū)別,只不過(guò)是CWindow重載了operator=操作符,而CListBox沒(méi)有這樣做罷了(嚴(yán)格說(shuō)來(lái),派生自CWindow的CListBox當(dāng)然繼承了CWindow的operator=,但是它并不能用于CListBox對(duì)象,如果強(qiáng)行使用則會(huì)得到一個(gè)“error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'struct HWND__ *' (or there is no acceptable conversion)”的錯(cuò)誤)。如果你也希望CListBox支持operator=的初始化方式,可以這樣來(lái)對(duì)CListBox進(jìn)行封裝:

            class CListBox : public CWindow
            {
            public:
            CListBox& operator=( HWND hWnd )
            {
            m_hWnd = hWnd;
            return *this;
            }
            public:
            int AddString( LPCTSTR lpszString )
            {
            return ::SendMessage( m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszString );
            }
            };

            下面來(lái)介紹對(duì)控件事件的處理。通常控件在某些事件發(fā)生時(shí)會(huì)以發(fā)送WM_COMMAND(普通控件)或WM_NOTIFY(公共控件)消息的方式通知其父窗口,然后我們?cè)谄涓复翱诘拇翱谶^(guò)程中處理這些消息即可。WM_COMMAND和WM_NOTIFY的參數(shù)意義如下:

              WM_COMMAND WM_NOTIFY
            wParam HIWORD(wParam)為通知消息代碼,LOWORD(wParam)為控件ID 發(fā)生通知消息的控件ID,不過(guò)仍建議使用lParam參數(shù)中的ID
            lParam 發(fā)生通知消息的控件句柄 一個(gè)指向NMHDR結(jié)構(gòu)的指針,這個(gè)結(jié)構(gòu)中包含了通知消息的各種信息

            在ATL中,可以使用如下的宏來(lái)進(jìn)行各種消息的分流(在此將Windows消息分流的宏也一并加上):

            消息分流宏 說(shuō)明
            MESSAGE_HANDLER 用于將某個(gè)特定消息分流至一個(gè)消息處理函數(shù)。
            MESSAGE_RANGE_HANDLER 用于將某個(gè)范圍內(nèi)的消息一并分流至同一個(gè)消息處理函數(shù)。
            COMMAND_HANDLER 用于將來(lái)自特定ID、特定通知碼的WM_COMMAND消息分流至一個(gè)消息處理函數(shù)。
            COMMAND_ID_HANDLER 用于將來(lái)自特定ID的WM_COMMAND消息分流至一個(gè)消息處理函數(shù)。
            COMMAND_CODE_HANDLER 用于將來(lái)自特定通知碼的WM_COMMAND消息分流至一個(gè)消息處理函數(shù)。
            COMMAND_RANGE_HANDLER 用于將來(lái)自某個(gè)ID范圍內(nèi)的WM_COMMAND消息分流至一個(gè)消息處理函數(shù)。
            NOTIFY_HANDLER 用于將來(lái)自特定ID、特定通知碼的WM_NOTIFY消息分流至一個(gè)消息處理函數(shù)。
            NOTIFY_ID_HANDLER 用于將來(lái)自特定ID的WM_NOTIFY消息分流至一個(gè)消息處理函數(shù)。
            NOTIFY_CODE_HANDLER 用于將來(lái)自特定通知碼的WM_NOTIFY消息分流至一個(gè)消息處理函數(shù)。
            NOTIFY_RANGE_HANDLER 用于將來(lái)自某個(gè)ID范圍內(nèi)的WM_NOTIFY消息分流至一個(gè)消息處理函數(shù)。

            另外,處理Windows消息、WM_COMMAND消息、WM_NOTIFY消息的消息處理函數(shù)應(yīng)該分別滿足如下規(guī)格要求:

            // atlwin.h
            // Handler prototypes:
            // LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
            // LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
            // LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);

            李馬牌通訊錄管理系統(tǒng)

            別誤會(huì),這并不是什么正兒八經(jīng)的所謂“信息管理系統(tǒng)”,而只是我為本章寫下的一個(gè)簡(jiǎn)單示例而已。這里面并不涉及數(shù)據(jù)的存儲(chǔ),而只是為演示本章的內(nèi)容而實(shí)現(xiàn)了必要的流程而已。在此李馬并不打算對(duì)這個(gè)程序的代碼進(jìn)行過(guò)多解說(shuō),僅僅點(diǎn)出幾點(diǎn)需要特殊說(shuō)明的。

            1. 由于程序中使用了公共控件ListView,所以在WinMain的開頭需要對(duì)公共控件庫(kù)進(jìn)行初始化:
              // 初始化公共控件先
              INITCOMMONCONTROLSEX init;
              init.dwSize = sizeof( init );
              init.dwICC = ICC_LISTVIEW_CLASSES;
              InitCommonControlsEx( &init );
              在此我有必要指出,對(duì)公共控件庫(kù)的初始化應(yīng)該盡量使用InitCommonControlsEx,即使InitCommonControls貌似更加方便一些。我曾經(jīng)做過(guò)測(cè)試,一個(gè)使用了DateTime控件并由InitCommonControls初始化的應(yīng)用程序在WinXP sp2 + VC 6.0編譯完成后,在Win2K下是不能運(yùn)行的。
            2. CMainDlg::OnRadioSex是為了演示COMMAND_RANGE_HANDLER而寫的一個(gè)消息處理函數(shù),其實(shí)針對(duì)這個(gè)示例并不用編寫之——因?yàn)閃indows系統(tǒng)會(huì)自動(dòng)對(duì)Radio按鈕進(jìn)行檢選狀態(tài)的處理;但如若考慮到多組Radio按鈕存在的情況,CMainDlg::OnRadioSex這樣的處理函數(shù)便會(huì)凸顯出它的用處。
            3. LListView::GetSelectionMark并不能用來(lái)準(zhǔn)確判斷ListView的選中項(xiàng),尤其是在選中項(xiàng)被刪除之后。

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            久久婷婷五月综合97色直播| 久久男人中文字幕资源站| 日韩人妻无码一区二区三区久久| 亚洲欧美国产日韩综合久久 | 久久亚洲国产精品一区二区| 中文字幕一区二区三区久久网站| 久久亚洲高清综合| 无码人妻久久久一区二区三区| 久久最近最新中文字幕大全| 久久精品桃花综合| 99久久婷婷国产一区二区| 亚洲人成无码www久久久| 久久成人精品视频| 欧洲精品久久久av无码电影| 久久久久久国产精品免费免费| 亚洲va久久久噜噜噜久久男同| 国产日韩久久免费影院| 久久久久亚洲AV无码网站| 亚洲午夜精品久久久久久浪潮| 99久久久精品免费观看国产| yy6080久久| 久久亚洲精品无码观看不卡| 久久电影网2021| 国产国产成人精品久久| 看久久久久久a级毛片| 伊人色综合久久天天人守人婷| 精品国产一区二区三区久久蜜臀| 久久久久亚洲AV无码麻豆| 婷婷久久久亚洲欧洲日产国码AV | 综合人妻久久一区二区精品| 久久久国产精品| 久久电影网| 久久99久久无码毛片一区二区| 日本福利片国产午夜久久| 99久久精品国产高清一区二区 | 四虎亚洲国产成人久久精品| 国产福利电影一区二区三区久久老子无码午夜伦不 | 丁香狠狠色婷婷久久综合| 久久精品中文字幕一区| 奇米影视7777久久精品人人爽| 国产成人精品综合久久久|