WTL for MFC Programming Practice --- A custom ComboBox transplantation process (on)

zhaozj2021-02-16  104

WTL for MFC Programming Practice --- A Custom ComboBox porting process

--- Snail Hand

Now there is a MFC written custom ComboBox intended to the WTL, so according to the WTL writing method, you get the following code:

Class CCOMBOBOXEX: PUBLIC CCOMBOBOX

{

protected:

Void OnDrawItem (Uint WPARAM, LPDRAWITEMSTRUCT LPDRAWITEMSTRUCT);

PUBLIC:

BEGIN_MSG_MAP_EX (CCOMBOBOXEX)

MSG_OCM_DRAWITEM (ONDRAWITEM)

END_MSG_MAP ()

}

Class CMAINDLG: Public CDialogIMPL

{

Protected:

CCOMBOBOXEX M_CMBEX;

PUBLIC:

Begin_DDX_MAP (CPAGECONFIGFONT)

DDX_Control_Handle (IDC_COMBOBOXEX, M_CMBEX);

END_DDX_MAP ()

Begin_msg_map_ex (CPAGEConfigfont)

MSG_WM_INITDIALOG (OnInitDialog)

Reflect_notifications ()

END_MSG_MAP ()

}

How to generate the meaning of the above code and code, the original book is introduced, because it is not the focus of this article, no longer explain one by one.

What to say is, add DDX_Control_Handle macro to WTL 7.1, can be used to set controls, unlike DDX_Control, it does not require control class by cwindowimpl, that is, there is no need to include the subclasswindow () function, so we can use DDX To set our class from CCOMBOBOX (it makes sense, it is actually the error thinking in MFC programming habits).

Of course, to implement there is a small problem, DDX_Control_Handle Macro requires our class contains an operator "=", how to write this function? See the realization method of the base class:

CCOMBOBOXEXT & Operator = (HWND HWND)

{

m_hwnd = hwnd;

RETURN * THIS;

}

See WTL file

It turns out that only M_HWnd is assigned, so we add the following code in our class:

CCOMBOBOXEX & OPERATOR = (hwnd hwnd)

{

m_hwnd = hwnd;

RETURN * THIS;

}

So compiled. (As everyone knows potential mistakes, this is deeply buried)

But why DDX_Control_Handle Macro requires our class contains an operator "="? Let's take a look at how ddx_control_handle macro is implemented:

The whole DDX_MAP is actually defined a DODATAEXCHANGE function, the begin_ddx_map macro defines the function header, and the end_ddx_map defines the specific content of the DDX definition function of the function tail, the middle one item, and when you define DDX_map in the code, it is equal to overload The CWindataExchange :: DODATAEXCHANGE () function, the specific code is as follows:

#define begin_ddx_map (thisclass) /

Bool DodataExchange (Bool BsaveAndValidate = false, uint nctlid = (uint) -1) / {/

BsaveAndValidate; /

Nctlid;

#DEFINE END_DDX_MAP () /

Return True; /

}

See WTL file

For DDX_Control_Handle Macros, it is actually called the CWindataExchange :: DDX_Control_Handle function, the specific code is as follows:

// Simple Control Attaching (for HWnd Wrapper Controls)

Template

Void DDX_Control_Handle (uint Nid, Tcontrol & Ctrl, Bool Bsave)

{

IF (! BSAVE && Ctrl.m_hwnd == NULL)

{

T * pt = static_cast (this);

Ctrl = pt-> getdlgitem (NID);

}

}

See WTL file

As the code above, the DDX_Control_Handle Macro directly assigns the form handle corresponding to the ID to the control class linked to the DDX, so our statement defined in DDX_MAP is equivalent to the following statement:

m_cmbex = this-> getdlgitem (IDC_COMBOBOXEX);

So if you want to use the above statement to use, the overload operator becomes a good way to solve the problem, this is the reason for the DDX_Control_Handle Macro requires our class contains the operator "=".

Here, we already know why, and do what should be done, and the task of transplantation is left. Of course, if you are familiar with WTL or look at the code above, you will find a big problem lurking. However, we are the programmers of MFC, habits to think with MFC, so we are strange things happened during testing.

Running everything is normal, just we don't run in the code in the redraint function, in other words, it is not triggered.

why? All of our code is written in accordance with the correct way, add the MSG_OCM_DRAWITEM macro map of CCOMBOBOXEX, add a reflect_notifications () macro in the cmaindlg MSG_MAP.

It's doing it. Why can't it?

In the original book, use default_reflection_handler to handle the default reflection event, is it missing this macro? Although this is not a logical idea, it is now a medical doctor who will do it.

So we added this macro in the original class, and the result was wrong, prompting the definition of the defaultReflectionHandler function, oh? what does this mean? Let's investigate the original code:

#define default_reflection_handler () /

IF (DefaultReflectionHandler (HWND, UMSG, WPARAM, LPARAM, LRESULT)) /

Return True;

See the ATL file

The original default_reflection_handler macro is only called the DefaultReflectionHandler function, then this function is it? DefaultReflectionHandler CWindowImplRoot is a member function, but also can be said to be a member function CWindowImpl because CWindowImpl derived from the CWindowImplBase, and CWindowImplBase derived from the CWindowImplRoot, DefaultReflectionHandler function is actually a package of DefWindowProc API function, but it is limited to the event processing OCM_. As of the following code: Template

Bool CWindowImplroot :: DefaultReflectionHandler (HWND HWND, UINT UMSG, WPARAM WPARAM, LRESULT & LRESULT)

{

Switch (UMSG)

{

Case OCM_COMMAND:

Case OCM_NOTIFY:

Case Ocm_ParentNotify:

Case OCM_DRAWITEM:

Case OCM_MEASUREITEM:

Case OCM_CompareItem:

Case OCM_DELETEITEM:

Case OCM_VKEYTOITEM:

Case OCM_CHARTOITEM:

Case OCM_HSCROLL:

Case OCM_VSCROLL:

Case OCM_CTLCOLORBTN:

Case OCM_CTLCOLORDLG:

Case OCM_CTLCOLOREDIT:

Case OCM_CTLCOLORLISTBOX:

Case OCM_CTLCOLORMSGBOX:

Case OCM_CTLCOLORSCROLLBAR:

Case OCM_CTLCOLORSTATIC:

LRESULT = :: DefWindowProc (HWND, UMSG - OCM__BASE, WPARAM, LPARM);

Return True;

DEFAULT:

Break;

}

Return False;

}

See here, if you want to add a default_reflection_handler macro, the control class will be derived from CWindowImpl. In order to test the idea of ​​the death horse as a living horse doctor, we change the definition of the class to the following:

Class CCOMBOXEX: Public CWindowImpl

So, the addition of the default_reflection_handler's macro operation passed compilation, but it turns out that the unreasonable idea is difficult to bring the correct result, not only the heavy-in event is not triggered, after modification, the ATL's assertion is encountered when the control class sectors.

The error message is that the class is destructed before the form handle is destroyed.

This error allows us to think of a WTL feature mentioned in the original book, WTL does not automatically sell the form handle and needs to manually Detach () form handles. In this case, we added the following code:

~ CcomboBOXEX () {

Detach ();

}

Although it is a bit blamed, but after all, the assertion of ATL will not appear. However, the problem is not resolved, and the heavy painting event is still not triggered. Is it CMAINDLG without reflection incident? Check out the code for reflect_notifications macro used to reflect the event:

#define reflect_notifications () / {/

Bhandled = true; /

LResult = ReflectNotifications (UMSG, WPARAM, LPARAM, BHANDED); /

IF (bhandled) /

Return True; /

}

See the ATL file

Reflect_notifications macro is a function CWINDOWIMPLROOT :: ReflectNotification. This function acquires the form handle of the event control through the parameter, and send the event to the control through the handle, the code is as follows:

Template

LResult CwindowImplRoot :: ReflectNotifications (Uint UMSG, WPARAM WPARAM, LPARAM LPARAM, BOOL & BHAND)

{

HWND HWNDCHILD = NULL;

Switch (UMSG)

{

Case WM_COMMAND:

IF (lparam! = null) // NOT from a menu

HWNDCHILD = (hwnd) lparam;

Break;

Case WM_NOTIFY:

HWNDCHILD = ((lpnmhdr) LParam) -> hwndfrom;

Break;

Case WM_ParentNotify:

Switch (loword (wparam))

{

Case WM_CREATE:

Case WM_DESTROY:

HWNDCHILD = (hwnd) lparam;

Break;

DEFAULT:

HWNDCHILD = Getdlgitem (HiWord (WPARAM));

Break;

}

Break;

Case WM_DRAWITEM:

IF (wparam) // NOT from a menu

HWNDCHILD = (LPDRAWITEMSTRUCT) LPARAM -> HWndItem;

Break;

Case WM_MeasureItem:

IF (wparam) // NOT from a menu

HWNDCHILD = Getdlgitem ((lpMeasureItemstruct) LPARAM) -> CTLID);

Break;

Case WM_CompareItem:

IF (wparam) // NOT from a menu

HWNDCHILD = Getdlgitem ((lpCompareItemstruct) lparam) -> CTLID);

Break;

Case WM_DELETEITEM:

IF (wparam) // NOT from a menu

HWNDCHILD = Getdlgitem ((LPDeleteItemstruct) LPARAM -> CTLID);

Break;

Case WM_VKEYTOITEM:

Case WM_CHARTOITEM:

Case WM_HSCROLL:

Case WM_VSCROLL:

HWNDCHILD = (hwnd) lparam;

Break;

Case WM_CTLCOLORBTN:

Case WM_CTLCOLORDLG:

Case WM_CTLCOLOREDIT:

Case WM_CTLCOLORLISTBOX:

Case WM_CTLCOLORMSGBOX:

Case WM_CTLCOLORSCROLLBAR:

Case WM_CTLCOLORSTATIC:

HWNDCHILD = (hwnd) lparam;

Break;

DEFAULT: BREAK;

}

IF (hwndchild == null)

{

BHANDLED = false;

Return 1;

}

Atlassert (:: iswindow (hwndchild));

Return :: SendMessage (HWNDCHILD, OCM__BASE UMSG, WPARAM, LPARAM);

}

See the ATL file

What we are interested is the last sentence, the control is received by ID = OCM__BASE WM_DRAWITEM, then we can let the control directly receive messages (OCM__BASE WM_DRAWITEM) for replacing the MSG_OCM_DRAWITEM that does not work. So the following code:

Message_handler_ex (OCM__BASE WM_DRAWITEM, ONDRAWITEM)

But the result is still the same - heavy draw events are not triggered.

Fortunately, we have a new discovery, otherwise it may not solve this problem by confidence. We added a WM_DrawItem event in CMAINDLG, and the result captured the CCOMBOBOXEX's heavy-in event. This shows that CCOMBOXEX's redrack event is issued, but I don't know why there is no reflection control. So we added it in cmaindlg :: onDrawItem ()

SendMessage (m_cmbex.m_hwnd, ocm__base wm_drawitem, 0, 0)

The automatic reflection made by replacing the Reflect_Notifications macro, and the results have not been received. Is the WTL event handled a problem? We added a non-reflected event WM_Paint for CCOMBOBOXEX, and the result found that the WM_PAINT event was not triggered! ! !

CCOMBOBOXEX cannot receive any events! ! ! ! !

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

New Post(0)