User interface element update principle in MFC

zhaozj2021-02-16  43

Everyone must encounter this situation in the process of programming: It is necessary to set whether the menu item is selected according to the value of a certain variable, and the setting toolbar button is pressed or displayed in the status bar.

MFC

A mechanism is provided to help us complete this work:

Classwizard

Add one for the corresponding menu item or toolbar button

UPDATE_COMMAND_UI

Processing function, in which

CCMDUI :: SetCheck

The state of the function of these user interface elements can be set. but

MFC

How did this function?

Let us first take a look at the implementation method of menu status update. First of all, we must know that when you have a menu item with a submenu (such as "file" on the menu bar), send a WM_INITMENUPOPUP to the window with this menu, below is the default processing of the MFC on this message: Void CFrameWnd :: OnNITMenupopup (cmenu * pmenu, uint nindex, bool bsysmenu) {

/ / In order to explain the problem, I omitted a lot of code.

CCMDUI State;

State.m_pmenu = pmenu;

State.m_nindexmax = pmenu-> getMenuItemCount ();

FOR (state.m_nindex = 0; state.m_nindex

State.m_nid = pmenu-> getMenuItemID (state.m_nindex);

IF (state.m_nid == (uint) -1)

{

// m_nid == - 1 indicates that there is also a popup menu (a menu item with the right arrow),

// It doesn't automatically deisable

}

Else

{

State.m_psubmenu = null;

State.doupdate (this, m_bautomenuenable && state.m_nid <0xf000);

}

}

Below is the code of ccmdui :: doupdate:

Bool ccmdui :: Doupdate (ccmdtarget * ptarget, bool bdisableifnohndler) {

m_benablechanged = false;

Bool Bresult = ptarget-> oncmdmsg (m_nid, cn_update_command_ui,

this, null;

IF (bdisableifnohndler &&! m_benablechanged) {

AFX_CMDHANDLERINFO INFO;

Info.ptarget = NULL;

Bool bhandler = ptarget-> oncmdmsg (M_NID, CN_COMMAND, THIS, & INFO);

Enable (bhandler);

}

Return BRESULT;

}

Doupdate's process is: First send a cn_update_command_ui command message to your menu item, let your menu item to display the update before display, this is the update_commadn_ui message you can see in ClassWizard, you add the handler is this Time called. If you have handled CN_UPDATE_COMMAND_UI, then m_benablechange turns True, and then return directly. Otherwise, if BDISABLEIFNOHNDLER is also true, send a CN_COMMAND message to the menu item, if you don't respond to this message, this menu item has not handled a function, then BnHandler is Flase, then enable (false) put your menu Item is getting gray. Note that the parameters when calling DOUPDATE in cframeWnd :: OnNitMenupup is m_bautomenuenable && stat, which means if you set m_bautomenuenable to false, in fact, the MFC automatic Diable does not have a menu item for the function. Features. The update of the toolbar is another way. First, you need to know that when your program becomes idle, there is no message that needs to be handled, the MFC will call the cwinapp :: onIdle function to use this time to make some special work, one is to update your toolbar and status bar. Let's see the relevant code:

Bool Cwinthread :: OnIdle (long lcount) {

IF (lcount <= 0) {

// Send a WM_IdleUpdateCMDUI message to the main window and all the sub-windows, this message indicates the receiving window to update the operation.

CWND * PMainWnd = m_pmainwnd;

IF (PMainWnd! = null && pmainwnd-> m_hwnd! = null&&

PMainWnd-> iswindowvisible ())

{

AFXCallWndProc (PMainWnd, PMainWnd-> M_HWND,

WM_IdleUpdatecmdui, (WPARAM) TRUE, 0);

PMainWnd-> SendMessageTodescendants

(WM_IdleUpdatecmdui, (WPARAM) True, 0, True, True;

}

/ / Next, all Frame Window created to this thread Send WM_IdleUpdateCmDui messages

AFX_MODULE_THREAD_STATE * PSTATE =

_AFX_CMDTARGET_GETSTATE () -> m_thread;

CframeWnd * pframewnd = pstate-> m_framelist;

While (pframewnd! = null) {

IF (pframewnd-> iswindowvisible () || pframeWnd-> m_nshowdelay> = 0) {

AFXCallWndProc (pframewnd, pframewnd-> m_hwnd,

WM_IdleUpdatecmdui, (WPARAM) TRUE, 0);

PframeWnd-> SendMessageTodescendants (WM_IdleUpdatecmdui,

(Wparam) True, 0, True, True;

}

}

}

}

Your Toolbar or Statusbar is always a sub-window of a Frame Window (including sub-window ...), so it will definitely receive a WM_IdleUpdateCmDui message. Ctoolbar and cstatusbar are derived from ccontrolbar, below CControlbar's processing for this message: LRESULT CCONTROLBAR :: OnIdleUpdatecmdui (WPARAM WPARAM, LPARAM)

{

IF ((GetStyle () & ws_visible))

{

// Point ptarget to this closest Father Window

CframeWnd * ptarget = (cframewnd *) getowner ();

IF (ptarget == null ||! ptarget-> isframeWnd ())

Ptarget = getParentFrame ();

// Call the virtual member function onupdatecmdui

IF (Ptarget! = NULL)

Onupdatecmdui (Ptarget, (Bool) WPARAM;

}

Return 0L;

}

OnupdateCmDui is a pure virtual function of the CControlbar class, and this function is defined in CToolbar:

Void ctoolbar :: onupdatecmdui (cframeWnd * ptarget, bool bdisableifnohndler) {

CToolcmdui State;

State.m_pother = this;

State.m_nIndexmax = DefWindowProc (TB_ButtonCount, 0, 0); / / The number of buttons on the toolbar

FOR (state.m_nindex = 0; state.m_nindex

// If you have done your own CToolbar class, let's perform the status update first.

IF (cwnd :: oncmdmsg (state.m_nid, cn_update_command_ui, & stat, null)

CONTINUE;

// If Toolbar does not update yourself, let Ptarget (that is, the parent Frame Window from it) to update it. For example, the PTARGET will point to CMAINFRAME for the SDI framework that MFC automatically generated.

State.doupdate (PTARGET, BDISABLEIFNOHNDLER);

}

}

// If there is a control created in CToolbar, you will be updated together.

UpdatedialogControls (PTARGET, BDISABLNOHNDLER);

}

CCMDUI :: Doupdate's code has been listed. At this point, the toolbar and status bar can also be more smooth.

Experienced friends should know that if you imitate the method in a doc / view structure in a dialog-based program, there is no effect if you use Update_Command_UI to update the user interface element. The reason is that after a modal dialog is displayed, the program will enter this dialog to loop its message loop (see this Domodal source code), this will not have WM_IdleUpdateCMDUI to be sent to these interface elements. Let's talk about this situation, you can view the source code of the MFC to find out the principles: first add a header file AFXPriv.h (where Kickidle message is defined), then add a message mapping to handle the WM_KICKIDLE message : ON_MESSAGE (WM_KICKIDLE, ONKICKIDLE). Where ONKICKIDLE is defined as follows:

Lresult CTabDialog :: ONKICKIDLE (WPARAM WP, LPARAM LCOUNT) {UpdatedialogControls (this, true);

Return 0;

}

After completing these work, you can use the Update_command_ui mechanism smoothly.

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

New Post(0)