WTL for MFC Programmers, Part IV - Dialogs and Controls

xiaoxiao2021-03-06  191

WTL for MFC Programmers, Part IV - Dialogs and Controls

Original: Michael Dunn [English] Translation: Orbit (orange pendulum) [http://www.winmsg.com/wtl/part4.htm#moreddx]

Download Demonstration Code

This chapter

Introduction to review the ATL dialog box Universal Control Application Wizard to generate a dialog-based program to use control package classes

ATL Way 1 - Connect a CWindow Object ATL Mode 2 - CconTainedWindow ATL Way 3 - Subclassing WTL Way - Dialog Box Data Exchange (DDX) DDX Details

DDX Macro about DODATAExChange () Details Use DDX Processing Controls to send notification messages

Inflection message reflection notification message in response to the control in the parent window

WTL macro used to handle reflection messages, easy to make mistakes and confused

Dialog box font _ATL_MIN_CRT modified record

Introduction to Chapter 4

MFC's dialog box and control packaging is really saving you a lot of time and effort. Without MFC's encapsulation of controls, you have to operate controls to fill in various structures and write a lot of SendMessage calls. The MFC also provides dialog data exchange (DDX), which can transmit data between controls and variables. WTL, of course, also provides these features, and has made a lot of improvements to the control of the control. This article will focus on a dialog-based program to demonstrate your previous use of the MFC implementation, in addition to the enhancement of WTL message processing. The fifth chapter will introduce advanced interface features and wtl's packages for new controls.

Review the ATL dialog

Now review the two dialog boxes mentioned in the first chapter, CDIALOGIMPL and CAXDIALOGIMPL. CAXDIALOGIMPL is used to include a dialog box for ActiveX controls. This article is not prepared to introduce the ActiveX control, so only CDIALOGIMPL is used.

Creating a dialog requires three things:

Creating a dialog resource From a CDIALOGIMPL class to add a new class to add a public member variable IDD, set it to the ID of the dialog resource.

Then add a message processing function like the main frame window, WTL has not changed these, but some other features can be used in the dialog box.

Universal control package

WTL has many controls package classes to be familiar because they use the same name as MFC (or almost the same). The naming of the control method is also the same as the MFC, so you can use these WTL package classes to refer to the MFC document. Insufficient is that the F12 key cannot be easily jumped to the class definition code.

Below is the package class of Windows built-in controls:

User Control: CStatic, CButton, CListBox, CComboBox, CEdit, CScrollBar, CDragListBox common controls: CImageList, CListViewCtrl (CListCtrl in MFC), CTreeViewCtrl (CTreeCtrl in MFC), CHeaderCtrl, CToolBarCtrl, CStatusBarCtrl, CTabCtrl, CToolTipCtrl, CTrackBarCtrl (CSliderCtrl in MFC ), CUpDownCtrl (CSpinButtonCtrl in MFC), CProgressBarCtrl, CHotKeyCtrl, CAnimateCtrl, CRichEditCtrl, CReBarCtrl, CComboBoxEx, CDateTimePickerCtrl, CMonthCalendarCtrl, CIPAddressCtrl MFC no wrapper classes: CPagerCtrl, CFlatScrollBar, CLinkCtrl (clickable hyperlink, available on XP only)

There are also wtl unique classes: CBitmapButton, CChecklistViewCtrl (List control with check selection box), CTreeViewCtrlex and CtreeItem (usually used together, ctreeItem encapsulate htreeItem), ChyperLink (similar to hyperlink objects on the web, support all operations System) Need to pay attention to that most package classes are based on CWindow interfaces. They are the same as CWindow, which encapsulate hwnd and package the control message (for example, Clistbox :: getcursel () encapsulates the LB_GetCursel message). So, like CWindow, create a control package object and associate it with the existing controls, only a few resources, of course, like CWindow, the control package object is not destroyed when the control itself is destroyed. There are also some exceptions, such as CBitmapButton, CchecklistViewCtrl, and ChyperLink.

Since these articles are positioned in experienced MFC programmers, I will not waste time to introduce these packages, they are similar to the corresponding control of the MFC. Of course, I will introduce the new class of WTL: The CBitmapButtoncBitmapButton class is very different from the same name class of MFC, and ChyperLink is completely new.

Build a dialog-based program with application wizards

Run VC and start the WTL Application Wizard, I believe that you have used it when you do a clock program, named ControlMania1 for our new program. In the first page of the wizard, select the dialog-based application, but also to use the Mode dialog or use the Non-Mode dialog. They have a big difference, I will introduce them in Chapter 5, now we choose simple: Mode dialog. Select the Mode dialog box and generate the CPP file option as shown below:

All options on the second page are meaningful when only the main window is a frame window. Now they are unavailable, click "Finish", and then click "OK" to complete the wizard.

As you think, the wizard-creative dialog-based code is very simple. The _twinmain () function is in Controlmania1.cpp, below is an important part:

INT WINAPI _TWINMAIN

Hinstance hinstance, hinstance / * hprevinstance * /,

LPTSTR LPSTRCMDLINE, INT NCMDSHOW)

{

HRESULT HRES = :: Coinitialize (NULL);

AtlinitcommonControls (ICC_COOL_CLASS | ICC_BAR_CLASSES);

HRES = _Module.init (null, hinstance);

INT nret = 0;

// Block: Run Application

{

CMAINDLG DLGMAIN;

NRET = dlgmain.domodal ();

}

_Module.Term ();

:: Couninitialize ();

Return nret;

}

The code first initializes COM and creates a single-threaded apartment, which is necessary to use the ActiveX control dialog, then call the WTL functional function atlinitcommonControls (), this function is the package of InitCommontrolsex (). Global object _Module is initialized, the main dialog is displayed. (Note All the ATL dialogs created using the Domodal () is actually mode, which is not like the MFC, the MFC's dialog boxes are non-mode, the MFC is disabled by the parent window of the dialog to the analog mode dialogue behavior). _Module and COM are released, and the return value of Domodal () is used as the end code of the program. It is important to put CMAINDLG variables in a block because CMAINDLG may have a member using ATL and WTL characteristics, which will also use ATL / WTL characteristics when destructure, if not using blocks, cmaindlg Will call the destruction function to destroy himself (and members) after _Module.Term () (this function is completed ATL / WTL) call, and try to use the ATL / WTL characteristics, which will cause the program to diagnose errors crash. (WTL 3's wizard creative code does not use block, making some of my programs crash at the end)

You can now compile and run this program, although it is just a simple dialog:

CMAINDLG's code handled the message of WM_INITDIALOG, WM_CLOSE, and three buttons. If you like to browse these code, you should be able to understand the statement of CMAINDLG, its message mapping and its message processing function.

This simple project also demonstrates how to link controls and variables, this program uses several controls. In the next discussion, you can come back to see these charts.

Since the program uses the List View control, the call to atlinitCommonControls () needs to be modified, and it is changed to:

AtlinitcommonControls (ICC_WIN95_CLASES);

Although this registered control is much more than we use, but when we add different types of controls to the dialog, you don't have to remember the constant called ICC_ * (translator plus: a series of constants starting with ICC_).

Use control packages

There are several ways to associate a variable and control, you can use CWindows (or other Window interface class, such as ClistViewCtrl), you can also use CWindowImpl derived classes. If you just need a temporary variable, use CWindow if you need a copy of the subclass, you need to use CWINDOWIMPL.

ATL Way 1 - The easiest way to connect a CWindow object is to declare a CWindow or other Window interface class, and then call an attach () method, you can also use the CWindow constructor to associate the variables with the HWND of the control.

The following code links the variables and one LIST control:

HWND HWNDLIST = Getdlgitem (IDC_LIST);

ClistViewCtrl WNDLIST1 (HWNDLIST); // Use Constructionor

ClistViewCtrl WNDLIST2, WNDLIST3;

Wndlist2.attach (hwndlist); // use attach method

WNDLIST3 = hwndlist; // use assignment operator

Remember that CWindow's destructor does not destroy the control window, so it does not need to be separated from the control when the variable exceeds the scope. If you want, you can use it as a member variable: You can build in the OnInitDialog () handler The variable is connected to the control. ATL method 2 - package container window (ccontainedwindow)

CcontainedWindow is class between CWindow and CWindowIMPL, which can subclass control, handle messages for controls in the parent window of the control, which makes all message processing in the dialog class, do not need to be for each The control generates a separate CWINDOWIMPL derived class object. It should be noted that WM_COMMAND, WM_NNNOTIFY, and other notification messages cannot be handled with CContainedWindWind, as these messages are parental windows that are sent to the control.

CContainedWindow is just a data type defined by ccontainedWindowt. CContainedWindowt is a real class. It is a template class that uses the WINDOW interface class as a template parameter. This special ccontainedwindowt

Like the CWindow functionality,

CContainedWindow is just a self-written name it defines. To use a different WINDO interface class, you only need to use the class name of this class as a template parameter, such as CContainedWindowt.

.

Hook a ccontainedWindow object requires four things:

Create a CContainedWindowT member variable in the dialog. Add a message processing to the Alt_MSG_MAP section of the dialog message map. Call the ccontainedWindowt constructor in the constructor of the dialog and tell it which Alt_MSG_MAP section needs to be processed. Call CContainedWindowt :: SubclassWindowT :: SubclassWindow () method is associated with the control in OnInitDialog ().

In Controlmania1, I use a ccontainedWindow for three buttons, and the dialog process is sent to the WM_SETCURSOR message of each button, and change the mouse pointer shape.

Take a closer look at this step, first, we add CContainedWindow members in CMAINDLG.

Class CMAINDLG: PUBLIC CDIALOGIMPL

{

// ...

protected:

CCONTAINEDWINDOW M_WNDOKBTN, M_WNDEXITBTN;

}

Second, we add ALT_MSG_MAP sections, the OK button uses 1 section, the Exit button uses 2 sections. This means that all messages sent to the OK button will be processed by the Alt_MSG_MAP (1) section, all messages sent to the EXIT button will be processed by the Alt_MSG_MAP (2) section.

Class CMAINDLG: PUBLIC CDIALOGIMPL

{

PUBLIC:

BEGIN_MSG_MAP_EX (CMAINDLG)

Message_handler (WM_INITDIALOG, OnInitdialog)

Command_id_handler (ID_APP_ABOUT, ONAPPABOUT)

Command_id_handler (IDOK, ONOK)

Command_id_handler (IDCANCEL, ONCANCEL)

Alt_msg_map (1)

MSG_WM_SETCURSOR (OnsetCursor_ok)

Alt_msg_map (2)

MSG_WM_SETCURSOR (OnsetCursor_exit)

END_MSG_MAP ()

LRESULT ONSETCURSOR_OK (HWND HWNDCTRL, UINT UHITTEST, UINT UMOUSEMSG);

LRESULT ONSETCURSOR_EXIT (HWND HWNDCTRL, UINT UMOTTEST, UINT UMOUSEMSG);

Next, we call each CContainedWindow constructor that tells it which section of the ALT_MSG_MAP.

CMAINDLG :: CMAINDLG (): m_wndokbtn (this, 1),

m_wndexitbtn (this, 2)

{

}

The parameter of the constructor is the address map chain address and the bass number of the alt_msg_map. The first parameter usually uses this, which is the use of the dialog box's own message mapping chain, and the second parameter tells the object which section of the message to Alt_MSG_MAP.

Finally, we associate each CContainedWindow object with the control.

LResult cmaindlg :: oninitdialog (...)

{

// ...

// attach ccontainedwindows to ok and exit button

M_Wndokbtn.SubclassWindow (Getdlgitem (IDOK));

M_Wndexitbtn.SubclassWindow (Getdlgitem (IDCancel));

Return True;

}

Below is a new WM_SETCURSOR message handler:

Lresult cmaindlg :: Onsetcursor_ok (HWND HWNDCTRL, UINT UHITTEST, UINT UMOUSEMSG)

{

Static hcursor hcur = loadingcursor (null, idc_hand);

IF (NULL! = HCUR)

{

SetCursor (HCUR);

Return True;

}

Else

{

Setmsghand (false);

Return False;

}

}

Lresult cmaindlg :: Onsetcursor_exit (HWND HWNDCTRL, UINT UHITTEST, UINT UMOUSEMSG)

{

Static hcursor hcur = loadingcursor (null, idc_no);

IF (NULL! = HCUR)

{

SetCursor (HCUR);

Return True;

}

Else

{

Setmsghand (false);

Return False;

}

}

If you still want to use the feature of the button, you need this to declare the variable:

CCONTAINEDWINDOWT

m_wndokbtn;

This allows the CBUTTON class.

When you move your mouse light to these buttons, you can see the results of the WM_SETCURSOR message processing function:

ATL method 3 - subclass (SUBCLASSIN)

The third method creates a CWINDOWIMPL derived class and uses it to type a control. This is a bit similar to the second method, but the message processing is placed inside the CWindowIMPL class instead of the dialog box class.

Controlmania1 uses this method subclassized master dialog box from the About button. Below is the CButtonImpl class, he deals from the CWindowImpl class, handles WM_SETCURSOR message:

Class CbuttonImpl: Public CWINDOWIMPL

{

Begin_msg_map_ex (CButtonImpl)

MSG_WM_SETCURSOR (ONSETCURSOR)

END_MSG_MAP ()

LResult OnSetcursor (HWND HWNDCTRL, UINT UHITTEST, UINT UMOUSEMSG)

{

Static hcursor hcur = loadingcursor (null, idc_sizeall); if (null! = hcur)

{

SetCursor (HCUR);

Return True;

}

Else

{

Setmsghandled (

FALSE);

Return False;

}

}

}

Then a CButtonImpl member variable is declared at the master dialog:

Class CMAINDLG: PUBLIC CDIALOGIMPL

{

// ...

protected:

CCONTAINEDWINDOW M_WNDOKBTN, M_WNDEXITBTN;

CButtonImpl M_WndaboutBTN;

}

Finally, in the OnInitDialog () seed classification.

LResult cmaindlg :: oninitdialog (...)

{

// ...

// attach ccontainedwindows to ok and exit button

M_Wndokbtn.SubclassWindow (Getdlgitem (IDOK));

M_Wndexitbtn.SubclassWindow (Getdlgitem (IDCancel));

// CButtonImpl: Subclass The About Button

M_wndaboutbtn.subclassWindow (Getdlgitem (ID_APP_ABOUT));

Return True;

}

WTL mode - dialog data exchange (DDX)

WTL's DDX (dialog data exchange) is very like MFC, you can use very simple ways to associate variables and controls. First, as in the previous example, you need to derive a new class from CWindowImpl, this time we use a new class CEDITIMPL, because this time we use the Edit control. You also need to add #include atlddx.h into stdafx.h so you can use DDX code.

To make the main dialog support DDX, you need to add CWindataExchange to the inheritance list:

Class CMAINDLG: PUBLIC CDIALOGIMPL

,

Public CWINDATAEXCHANGE

{

// ...

}

Then add a DDX chain in the dialog class, which is similar to the DODataExchange () function function using the MFC's class wizard. For different types of data, you can use different DDX macros, we use DDX_Control to connect to variables and controls, this time we use CeditIMPL to process WM_CONTEXTMENU messages so that it can do something when you right-click control.

Class CeditiMPL: Public CWINDOWIMPL

{

BEGIN_MSG_MAP_EX (CEDITIMPL)

MSG_WM_CONTextMenu (OnContextMenu)

END_MSG_MAP ()

Void OnContextMenu (HWND HWNDCTRL, CPOINT PTTClick)

{

Messagebox

"Edit Control Handled WM_ContextMenu");

}

}

Class CMAINDLG:

Public CDialogIMPL

,

Public CWINDATAEXCHANGE

{

// ...

Begin_DDX_MAP (CMAINDLG)

DDX_CONTROL (IDC_EDIT, M_Wndedit)

END_DDX_MAP ()

protected:

CCONTAINEDWINDOW M_WNDOKBTN, M_WNDEXITBTN;

CButtonImpl M_WndaboutBTN;

CEDITIMPL M_WNDIT;

Finally, call the DODATAEXCHANGE () function in OnInitDialog (), this function is inherited from CWindataExchange. DODATAEXCHANGE () The subclass of the relevant control is first called, so in this example, the DODataExchange () subclass ID is an IDC_EDIT control, which is associated with M_Wndedit.

LResult cmaindlg :: oninitdialog (...)

{

// ...

// attach ccontainedwindows to ok and exit button

M_Wndokbtn.SubclassWindow (Getdlgitem (IDOK));

M_Wndexitbtn.SubclassWindow (Getdlgitem (IDCancel));

// CButtonImpl: Subclass The About Button

M_wndaboutbtn.subclassWindow (Getdlgitem (ID_APP_ABOUT));

// First Ddx Call, Hooks Up Variables to Controls.

DODATAEXCHANGE (FALSE);

Return True;

}

The parameters of DODATAEXChange () are the same as the parameters of the MFC's updatedata () function, I will introduce in the next section.

Now running the Controlmania1 program, you can see the effect of subclass. Right-click the Edit box to pop up the message box, when the mouse is on the button, the mouse shape changes.

Detailed DDX

Of course, DDX is used to do data exchange, WTL supports exchange data between the EDIT control and string, or resolves strings into numbers, converts to integer or floating-point variables, and supports Check Box and Radio Button. The state of the group and the conversion between INT type variables.

DDX macro

DDX can use 6 macros, each macro corresponding to a CWindataExchange class to support its work, each macro uses the same form: DDX_FOO (Control ID, variable), each macro can support multiple types Variables, such as DDX_Text's overload supports multiple types of data.

DDX_Text

Transferring data between strings and Edit Box controls, variable types can be CString, BSTR, CCOMBSTR, or static assignment string array, but cannot use NEW dynamically assigned an array.

DDX_INT

Transfer INT data between the Edit Box control and the digital variable.

DDX_UINT

Transfer no symbol INT data between the Edit Box control and the digital variable.

DDX_FLOAT

Transfer floating point (FLOAT) data or double-precision data (double) between the Edit Box control and the digital variable.

DDX_CHECK

Convert the status of the Check Box control between the Check Box control and the INT type variable.

DDX_RADIO

Convert the status of the Radio Buttons control group between the Radio button and INT variable.

DDX_FLOAT macro has some special, to use DDX_FLOAT macros to add a line definition before all WTL header files of the stdafx.h file:

#define _atl_use_ddx_float

This definition is necessary because the default status is to optimize the size of the program without supporting the floating point number.

Details about DODATAEXChange ()

Call the DODATAEXChange () method, like Updatedata () in the MFC, the function prototype of DODATAEXChange () is:

Bool DodataExchange (Bool BsaveandValidate = false, uint nctlid = (uint) -1); parameter:

BsaveAndvalidate

Indicates the flag of the data transfer direction. True means that the data is transferred from the control to the variable, and false means transmitting data from variables to the control. It is necessary to pay attention to the default value of this parameter is False, and the default value of the MFC's updatedata () function is true. For your convenience, you can use DDX_save and DDX_LOAD labels (they are defined as TRUE and FALSE).

Nctlid

Use -1 to update all controls, if you just want DDX macro to use the Control ID using the control.

If the control update success dodataExchange () will return true if the failed is returned, and the dialog box class has two overload functions to handle data exchange errors. One is onDataExchangeError (), no matter what reason, this function will call this function. The default implementation of this function is in CWindataExchange. It is just a buzzing of the PC speaker and set the error control to the current focus. Another function is onDataValidateError (), but it is used to get DDV to the fifth chapter of this article.

Use DDX

Add several variables to cmaindlg, demonstrating how DDX is used.

Class CMAINDLG: PUBLIC ...

{

// ...

Begin_DDX_MAP (CMAINDLG)

DDX_CONTROL (IDC_EDIT, M_Wndedit)

DDX_Text (IDC_EDIT, M_SEDITCONTENTS)

DDX_INT (IDC_EDIT, M_NeditNumber)

END_DDX_MAP ()

protected:

// ddx variables

CString M_SEDITCONTENTS;

INT m_NEDITNUMBER;

}

In the processing function of the OK button, we first call DODATAEXChange () will transfer the data of the Edit control to the two variables we just added, and then display the results in the list control.

Lresult cmaindlg :: Onok (uint ucode, int nid, hwnd hwndctl)

{

CString Str;

// Transfer Data from The Controls to Member Variables.

IF (! DODATAEXCHANGE (TRUE))

Return;

m_wndlist.deleteallItems ();

m_wndlist.insertitem (0, _T ("ddx_text"));

M_WndList.SetItemText (0, 1, m_seditcontent);

Str.Format (_T ("% d"), m_neditnumber;

M_WndList.InsertItem (1, _t ("ddx_int"));

m_wndlist.setitemtext (1, 1, str);

}

If the edit control is not digital, DDX_INT will fail and trigger the call of OONDATAEXChangeError (), and CMAINDLG is overloaded with the onDataExchangeError () function display a message box:

Void CMAINDLG :: OndataExchangeError (uint nctrlid, bool bsave)

{

CString Str;

Str.Format ("DDX ERROR DURING Exchange with Control:% U"), NCTRLID;

MessageBox (STR, _T ("Controlmania1"), MB_ICONWARNING); :: setfocus (getdlgitem (nctrlid));

}

As a last example of using DDX, we add a Check Box demo DDX_CHECK to use:

The variable type used by DDX_CHECK is an INT type, which may be 0, 1, 2, corresponding to the unselected state, select state, and uncertain state of the Check Box, respectively. You can also use constant BST_unchecked, bst_checked, and bst_indeeterminate instead, for Check Box only, you can treat it as a Boolean variable for Check Box.

The following is a change to use the CHECK BOX DDX:

Class CMAINDLG: PUBLIC ...

{

// ...

Begin_DDX_MAP (CMAINDLG)

DDX_CONTROL (IDC_EDIT, M_Wndedit)

DDX_Text (IDC_EDIT, M_SEDITCONTENTS)

DDX_INT (IDC_EDIT, M_NeditNumber)

DDX_CHECK (IDC_SHOW_MSG, M_NSHOWMSG)

END_DDX_MAP ()

protected:

// ddx variables

CString M_SEDITCONTENTS;

INT m_NEDITNUMBER;

INT M_NSHOWMSG;

}

At the end of Onok (), check if the value of M_NShowMsg looks at whether the check box is selected.

Void CMAINDLG :: Onok (uint ucode, int nid, hwnd hwndctl)

{

// Transfer Data from The Controls to Member Variables.

IF (! DODATAEXCHANGE (TRUE))

Return;

// ...

IF (M_NShowMSG)

Messagebox (_T ("DDX Complete!"), _T ("Controlmania1"),

MB_ICONINFORMATION);

}

An example code using other DDX_ * macros is included in the example project.

Handling the notification message sent by the control

Processing notification messages in WTL are similar to using API mode, the control sends a notification event to the parent window in a way in WM_COMMAND or WM_NOTIFY messages, and the parent window is correspondingly processed. A few other messages can also be seen as a notification message, such as: wm_drawitem, when a self-drawing control needs to draw itself, the message will be sent this message, and the parent window can handle this message, and then reflect it to the control, MFC adopts That is, the message reflection is made, so that the control can handle the notification message, improve the encapsulation and reusability of the code.

In response to the notification message of the control in the parent window

The notification message sent in the form of WM_NOTIFY and WM_COMMAND messages contains various information. The parameters of the WM_COMMAND message include the control ID, control of the control message, the control window handle, and the notification code, the parameters of the WM_NOTIFY message also contain a pointer to the NMHDR data structure. ATL and WTL have various message mapping macro to handle these notification messages, I only introduce WTL macros here, because this article is WTL. Use these macros to use Begin_MSG_MAP_EX in the message map chain and contain the ATLCRACK.H file.

Message map macro

To handle the WM_COMMAND notification message, you need to use Command_Handler_ex macros:

Command_handler_ex (ID, Code, FUNC)

Processing a certain notification code from a control. Command_id_handler_ex (id, func)

Processing all the notification code from a control.

Command_code_handler_ex (code, func)

Handle a notification code to get all messages, whether it is from that control.

Command_Range_Handler_EX (IDFirst, Idlast, FUNC)

Processing all the notification code sent by Idfirst and IDLAST.

Command_Range_code_handler_ex (IDFirst, Idlast, Code, FUNC)

Processing the ID to which a certain notification code sent between IDFirst and IDLAST.

example:

Command_handler_ex (idc_username, en_change, onusernamechange): Processing an en_change notification message from the ID is an Edit Box control from IDC_USERNAME. Command_id_handler_ex (idok, onok): Processing the ID is all the notification messages sent by the IDOK. Command_range_code_handler_ex (idc_monday, idc_friday, bn_clicked): Processing the bn_clicked notification message sent between IDC_MOMONDAY and IDC_FRIDAY.

There are also some macros to deal with WM_NOTIFY messages, similar to the above macro function, just their names begin with "notify_" instead of "Command_".

The prototype of the WM_COMMAND message handler is:

Void Func (Uint Ucode, Int NCTRLID, HWND HWNDCTRL);

The WM_COMMAND notification message does not need to return a value, so the handler does not need to return a value, the prototype of the WM_NOTIFY message processing function is:

LRESULT FUNC (NMHDR * PHDR);

The return value of the message processing function is used as a message corresponding to the return value, which is different from the MFC, and the MFC message response is returned by the LRESULT * parameter of the message processing function. The window handle and notification code for the control of the sending notification message is included in the NMHDR structure, namely Code and HendFrom members, respectively. As the MFC is that if the notification message is not a normal NMHDR structure, your message handler should convert the PHDR parameter into the correct type.

We will add a LVN_ItemChanged notification to cmaindlg to handle this notification from the List control, display the currently selected item in the dialog box, start with the addition message mapping macro and message processing function:

Class CMAINDLG: PUBLIC ...

{

BEGIN_MSG_MAP_EX (CMAINDLG)

Notify_handler_ex (idc_list, lvn_itemchanged, onlistItemchange)

END_MSG_MAP ()

LResult OnListItemChanged (NMHDR * PHDR);

// ...

}

Here is the message processing function:

Lresult cmaindlg :: OnListItemChanged (nmhdr * phdr)

{

NmlistView * pnmlv = (nmlistview *) PHDR;

INT NSELITEM = m_wndlist.getSelectedIndIndex ();

Cstring smsg;

// if no item is selected, show "none". OtherWise, Show ITS INDEX.

IF (-1 == NSELITEM)

SMSG = _T ("(none));

Else

SMSG.Format (_T ("% d"), nSelitem); setdlgitemtext (IDC_SEL_ITEM, SMSG);

Return 0; // RetVal ignored

}

This processing function has not been used to PHDR parameters, I enforce him to nmlistView * just to demonstrate usage.

Reflection notification message

If you are a derived classes that use CWindowImpl, such as the CEDITIMPL used earlier, you can process the notification message instead of the category instead of in the dialog box, this is the reflection of the notification message, which is similar to the Message reflection of the MFC. Different is that the WTL Macro window and control can handle the notification message, while only the control can handle the notification message in the MFC (the translator plus: unless you overload the WindowProc function, intercept them before the MFC reflects these messages).

If you need to reflect the notification message to the control package class, simply add a reflect_notifications () macro in the message map chain of the dialog:

Class CMAINDLG: PUBLIC ...

{

PUBLIC:

BEGIN_MSG_MAP_EX (CMAINDLG)

// ...

Notify_handler_ex (idc_list, lvn_itemchanged, onlistItemchange)

Reflect_notifications ()

END_MSG_MAP ()

}

This macro message map chain adds some code to handle notification messages that are not previously macros, it checks if the HWND window handle is valid and forwards the message to this window, of course, the value of the message code is changed to OLE The value used by the control, the OLE control has a similar message reflecting system. The new message code value replaces WM_XXX with OCM_xxx, but the processing method of the message is the same as before the reflection.

There are 18 reflected news:

Control notification message: WM_COMMAND, WM_NOTIFY, WM_PARENTNOTIFY self-portraits message: WM_DRAWITEM, WM_MEASUREITEM, WM_COMPAREITEM, WM_DELETEITEM List box keyboard message: WM_VKEYTOITEM, WM_CHARTOITEM other: WM_HSCROLL, WM_VSCROLL, WM_CTLCOLOR *

Don't forget to use the default_reflection_handler () macro if you want to add a reflected message processing. The following example is a self-drawn button class, which corresponds to the WM_DRAWITEM message reflected from the parent window.

Class CodbuttonImpl: Public CWINDOWIMPL

{

PUBLIC:

BEGIN_MSG_MAP_EX (CODBUTTONIMPL)

MSG_OCM_DRAWITEM (ONDRAWITEM)

Default_reflection_handler ()

END_MSG_MAP ()

Void OnDrawItem (uint idctrl, lpdrawitemstruct lpdis)

{

// Do Drawing Here ...

}

}

WTL macro used to deal with reflection messages

We only see a WTL message reflecting a macro: MSG_OCM_DRAWITEM, and 17 such reflection macro. Since the parameters of the WM_Notify and the WM_Command message need to be expanded, WTL provides special macro MSG_OCM_COMMAND and MSG_OCM_NOTIFY to do these things. These macros have the same work as command_handler_ex and notify_handler_ex macro, but "Reflected_" in front, for example, a tree control class may exist such a message mapping chain: Class CMYTREECTRL: Public CWindowImpl

{

PUBLIC:

BEGIN_MSG_MAP_EX (CMYTREECTRL)

Reflected_notify_code_handler_ex (TVN_ItemExpanding, OnItemXPanding)

Default_reflection_handler ()

END_MSG_MAP ()

LRESULT OnItemExpanding (NMHDR * PHDR);

}

In the Controlmania1 dialog box, use a tree control with the above code, and the member M_Wndtree of the CMAINDLG class m_wndtree uses DDX to connect to the control, the cmaindlg reflects the notification message, the processing function of the tree control overItemExpanding () is:

LResult CbuffyTreeCtrl :: OnItemExpanding (NMHDR * PHDR)

{

NMTREEVIEW * PNMTV = (NMTreeView *) PHDR;

IF (PNMTV-> Action & TVE_COLLLAPSE)

Return True; // Don't Allow IT

Else

Return False; // Allow IT

}

Run Controlmania1, click on the /- button on the tree control with the mouse, you will see the role of the message processing function - the node will not be folded again.

Easy to make mistakes and confused places

Dialog box font

If you are very paying attention to the interface like me and is using Windows 2000 or XP, you will be strange why the dialog box uses the MS Sans Serif font instead of the Tahoma font, because VC6 is too old, it generates the resource file on NT 4 Work is very good, but there is a problem for new versions. You can modify yourself, you need to manually edit the resource file, according to what I know VC 7 does not exist.

Three places need to be modified at the entrance to the dialog box in the resource file:

Dialog Type: Change Dialog to DialoGex Window Type: Add DS_SHELLFONT dialog box: Change MS Sans Serif to MS Shell DLG

Unfortunately, the first two modifications will be lost at each saving resource file (the VC is changed back to the original), so it is necessary to repeat these modifications. The following is the code before the change:

IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 187, 102

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

Caption "About"

Font 8, "MS Sans Serif"

Begin

...

End

This is the code after the change:

IDD_ABOUTBOX DIALOGEX DISCARDABLE 0, 0, 187, 102

STYLE DS_SHELLFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

Caption "About" Font 8, "MS Shell DLG"

Begin

...

End

After this, the dialog box will use Tahoma fonts on a new operating system, while the MS Sans Serif font is still used on the old operating system.

_TL_MIN_CRT

This article FAQ has been explained, and the optimization settings containing the ATL allow you to create a program that does not use the C Runault (CRT), using this optimization to add a _atl_min_crt label in the pre-processing setting, the code is generated in the Release configuration This optimization is used by default. Since I will always use the CRT function, I always remove this label. If you use the floating point calculation characteristics in the CSTRING class or DDX, you have to remove this label.

carry on

In Chapter 5, I will introduce dialog data validation (DDV), WTL's package and self-drawing controls for new controls, custom appearance controls, etc. Some advanced interface features.

Modify record

On April 27, 2003, this article was first published.

转载请注明原文地址:https://www.9cbs.com/read-128793.html

New Post(0)