WTL for MFC Programmers, Part V - Advanced Dialog Ui Classes
Original: Michael Dunn [English] Translation: Orbit (Star Rails Orbit) [http://www.winmsg.com/cn/orbit.htm]
Download Demonstration Code
This chapter
Chapter 5 introduces special self-drawn and appearance custom classes
COWNERDRAW CCUSTOMDRAW WTL new control
CTRETMAPB / CTREEWCTRL CTREEVIEWCTRLEX and CTREEITEM CHYPERLINK dialog UI Updating DDV
Processing DDV Verification Failing Change Dialog Size Continued Reference Modify Record
Chapter 5 Introduction
In the previous article we introduced some features related to dialogs and controls, they are the same as those of the MFC. This article will introduce some new classes to implement advanced interface features new classes: control self-drawn and custom appearance control, new WTL control, Updating, and dialog data verification (DDV).
Special self-drawn and appearance custom class
Since the self-drawn and custom appearance controls are very common in the graphical user interface, WTL provides several embedded classes to accomplish these troubles. I will then introduce them, in fact, we have done this in the end of the last example of the end of Controlmania2. If you are creating a new project with my explanation of the application generated wizard, don't forget to use the Mode dialog box, in order to make normal work must use the Mode dialog box, I will in the dialog box UI Updating section Explain why this is made.
CownerDraw
The self-drawing of the control needs to respond to four messages: WM_MEASUREITEM, WM_DRAWITEM, WM_CompareItem, and WM_DELETEITEM, the COWNERDRAW class defined in the atlframe.h header file can simplify these work, you only need to handle these four messages, you only need to Message link into COWNERDRAW, which calls the overload function in your class.
How to link messages into COWNERDRAW Since reflecting messages to controls, both methods are somewhat different. Below is the message mapping chain of the COWNERDRAW class, which makes the difference between the two methods more obvious:
template
Note that the main part of the message mapping chain processes the WM_ * message, and the ATL part processes the reflected message, OCM_ *. The self-drawn notification message is like a WM_Notify message, you can process them in the parent window, you can also reflect the control, if you use the previous method, the message is directly chained into CownerDraw:
class CSomeDlg: public COwnerDraw
class CSomeButtonImpl: public COwnerDraw
The CownerDraw class expands the parameters passing the message and then calls the implementation function in your class. In the above example, our own class implements a DrawItem () function, which is called when there is a WM_DRAWITEM or OCM_DRAWITEM message being lined into COWNERDRAW. The way you can overload is:
void DrawItem (LPDRAWITEMSTRUCT lpDrawItemStruct); void MeasureItem (LPMEASUREITEMSTRUCT lpMeasureItemStruct); int CompareItem (LPCOMPAREITEMSTRUCT lpCompareItemStruct); void DeleteItem (LPDELETEITEMSTRUCT lpDeleteItemStruct);
If you don't want to handle a message, you can call SETMSGHAND (FALSE), and the message is passed to other respondents in the message map chain. SetmsgHandled () is actually a member function of the COWNERDRAW class, but its role is the same as setmsgHandled () in Begin_MSG_MAP_EX ().
For Controlmania2, it starts from the tree control in Controlmania1, adds the self-drawn button to process the reflected WM_DRAWITEM message, below is the new button in the resource editor:
Now we need a new class to implement the self-drawn button:
class CODButtonImpl: public CWindowImpl
DrawItem () uses the GDI function like Bitblt () to the surface of the button, the code should be easily understood because the class name and function names used by WTL are similar to 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 click. 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);} Our button looks like this:
CCustomDraw
CCUSTOMDRAW class uses the same method of the COWNERDRAW class to handle the NM_CUSTOMDRAW message, there is a corresponding overload function for each stage of custom drawing:
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);
These functions are returned to CDRF_DODEFAULT by default. If you want to self-draw control or return a different value, you need to overload these functions:
You may notice the above screenshots to display "Dawn" (Dawn: Female Name) as green, because CBuffyTreeCtrl links the message into CCUSTOMDRAW and overprepaint () and onItemPrepaint () methods. When adding a node to the tree control, the Node's Item Data field is set to 1, onItemPrepaint () checks this value, then change the color of the text. 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;}
The CCustomDraw class also has a SetMSGHandLED () function that you can use this function as the COWNERDRAW class.
WTL new control
There are several new controls for WTL, or they either the extension of other package classes (like ctreeViewctrlex), or provide new features that are not available in Windows standard controls (like ChyperLink).
CBitmapButton
WTL's CBITMAPBUTTON Class declaration is in AtlCtrlx.h, it is simple to use than the MFC's same name class. WTL's CBitmapButton class uses Image List instead of a single bitmap resource, you can put multiple buttons in a bitmap file to reduce the occupation of GDI resources. This is good for programs that use a lot of pictures and need to run on the Windows 9x system, because using too many single bitmaps will quickly exhaust the GDI resources and cause the system to crash.
CBitMapButton is a CWindowIMPL derived class, which also features: Automatically adjust the size of the control, automatically generates a 3D border, supports Hot-Tracking, each button can use multiple images to indicate the different states of the button.
In Controlmania2, we created a self-draw button created by the previous example using the CBITMAPBUTTON class. Now add the CMAINDLG dialog class to the CBitMapButton type variable M_WndBMPBTN, call the subclassWindow () function or use DDX to load it with the control, load the bitmap to Image List and tell the button to use this image, but also tell the button Each image What state is respectively corresponding to the button. Below is the code segment that establishes and uses this button in the OnInitDialog () function:
// 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);
By default, buttons are just reference Image List, so OnInitDialog () cannot delete Image List it created. The general state of the new button is displayed below, and the control is how to adjust its size based on the size of the image. Because CBitmapButton is a very useful class, I want to introduce it to its public method.
CBitmapButton Methods
The CBitmapButtonImpl class contains all code that implements a button unless you want to overload a method or message processing, you can use the CBitmapButton class directly to the control.
CBITMAPBUTTONIMPL CONSTRUCTOR
CBitmapButtonImpl (DWORD DWEXTENDSTYLE = BMPBTN_AUTOSIZE, HIMAGELIST HIMAGELIST = NULL)
Constructor can specify a button extension style (this is not conflict with the window of the window) and an image list, which usually uses the default parameters to be sufficient because other methods can be used to set these properties.
SubclassWindow ()
Bool SubclassWindow (HWND HWND)
SubclassWindow () is an overload function, which is mainly completed by the subclass of the control and internal data owned by the initialization control class.
Bitmap Button Extended Styles
DWord getBitmapButtoneXtendedStyle () DWORDSTYLE (DWORD DWEXTENDEDSTYLE, DWORD DWMASK = 0)
CBitmapButton supports some extension styles, which affect the appearance and operation of the button:
BMPBTN_HOVER
Use Hot-Tracking to draw the focus status when the mouse moves to the button.
BMPBTN_AUTO3D_SINGLE, BMPBTN_AUTO3D_DOUBLE
A three-dimensional border is automatically generated around the button image. When the button has a focus, a dotted rectangular box indicating the focus is displayed. In addition, if you don't specify an image that is prescribed, it will automatically generate one. The border generated by BMPBTN_AUTO3D_DOUBLE style is slightly thick, and other features are the same as BMPBTN_AUTO3D_SINGLELELE.
BMPBTN_AUTOSIZE
The button adjusts its size to accommodate the image size, which is the default style.
BMPBTN_SHAREIMAGELISTS
If this style is specified, the button is not responsible for destroying the Image List used, if this style is not used, the CBitmapButton's destruction function destroys the Image List used by the button.
BMPBTN_AUTOFIRE
If this style is set, press and hold the left mouse button on the button to generate a continuous WM_COMMAND message.
When you call setBitMapButtonExtendedStyle (), the DWMask parameter controls that style will be changed, the default is 0, which means completely replacing the old style with the new style.
Image List Management
Himagelist GetImagelist () himagelist setimagelist (himagelist himagelist)
Call the setimagelist () Setting the image list used by the button.
Tooltip Management
INT GetTooltiptextLength () Bool getTooltiptext (LPTSTSTSTOOLTRTEXT, INT NLENGTH) BOOL SetTooltiptext (LPCTSTR LPSTRTEXT)
CBitMapButton supports the display tool tips (Tooltip), calling setToolTIPText () Specify the text displayed.
Setting the images to usvoid setimages (int nNormal, int npushed = -1, int nfocusorhover = -1, int ndisabled = -1)
Call the setImages () function tells the button Said that the image of Image List represents that state. nNORMAL is necessary, others are optional, using -1 indicates that the corresponding state has no image.
CchecklistViewCtrl
CChecklistViewCtrl classes are defined in AtlCtrlx.h, it is a CWindowIMPL derived class that implements a list of List view controls with check boxes. It is different from the MFC's cchecklistbox, cchecklistbox is just a list box, not List view. The CChecklistViewCtrl class is very simple, only a few functions, of course, it uses a new secondary cchecklistviewctrlimpltraits, which is similar to the Cwintraits class, just the third parameter is an extension style property of the List view control, if you don't have Define your own cchecklistviewctrlimpltraits, which will use no default style: lvs_ex_checkboxes | lvs_ex_fullrowselect.
Below is an example of defining a List view extension style properties, adding a new class using this style. (Note that the extension attribute must contain lvs_ex_checkboxes, otherwise it will be incorrectly reported.)
typedef CCheckListViewCtrlImplTraits
CchecklistViewCtrl Methods
SubclassWindow ()
SubclassWindow () views the extension style properties of CChecklistViewCtrlIMPLTRAITS and applies it to the controls when they already exist. Not used to the first two parameters (window styles and extended window styles).
SetCheckState () and getCheckstate ()
These methods are actually in CLISTVIEWCTRL, setCheckState () uses rows indexes and a Boolean parameter, the value of the Boolean parameter indicates whether Check this is. GetCheckState () Returns the CHECKED Status of the Return to Return to the Index.
CHECKSELECTEDITEMS () This method uses Item's index as a parameter, which flips this item's Check status, this item must be selected, and all other selected items are set to the appropriate state (translator plus: multi-selection state under). You probably not use this method, because cchecklistViewCtr will set the status of the corresponding Item when the check box is clicked or when the user presses the space bar.
Here is the CChecklistViewCtrl in Controlmania2:
CTreeViewCtrlex and CtreeItem
There are two classes that make the tree controls simplify a lot: CtreeItem classes encapsulate HtreeItem, a ctreeItem object contains a HtreeItem and a pointer to the tree control containing this HtreeItem, so you don't have to quote tree control each time; CTreeViewCtrlex and Like CTreeViewCtrl, just its method operates CtreeItem instead of htreeItem. For example, an InsertItem () function returns a ctreeItem instead of htreeItem, you can use CTREEITEM to operate newly added Item. Below is an example:
// 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 CTREETEMS: CTREEITEM TI, TI2; Ti = M_WndtreeEx.insertitem ("foo", TVi_root, TVi_last); Ti2 = Ti.Addtail ("bar", 0); Ti2.SetData (100);
CtreeViewCtrl's HtreeItem has a corresponding method, and is as a CWindow method with a CWINDOW method, as a CTREEITEM. Viewing the CONTROLMANIA2 code can see the demo of more CtreeViewCtrlex and CTreeItem classes.
Chyperlink
ChyperLink is a CWINDOWIMPL derived class that subcatenates a Static Text control that changes it into a clickable hyperlink. ChyperLink also supports the keyboard navigation according to the color draw object used by the user's IE. The constructor of the ChyperLink class has no parameters, and below is other public methods.
ChyperLink Methods
The ChyperLinkIMPL class includes all code that implements a hyperlink. If you don't need to overload it or to process the message, you can use the ChyperLink class directly.
SubclassWindow ()
Bool SubclassWindow (HWND HWND)
The overload function subclasswindow () completes the control subclass and then initializes the internal data that the class.
Text Label Management
Bool getlabel (LPTSTR LPSTRBUFER, INT NLENGTH) BOOL setLabel (LPCTSTR LPSTRLABEL)
Get or set the text displayed text, if you do not specify a display text, the control will display the resource editor to specify a static string of the control.
HyperLink Management
Bool GethyperLink (LPTSTR LPSTRBUFFER, INT NLENGTH) BOOL SETHYPERLINK (LPCTSTR LPSTRLINK) Gets or sets the URL of the control associated hyperlink, and if the hyperlink URL is not specified, the control uses the displayed text string as the URL.
Navigation
Bool navigate ()
Navigate to the current hyperlink URL, the URL, or the URL specified by the sethyperlink () function, or the window text of the control.
Tooltip Management
There is no public method setting tooltip, so you need to use the CTooltipCtrl member M_TIP directly.
The following figure shows the hyperlink control in the ControlMania2 dialog:
Set up URL in the OnInitDialog () function:
M_WndLink.sethyperLink (_t ("http://www.codeproject.com/"));
UI Updating in the dialog box
The UpDating control in the dialog is much simpler than the MFC. In the MFC, you need to respond to the unapproved WM_kickidle message, process this message and trigger the Updating of the control, in the WTL, there is no trick, but there is a bug in the wizard Need manually to add a line of code to solve this problem.
First, it is necessary to remember that the dialog must be no mode because CupdateUi needs to work under the message loop control of the program. If the dialog box is mode, the system handles the message loop, the idle processing function of our programs will not be called, because CupdateUI is working in idle time, so there is no Updating in the II Updating.
Controlmania2 dialogs are non-mode, and the class definition is very similar to a frame window class:
class CMainDlg: public CDialogImpl
Note that the CMAINDLG class is derived from Cupdateui and contains an Update UI chain. OnInitDialog () made these work, this is similar to the code in the frame window described in the previously introduced:
// register object for message filtering and idle updates CMessageLoop * pLoop = _Module.GetMessageLoop (); ATLASSERT (pLoop = NULL!); PLoop-> AddMessageFilter (this); pLoop-> AddIdleHandler (this); UIAddChildWindowContainer (m_hWnd); only This time we didn't call UIAddToolbar () or uiaddstatusbar (), but call UIAddChildWindowContainer (), telling Cupdateui Our dialog box contains the word window that requires Updating, just see OnIdle (), you will be less written:
Bool cmaindlg :: onidle () {Return False;}
You may guess another CupdateUi method should call another real Updating job, you are right, it should be like this, the wizard missed a line of code in OnIdle (), add:
Bool cmaindlg :: onidle () {uiupdatechildwindows (); return false;}
To demonstrate UI Updating, we set the mouse click the bitmap button on the left, so that the button on the right is available or disabled. First add a message entry in the UPDATE UI chain, use the UPDUI_ChildWindow flag indicating that this entry is a sub-window type:
Begin_UPDATE_UI_MAP (CMAINDLG) UPDATE_ELEMENT (IDC_ALYSON_BMPBTN, UPDUI_CHILDWINDOW) End_UPDATE_UI_MAP ()
In the click event handle of the button on the left, we call uienable () to flip the other button to the power:
Void cmaindlg :: OnDerysonoDBTN (uint ucode, int nid, hwnd hwndctrl) {static bool s_bbtnenabled = true; s_bbtnenabled =! s_bbtnenabled; uienable (idc_alyson_bmpbtn, s_bbtnenable);}
DDV
WTL's dialog data verification (DDV) is simpler than MFC, you need to use DDX (dialog data exchange) macro and DDV (dialog data verification) macro in MFC, just one macro in WTL, WTL Contains basic data verification support, you can use three macros in the DDV chain:
DDX_TEXT_LEN
Like DDX_Text, just verify the length of the string (no empty characters that do not contain the end) less than or equal to the limit length.
DDX_INT_RANGE AND
DDX_UINT_RANGE
Like DDX_INT, DDX_UINT, the verification of the maximum minimum value of the numbers is added.
DDX_FLOAT_RANGE
In addition to completing data exchange as DDX_FLOAT, verify the maximum minimum value of numbers.
Controlmania2 has an ID to be IDC_FAV_SEASON's Edit Box, which is associated with the member variable m_nseason.
Since the valid value is 1 to 7, such a data verification macro is used:
Begin_ddx_map (cmaindlg) // ... DDX_INT_RANGE (IDC_FAV_SEASON, M_NSEASON, 1, 7) end_ddx_map ()
Onok () calls DODATAEXChange () Gets the value of Season and verifies between 1 and 7. Processing DDV verification failed
If the data verification fails, CWindataExchange calls the overload function onDatavalidateError (). The default to the process is to drive the PC speaker to make sounds, you might want to give more friendly error instructions. The function prototype of onDatavalidateError () is:
Void OnDataValidateError (uint nctrlid, bool bsave, _xdata & data);
_XDATA is a WTL internal data structure, CWindataExchange pops this data structure based on the input data and the allowed data range. Here is the definition of this data structure:
Struct _xdata {_xdatatype nDataType; union {_xtextdata textdata; _xintdata intdata;};
NDATYPE indicates that the three members of the joint is meaningful, and the NDATATYPE can be:
Enum _xdatatype {ddxdatanull = 0, ddxdatatext = 1, ddxdataint = 2, ddxdatafloat = 3, ddxdataDouble = 4};
In our example, the value of NDataType is DDXData, which means that _Xintdata members in _XDATA are valid, _xintdata is a simple data structure:
Struct _xintdata {long nval; long nmin; long nmax;};
We overload the onDatavalidateError () function, display the error message and tell the user to range:
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));}
The other two structures in _XDATA _XTextData and _xfloatdata are defined in atlddx.h, if you are interested, you can open this file.
Change the size of the dialog
WTL caused my attention to my attention is the built-in support for the adjustable size dialog. Before this, I wrote an article about this topic. For details, please refer to this article. Simply put to add the CDIALOGRESIZE class to the integrated list of dialogs, call DLGResize_init () in OnInitDialog (), then link the message into CDialogResize.
carry on
Next chapter, I will show how to use ActiveX controls in the dialog and how to handle controls of controls.
reference
USING WTL's Built-in Dialog Resizing Class - Michael Dunn
Using ddx and ddv with wtl - Less Wright
Modify record