<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # Part V - Advanced Dialog UI Classes 原作 :[**Michael Dunn**](http://www.codeproject.com/wtl/WTL4MFC5.asp) 翻譯 :[Orbit(桔皮干了)](http://www.winmsg.com/cn/orbit.htm) ## 本章內容 * [第五章介紹](#intro) * [特別的自畫和外觀定制類](#specialized) * [COwnerDraw](#cownerdraw) * [CCustomDraw](#ccustomdraw) * [WTL的新控件](#newwtlctrls) * [CBitmapButton](#newcbitmapbutton) * [CCheckListViewCtrl](#cchecklist) * [CTreeViewCtrlEx 和 CTreeItem](#treeex) * [CHyperLink](#chyperlink) * [對話框中控件的UI Updating](#uiupdctrl) * [DDV](#ddv) * [處理DDV驗證失敗](#ddvfail) * [改變對話框的大小](#resizing) * [繼續](#upnext) * [參考](#references) * [修改記錄](#revisionhistory) ## 第五章介紹 在上一篇文章我們介紹了一些與對話框和控件有關的WTL的特性,它們和MFC的相應的類作用相同。本文將介紹一些新類實現高級界面特性新類:控件自畫和自定外觀控件,新的WTL控件,UI updating和對話框數據驗證(DDV)。 ## 特別的自畫和外觀定制類 由于自畫和定制外觀控件在圖形用戶界面中是很常用的手段,所以WTL提供了幾個嵌入類來完成這些令人厭煩的工作。我接著就會介紹它們,事實上我們在上一個例子工程ControlMania2的結尾部分已經這么做了。如果你正隨著我的講解用應用程序生成向導創建新工程,請不要忘了使用無模式對話框,為了使正常工作必須使用無模式對話框,我會在[對話框中控件的UI Updating](#uiupdctrl)部分詳細解釋為什么這樣作。 ### COwnerDraw 控件的自畫需要響應四個消息:WM_MEASUREITEM, WM_DRAWITEM, WM_COMPAREITEM, 和WM_DELETEITEM,在atlframe.h頭文件中定義的COwnerDraw類可以簡化這些工作,使用這個類就不需要處理這四個消息,你只需將消息鏈入COwnerDraw,它會調用你的類中的重載函數。 如何將消息鏈入COwnerDraw取決與你是否將消息反射給控件,兩種方法有些不同。下面是COwnerDraw類的消息映射鏈,它使得兩種方法的差別更加明顯: ``` template <class T> class COwnerDraw { public: BEGIN_MSG_MAP(COwnerDraw<T>) MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem) MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem) MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem) MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem) ALT_MSG_MAP(1) MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem) MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem) MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem) MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem) END_MSG_MAP() }; ``` 注意,消息映射鏈的主要部分處理WM_*消息,而ATL部分處理反射的消息,OCM_*。自畫的通知消息就像WM_NOTIFY消息一樣,你可以在父窗口處理它們,也可以將它們反射會控件,如果你使用前一種方法,消息被直接鏈入COwnerDraw: ``` class CSomeDlg : public COwnerDraw<CSomeDlg>, ... { BEGIN_MSG_MAP(CSomeDlg) //... CHAIN_MSG_MAP(COwnerDraw<CSomeDlg>) END_MSG_MAP() void DrawItem ( LPDRAWITEMSTRUCT lpdis ); }; ``` 當然,如果你想要控件自己處理這些消息,你需要使用CHAIN_MSG_MAP_ALT宏將消息鏈入ALT_MSG_MAP(1)部分: ``` class CSomeButtonImpl : public COwnerDraw<CSomeButtonImpl>, ... { BEGIN_MSG_MAP(CSomeButtonImpl) //... CHAIN_MSG_MAP_ALT(COwnerDraw<CSomeButtonImpl>, 1) DEFAULT_REFLECTION_HANDLER() END_MSG_MAP() void DrawItem ( LPDRAWITEMSTRUCT lpdis ); }; ``` COwnerDraw類將對消息傳遞的參數展開,然后調用你的類中的實現函數。上面的例子中,我們自己的類實現DrawItem()函數,當有WM_DRAWITEM或OCM_DRAWITEM消息被鏈入COwnerDraw時,這個函數就會被調用。你可以重載的方法有: ``` void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); int CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct); void DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct); ``` 如果你不想處理某個消息,你可以調用SetMsgHandled(false),消息會被傳遞給消息映射鏈中的其他響應者。SetMsgHandled()事實上是COwnerDraw類的成員函數,但是它的作用和在BEGIN_MSG_MAP_EX()中使用SetMsgHandled()一樣。 對于ControlMania2,它從ControlMania1中的樹控件開始,添加了自畫按鈕處理反射的WM_DRAWITEM消息,下面是資源編輯器中的新按鈕: ![](https://box.kancloud.cn/2016-01-13_56962fbc8c33a.png) 現在我們需要一個新類實現自畫按鈕: ``` class CODButtonImpl : public CWindowImpl<CODButtonImpl, CButton>, public COwnerDraw<CODButtonImpl> { public: BEGIN_MSG_MAP_EX(CODButtonImpl) CHAIN_MSG_MAP_ALT(COwnerDraw<CODButtonImpl>, 1) DEFAULT_REFLECTION_HANDLER() END_MSG_MAP() void DrawItem ( LPDRAWITEMSTRUCT lpdis ); }; ``` DrawItem()使用了像BitBlt()這樣的GDI函數向按鈕的表面畫位圖,代碼應該很容易理解,因為WTL使用的類名和函數名都和MFC類似。 ``` void CODButtonImpl::DrawItem ( LPDRAWITEMSTRUCT lpdis ) { // NOTE: m_bmp is a CBitmap init'ed in the constructor. CDCHandle dc = lpdis->hDC; CDC dcMem; dcMem.CreateCompatibleDC ( dc ); dc.SaveDC(); dcMem.SaveDC(); // Draw the button's background, red if it has the focus, blue if not. if ( lpdis->itemState & ODS_FOCUS ) dc.FillSolidRect ( &lpdis->rcItem, RGB(255,0,0) ); else dc.FillSolidRect ( &lpdis->rcItem, RGB(0,0,255) ); // Draw the bitmap in the top-left, or offset by 1 pixel if the button // is clicked. dcMem.SelectBitmap ( m_bmp ); if ( lpdis->itemState & ODS_SELECTED ) dc.BitBlt ( 1, 1, 80, 80, dcMem, 0, 0, SRCCOPY ); else dc.BitBlt ( 0, 0, 80, 80, dcMem, 0, 0, SRCCOPY ); dcMem.RestoreDC(-1); dc.RestoreDC(-1); } ``` 我們的按鈕看起來是這個樣子: ![](https://box.kancloud.cn/2016-01-13_56962fbc9db6b.png) ### CCustomDraw CCustomDraw類使用和COwnerDraw類相同的方法處理NM_CUSTOMDRAW消息,對于自定繪制的每個階段都有相應的重載函數: ``` DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnItemPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnItemPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnItemPostEraset(int idCtrl, LPNMCUSTOMDRAW lpNMCD); DWORD OnSubItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD); ``` 這些函數默認都是返回CDRF_DODEFAULT,如果想自畫控件或返回一個不同的值,就需要重載這些函數: 你可能注意到上面的屏幕截圖將“道恩”(Dawn:女名)顯示成綠色,這是因為CBuffyTreeCtrl將消息鏈入CCustomDraw并重載了OnPrePaint()和OnItemPrePaint()方法。向樹控件中添加節點時,節點的item data字段被設置成1,OnItemPrePaint()檢查這個值,然后改變文字的顏色。 ``` DWORD CBuffyTreeCtrl::OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD) { return CDRF_NOTIFYITEMDRAW; } DWORD CBuffyTreeCtrl::OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCD) { if ( 1 == lpNMCD->lItemlParam ) pnmtv->clrText = RGB(0,128,0); return CDRF_DODEFAULT; } ``` CCustomDraw類也有SetMsgHandled()函數,你可以像在COwnerDraw類那樣使用這個函數。 ## WTL的新控件 WTL有幾個新控件,它們要么是其他封裝類的擴展(像 CTreeViewCtrlEx),要么是提供windows標準控件沒有的新功能(像 CHyperLink)。 ### CBitmapButton WTL的CBitmapButton類聲明在atlctrlx.h中,它比MFC的同名類使用起來要簡單的多。WTL的CBitmapButton類使用image list而不是單個的位圖資源,你可以將多個按鈕的圖像放到一個位圖文件中,減少GDI資源的占用。這對于使用很多圖片并需要在Windows 9X系統上運行的程序很有好處,因為使用太多的單個位圖將會很快耗盡GDI資源并導致系統崩潰。 CBitmapButton是一個CWindowImpl派生類,它又很多特色:自動調整控件的大小,自動生成3D邊框,支持hot-tracking,每個按鈕可以使用多個圖像分別表示按鈕的不同狀態。 在ControlMania2中,我們對前面的例子創建的自畫按鈕使用CBitmapButton類。現在CMainDlg對話框類中添加CBitmapButton類型的變量m_wndBmpBtn,調用SubclassWindow()函數或使用DDX將其和控件聯系起來,將位圖裝載到image list并告訴按鈕使用這個image list,還要告訴按鈕每個圖像分別對應按鈕的什么狀態。下面是OnInitDialog()函數中建立和使用這個按鈕的代碼段: ``` // Set up the bitmap button CImageList iml; iml.CreateFromImage ( IDB_ALYSON_IMGLIST, 81, 1, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION ); m_wndBmpBtn.SubclassWindow ( GetDlgItem(IDC_ALYSON_BMPBTN) ); m_wndBmpBtn.SetToolTipText ( _T("Alyson") ); m_wndBmpBtn.SetImageList ( iml ); m_wndBmpBtn.SetImages ( 0, 1, 2, 3 ); ``` 默認情況下,按鈕只是引用image list,所以OnInitDialog()不能delete它所創建的image list。下面顯示的是新按鈕的一般狀態,注意控件是如何根據圖像的大小來調整自己的大小。 ![](https://box.kancloud.cn/2016-01-13_56962fbcacdef.png) 因為CBitmapButton是一個非常有用的類,我想介紹一下它的公有方法。 #### CBitmapButton methods CBitmapButtonImpl類包含了實現一個按鈕的所有代碼,除非你想重載某個方法或消息處理,你可以對控件直接使用CBitmapButton類。 ##### CBitmapButtonImpl constructor ``` CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE,HIMAGELIST hImageList = NULL) ``` 構造函數可以指定按鈕的擴展樣式(這與窗口的樣式不沖突)和圖像列表,通常使用默認參數就足夠了,因為可以使用其他的方法設定這些屬性。 ##### SubclassWindow() ``` BOOL SubclassWindow(HWND hWnd) ``` SubclassWindow()是個重載函數,主要完成控件的子類化和初始化控件類保有的內部數據。 ##### Bitmap button extended styles ``` DWORD GetBitmapButtonExtendedStyle() DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0) ``` CBitmapButton支持一些擴展樣式,這些擴展樣式會對按鈕的外觀和操作方式產生影響: BMPBTN_HOVER 使用hot-tracking,當鼠標移到按鈕上時按鈕被畫成焦點狀態。 BMPBTN_AUTO3D_SINGLE, BMPBTN_AUTO3D_DOUBLE 在按鈕圖像周圍自動產生一個三維邊框,當按鈕擁有焦點時會顯示一個表示焦點的虛線矩形框。另外如果你沒有指定按鈕按下狀態的圖像,將會自動生成一個。BMPBTN_AUTO3D_DOUBLE樣式生成的邊框稍微粗一些,其他特征和BMPBTN_AUTO3D_SINGLE一樣。 BMPBTN_AUTOSIZE 按鈕調整自己的大小以適應圖像大小,這是默認樣式。 BMPBTN_SHAREIMAGELISTS 如果指定這個樣式,按鈕不負責銷毀按鈕使用的image list,如果不使用這個樣式,CBitmapButton的析構函數會銷毀按鈕使用的image list。 BMPBTN_AUTOFIRE 如果設置這個樣式,在按鈕上按住鼠標左鍵不放將會產生連續的WM_COMMAND消息。 調用SetBitmapButtonExtendedStyle()時,dwMask參數控制著那個樣式將被改變,默認值是0,意味著用新樣式完全替換舊的樣式。 ##### Image list management ``` HIMAGELIST GetImageList() HIMAGELIST SetImageList(HIMAGELIST hImageList) ``` 調用SetImageList()設置按鈕使用的image list。 ##### Tooltip management ``` int GetToolTipTextLength() bool GetToolTipText(LPTSTR lpstrText, int nLength) bool SetToolTipText(LPCTSTR lpstrText) ``` CBitmapButton支持顯示工具提示(tooltip),調用SetToolTipText()指定顯示的文字。 ##### Setting the images to use ``` void SetImages(int nNormal, int nPushed = -1,int nFocusOrHover = -1, int nDisabled = -1) ``` 調用SetImages()函數告訴按鈕分別使用image list的拿一個圖像表示那個狀態。nNormal是必須的,其它是可選的,使用-1表示對應的狀態沒有圖像。 ### CCheckListViewCtrl CCheckListViewCtrl類在atlctrlx.h中定義,它是一個CWindowImpl派生類,實現了一個帶檢查框的list view控件。它和MFC的CCheckListBox不同,CCheckListBox只是一個list box,不是list view。CCheckListViewCtrl類非常簡單,只添加了很少的函數,當然,它使用了一個新的輔助類CCheckListViewCtrlImplTraits,它和CWinTraits類的作用類似,只是第三個參數是list view控件的擴展樣式屬性,如果你沒有定義自己的CCheckListViewCtrlImplTraits,它將使用沒默認的樣式:LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT。 下面是一個定義list view擴展樣式屬性的例子,加入了一個使用這個樣式的新類。(注意,擴展屬性必須包含LVS_EX_CHECKBOXES,否則會因起斷言錯誤消息。) ``` typedef CCheckListViewCtrlImplTraits< WS_CHILD | WS_VISIBLE | LVS_REPORT, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES | LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE> CMyCheckListTraits; class CMyCheckListCtrl : public CCheckListViewCtrlImpl<CMyCheckListCtrl, CListViewCtrl, CMyCheckListTraits> { private: typedef CCheckListViewCtrlImpl<CMyCheckListCtrl, CListViewCtrl, CMyCheckListTraits> baseClass; public: BEGIN_MSG_MAP(CMyCheckListCtrl) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() }; ``` #### CCheckListViewCtrl methods ##### SubclassWindow() 當子類化一個已經存在的list view控件時,SubclassWindow()查看CCheckListViewCtrlImplTraits的擴展樣式屬性并將之應用到控件上。未用到前兩個參數(窗口樣式和擴展窗口樣式)。 ##### SetCheckState() and GetCheckState() 這些方法實際上是在CListViewCtrl中,SetCheckState()使用行的索引和一個布爾類型參數,該布爾參數的值表示是否check這一行。GetCheckState()以行索引未參數,返回改行的checked狀態。 ##### CheckSelectedItems() 這個方法使用item的索引作為參數,它翻轉這個item的check狀態,這個item必須是被選定的,同時還將其他所有被選擇的item設置成相應狀態(譯者加:多選狀態下)。你大概不會用到這個方法,因為CCheckListViewCtrl會在check box被單擊或用戶按下了空格鍵時設置相應的item的狀態。 下面是ControlMania2中的CCheckListViewCtrl的樣子: ![](https://box.kancloud.cn/2016-01-13_56962fbcb9a3f.png) ### CTreeViewCtrlEx and CTreeItem 有兩個類使得樹控件的使用簡化了很多:CTreeItem類封裝了HTREEITEM,一個CTreeItem對象含有一個HTREEITEM和一個指向包含這個HTREEITEM的樹控件的指針,使你不必每次調用都引用樹控件;CTreeViewCtrlEx和CTreeViewCtrl一樣,只是它的方法操作CTreeItem而不是HTREEITEM。例如,InsertItem()函數返回一個CTreeItem而不是HTREEITEM,你可以使用CTreeItem操作新添加的item。下面是一個例子: ``` // Using plain HTREEITEMs: HTREEITEM hti, hti2; hti = m_wndTree.InsertItem ( "foo", TVI_ROOT, TVI_LAST ); hti2 = m_wndTree.InsertItem ( "bar", hti, TVI_LAST ); m_wndTree.SetItemData ( hti2, 100 ); // Using CTreeItems: CTreeItem ti, ti2; ti = m_wndTreeEx.InsertItem ( "foo", TVI_ROOT, TVI_LAST ); ti2 = ti.AddTail ( "bar", 0 ); ti2.SetData ( 100 ); ``` CTreeViewCtrl對HTREEITEM的每一個操作,CTreeItem都有與之對應的方法,正像每一個關于HWND的API都有一個CWindow方法與之對應一樣。查看ControlMania2的代碼可以看到更多的CTreeViewCtrlEx和CTreeItem類的方法的演示。 ### CHyperLink CHyperLink是一個CWindowImpl派生類,它子類化一個static text控件,使之變成可點擊的超鏈接。CHyperLink根據用戶的IE使用的顏色畫鏈接對象,還支持鍵盤導航。CHyperLink類的構造函數沒有參數,下面是其它的公有方法。 #### CHyperLink methods CHyperLinkImpl類內含實現一個超鏈接的全部代碼,如果不需要重載它的方法或處理消息的話,你可以直接使用CHyperLink類。 ##### SubclassWindow() ``` BOOL SubclassWindow(HWND hWnd) ``` 重載函數SubclassWindow()完成控件子類化,然后初始化該類保有的內部數據。 ##### Text label management ``` bool GetLabel(LPTSTR lpstrBuffer, int nLength) bool SetLabel(LPCTSTR lpstrLabel) ``` 獲得或設置控件顯示的文字,如果不指定顯示文字,控件會顯示資源編輯器指定給控件的靜態字符串。 ##### Hyperlink management ``` bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) bool SetHyperLink(LPCTSTR lpstrLink) ``` 獲得或設置控件關聯超鏈接的URL,如果不指定超鏈接URL,控件會使用顯示的文字字符串作為URL。 ##### Navigation ``` bool Navigate() ``` 導航到當前超鏈接的URL,該URL或者是由SetHyperLink()函數指定的URL,或者就是控件的窗口文字。 ##### Tooltip management 沒有公開的方法設置工具提示,所以需要直接使用CToolTipCtrl成員m_tip。 下圖顯示的就是ControlMania2對話框中的超鏈接控件: ![](https://box.kancloud.cn/2016-01-13_56962fbcc90fd.png) 在OnInitDialog()函數中設置URL: ``` m_wndLink.SetHyperLink ( _T("http://www.codeproject.com/") ); ``` ## 對話框中控件的UI Updating 對話框中的的UI updating控制比MFC中簡單得多,在MFC中,你需要響應未公開的WM_KICKIDLE消息,處理這個消息并觸發控件的updating,在WTL中,沒有這個詭計,不過向導存在一個BUG,需要手工添加一行代碼解決這個問題。 首先需要記住的是對話框必須是無模式的,因為CUpdateUI需要在程序的消息循環控制下工作。如果對話框是模式的,系統處理消息循環,我們程序的空閑處理函數就不會被調用,由于CUpdateUI是在空閑時間工作的,所以沒有空閑處理就沒有UI updating。 ControlMania2的對話框是非模式的,類定義的開始部分很像是一個框架窗口類: ``` class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>, public CMessageFilter, public CIdleHandler { public: enum { IDD = IDD_MAINDLG }; virtual BOOL PreTranslateMessage(MSG* pMsg); virtual BOOL OnIdle(); BEGIN_MSG_MAP_EX(CMainDlg) MSG_WM_INITDIALOG(OnInitDialog) COMMAND_ID_HANDLER_EX(IDOK, OnOK) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) COMMAND_ID_HANDLER_EX(IDC_ALYSON_BTN, OnAlysonODBtn) END_MSG_MAP() BEGIN_UPDATE_UI_MAP(CMainDlg) END_UPDATE_UI_MAP() //... }; ``` 注意CMainDlg類從CUpdateUI派生并含有一個update UI鏈。OnInitDialog()做了這些工作,這和前面介紹的框架窗口中的代碼很相似: ``` // register object for message filtering and idle updates CMessageLoop* pLoop = _Module.GetMessageLoop(); ATLASSERT(pLoop != NULL); pLoop->AddMessageFilter(this); pLoop->AddIdleHandler(this); UIAddChildWindowContainer(m_hWnd); ``` 只是這次我們不是調用UIAddToolbar()或UIAddStatusBar(),而是調用UIAddChildWindowContainer(),它告訴CUpdateUI我們的對話框含有需要updating的字窗口,只要看看OnIdle(),你會懷疑少了寫什么: ``` BOOL CMainDlg::OnIdle() { return FALSE; } ``` 你可能猜想這里應該調用另一個CUpdateUI的方法做一些實在的updating工作,你是對的,應該是這樣的,向導在OnIdle()中漏掉了一行代碼,現在加上: ``` BOOL CMainDlg::OnIdle() { UIUpdateChildWindows(); return FALSE; } ``` 為了演示UI updating,我們設定鼠標點擊左邊的位圖按鈕,使得右邊的按鈕變得可用或禁用。先在update UI鏈中添加一個消息入口,使用UPDUI_CHILDWINDOW標志表示此入口是子窗口類型: ``` BEGIN_UPDATE_UI_MAP(CMainDlg) UPDATE_ELEMENT(IDC_ALYSON_BMPBTN, UPDUI_CHILDWINDOW) END_UPDATE_UI_MAP() ``` 在左邊的按鈕的單擊事件處理中,我們調用UIEnable()來翻轉另一個按鈕的使能狀態: ``` void CMainDlg::OnAlysonODBtn ( UINT uCode, int nID, HWND hwndCtrl ) { static bool s_bBtnEnabled = true; s_bBtnEnabled = !s_bBtnEnabled; UIEnable ( IDC_ALYSON_BMPBTN, s_bBtnEnabled ); } ``` ## DDV WTL的對話框數據驗證(DDV)比MFC簡單一些,在MFC中你需要分別使用DDX(對話框數據交換)宏和DDV(對話框數據驗證)宏,在WTL中只需一個宏就可以了,WTL包含基本的數據驗證支持,在DDV鏈中可以使用三個宏: `DDX_TEXT_LEN` 和DDX_TEXT一樣,只是還要驗證字符串的長度(不包含結尾的空字符)小于或等于限制長度。 `DDX_INT_RANGE` and `DDX_UINT_RANGE` 和DDX_INT,DDX_UINT一樣,還加了對數字的最大最小值的驗證。 `DDX_FLOAT_RANGE` 除了像DDX_FLOAT一樣完成數據交換之外,還驗證數字的最大最小值。 ControlMania2有一個ID是IDC_FAV_SEASON的edit box,它和成員變量m_nSeason相關聯。 ![](https://box.kancloud.cn/2016-01-13_56962fbcd6a8c.png) 由于有效的值是1到7,所以使用這樣的數據驗證宏: ``` BEGIN_DDX_MAP(CMainDlg) //... DDX_INT_RANGE(IDC_FAV_SEASON, m_nSeason, 1, 7) END_DDX_MAP() ``` OnOK()調用DoDataExchange()獲得season的數值,并驗證是在1到7之間。 ### 處理DDV驗證失敗 如果控件的數據驗證失敗,CWinDataExchange會調用重載函數OnDataValidateError(),默認到處理是驅動PC喇叭發出聲音,你可能想給出更友好的錯誤指示。OnDataValidateError()的函數原型是: ``` void OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data ); ``` _XData是一個WTL的內部數據結構,CWinDataExchange根據輸入的數據和允許的數據范圍填充這個數據結構。下面是這個數據結構的定義: ``` struct _XData { _XDataType nDataType; union { _XTextData textData; _XIntData intData; _XFloatData floatData; }; }; ``` nDataType指示聯合中的三個成員那個是有意義的,nDataType 的取值可以是: ``` enum _XDataType { ddxDataNull = 0, ddxDataText = 1, ddxDataInt = 2, ddxDataFloat = 3, ddxDataDouble = 4 }; ``` 在我們的例子中,nDataType的值是ddxDataInt,這表示_XData中的_XIntData成員是有效的,_XIntData是個簡單的數據結構: ``` struct _XIntData { long nVal; long nMin; long nMax; }; ``` 我們重載OnDataValidateError()函數,顯示錯誤信息并告訴用戶允許的數值范圍: ``` void CMainDlg::OnDataValidateError ( UINT nCtrlID, BOOL bSave, _XData& data ) { CString sMsg; sMsg.Format ( _T("Enter a number between %d and %d"), data.intData.nMin, data.intData.nMax ); MessageBox ( sMsg, _T("ControlMania2"), MB_ICONEXCLAMATION ); ::SetFocus ( GetDlgItem(nCtrlID) ); } ``` _XData中的另外兩個結構_XTextData和_XFloatData的定義在atlddx.h中,感興趣的話可以打開這個文件查看一下。 ## 改變對話框的大小 WTL引起我的注意的第一件事是對可調整大小對話框的內建的支持。在這之前我曾寫過一篇[關于這個主題的文章](http://www.codeproject.com/wtl/wtldlgresize.asp),詳情請參考這篇文章。簡單的說就是將CDialogResize類添加到對話框的集成列表,在OnInitDialog()中調用DlgResize_Init(),然后將消息鏈入CDialogResize。 ## 繼續 下一章,我將介紹如何在對話框中使用ActiveX控件和如何處理控件觸發的事件。 ## 參考 [Using WTL's Built-in Dialog Resizing Class](http://www.codeproject.com/wtl/wtldlgresize.asp) - Michael Dunn [Using DDX and DDV with WTL](http://www.codeproject.com/wtl/propsheetddx.asp) - Less Wright ## 修改記錄 2003年4月28日,本文第一次發表。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看