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

            笑看風云淡

            寵辱不驚,看庭前花開花落;去留無意,望天空云卷云舒
            posts - 96, comments - 48, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 ::  :: 聚合  :: 管理

            ATL的GUI程序設(shè)計(四)

            Posted on 2007-10-17 10:42 天之驕子 閱讀(608) 評論(0)  編輯 收藏 引用
            四章 對話框和控件

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

            題外話先

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

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

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

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

            CDialogImpl

            與ATL窗口類CWindowImpl相對應,ATL的對話框類名為CDialogImpl。它的定義如下:

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

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

            CDialogImpl的使用方法大致如下:

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

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

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

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

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

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

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

            CYourDlg dlg;
            dlg.DoModal();

            控件的使用

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

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

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

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

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

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

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

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

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

            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 );
            }
            };

            下面來介紹對控件事件的處理。通常控件在某些事件發(fā)生時會以發(fā)送WM_COMMAND(普通控件)或WM_NOTIFY(公共控件)消息的方式通知其父窗口,然后我們在其父窗口的窗口過程中處理這些消息即可。WM_COMMAND和WM_NOTIFY的參數(shù)意義如下:

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

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

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

            另外,處理Windows消息、WM_COMMAND消息、WM_NOTIFY消息的消息處理函數(shù)應該分別滿足如下規(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)

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

            1. 由于程序中使用了公共控件ListView,所以在WinMain的開頭需要對公共控件庫進行初始化:
              // 初始化公共控件先
              INITCOMMONCONTROLSEX init;
              init.dwSize = sizeof( init );
              init.dwICC = ICC_LISTVIEW_CLASSES;
              InitCommonControlsEx( &init );
              在此我有必要指出,對公共控件庫的初始化應該盡量使用InitCommonControlsEx,即使InitCommonControls貌似更加方便一些。我曾經(jīng)做過測試,一個使用了DateTime控件并由InitCommonControls初始化的應用程序在WinXP sp2 + VC 6.0編譯完成后,在Win2K下是不能運行的。
            2. CMainDlg::OnRadioSex是為了演示COMMAND_RANGE_HANDLER而寫的一個消息處理函數(shù),其實針對這個示例并不用編寫之——因為Windows系統(tǒng)會自動對Radio按鈕進行檢選狀態(tài)的處理;但如若考慮到多組Radio按鈕存在的情況,CMainDlg::OnRadioSex這樣的處理函數(shù)便會凸顯出它的用處。
            3. LListView::GetSelectionMark并不能用來準確判斷ListView的選中項,尤其是在選中項被刪除之后。
            99久久国产主播综合精品| 国产成人香蕉久久久久| 久久狠狠色狠狠色综合| 久久se精品一区精品二区| 国产精品狼人久久久久影院| 中文字幕精品久久久久人妻| 少妇人妻88久久中文字幕| 亚洲国产成人久久综合一| 美女久久久久久| 久久免费视频1| 精品国产一区二区三区久久久狼 | 天天做夜夜做久久做狠狠| 精品国产乱码久久久久软件| 久久久精品免费国产四虎| 久久精品国产色蜜蜜麻豆| 久久国产精品免费一区二区三区| 中文字幕无码久久久| 久久精品aⅴ无码中文字字幕重口| 久久精品国产精品亚洲艾草网美妙| 久久久久亚洲AV成人网人人网站| 国产—久久香蕉国产线看观看 | 亚洲国产精品久久久久网站| 亚洲中文字幕无码久久2017| 欧美成人免费观看久久| 久久国产成人午夜AV影院| 婷婷久久综合九色综合98| 久久久免费精品re6| 一本色道久久综合狠狠躁| 国产精品中文久久久久久久| 国产精品丝袜久久久久久不卡| 99久久无色码中文字幕| 久久精品www人人爽人人| 性欧美大战久久久久久久久| 久久精品国产日本波多野结衣| 亚洲国产成人精品无码久久久久久综合| 久久A级毛片免费观看| 热re99久久精品国99热| 久久国产欧美日韩精品 | 国产午夜精品久久久久九九电影| 国产精品久久久久久久久免费| 91视频国产91久久久|