Using ON_UPDATE_COMMAND_UI with all controls in a Dialog

From the previous messages in this thread, you can see how it is possible to use the ON_UPDATE_COMMAND_UI macro with Dialog boxes by overriding the PreTranslateMessage(MSG *pMsg) and calling UpdateDialogControls(CCmdUI*, BOOL). However, what has not been discussed so far and one reason why the App Wizard does not offer the option of adding update handlers to any controls in a dialog is that these controls are likely to require more than just simple window enabling or disabling. As a result, you might run into a couple of problems.

Consider a dialog with a CComboBox control, if all that is required is to enable and disable this control depending on a certain condition, then the ON_COMMAND_UPDATE_HANDLER provides a convenient method to perform this operation:


void CMyDialog::OnUpdateMyComboBox(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_bShowCombo);
}

However, if it is also necessary to update the selection of the CComboBox as well as enabling and disabling it then you might code the following:

void CMyDialog::OnUpdateMyComboBox(CCmdUI* pCmdUI)
{
	// Get the combo box
	CComboBox* pMyCombo = (CComboBox *) pCmdUI->m_pOther;

	// Update the selection according to our member variable
	pMyCombo->SetCurSel(m_nSel);

	pCmdUI->Enable(m_bShowCombo);
}

However, unfortunately the code above would lead you into all sorts of trouble. First of all, every time the UpdateDialogControls(CCmdUI*, BOOL) function is called (which will ultimately send out the desired ON_UPDATE_COMMAND_UI messages), the ComboBox as a result of the code in its update handler will change its current selection. All this is going to cause enough flicker to make it an irritating problem. The solution is as simple as is obvious; don’t update the selection if it will result in the same selection being made.

void CMyDialog::OnUpdateMyComboBox(CCmdUI* pCmdUI)
{
	CComboBox* pMyCombo = (CComboBox *) pCmdUI->m_pOther;

	int nCurSel = pMyCombo->GetCurSel();

	if (nCurSel != m_nSel)
		pMyCombo->SetCurSel( m_nSel );

	pCmdUI->Enable(m_bShowCombo);
}

Albeit that the above scenario isn't going to occur very often there are times when something similar will be required. However, we will still have another more immediate problem; that is, the user will not be able to make a selection using the ComboBox. Try it and you'll see why. If they try to, ON_UPDATE_COMMAND_UI messages will spring from all over the place and our combo box will be constantly trying to set the current selection.

The solution to this problem is slightly more taxing, but only just. Have a look at the following code:

// Does the current window have the focus
bool CMyDialog::HasFocus(const CWnd* pWndCtrl) const
{
	ASSERT(::IsWindow(pWndCtrl->m_hWnd));
	// if (pWndCtrl->m_hWnd == GetFocus()->m_hWnd) is insufficient, so...

	// Get window with focus
	CWnd* pFocusWnd = GetFocus();

	// Ensure its not null and is a window. Can occur if application loses and regains focus
	if (pFocusWnd == NULL || !::IsWindow(pFocusWnd->m_hWnd))
	return true;

	// Don't update if the control has the focus
	if (pFocusWnd->GetDlgCtrlID() == pWndCtrl->GetDlgCtrlID())
	return true;

	return false;
}

So now, all that is necessary is to place a call to the HasFocus() function (which does a bit more than checking for just focus) and do nothing if we do have the focus.


void CMyDialog::OnUpdateMyComboBox(CCmdUI* pCmdUI)
{
	CComboBox* pMyCombo = (CComboBox *) pCmdUI->m_pOther;

	If (HasFocus(pMyCombo)
		return;

	int nCurSel = pMyCombo->GetCurSel();

	if (nCurSel != m_nSel)
		pMyCombo->SetCurSel( m_nSel );

	pCmdUI->Enable(m_bShowCombo);
}

So with some very simple code, you can update all the controls in your dialog. If you only have a few controls then it really isn't worth it. However, if you have a fair few of them and in cases where their state changes regularly and perhaps even relies on the state of other controls in the dialog this could be a good way to go. Simply it is as follows:

Don't update if you have the focus
Don't update if you aren't changing any values

The demo exe and source below use the ON_UPDATE_COMMAND_UI macro with a little more vigour. The contents of one Combo box is updated to reflect the selection in another, CEdits perform similar things as other controls. Have a look why dont you.

Caveat Emptor

Oh yes the But...I almost forgot the But... But, ON_UPDATE_COMMAND_UI handling is done during your application OnIdle() function. If you consume loads of on idle time processing your handlers, you may find Windows gets a little fed up and the HDD will go into paging overdrive. As it happens, windows has some of its own stuff to do during OnIdle and if it doesn't get to do it... Saying that though, I've never had any problems and neither should you if keep your update handlers on the light side.

Download demo project (.exe) - 6 KB

Download source - 11 KB

Date Last Updated: January 25, 1999



Comments

  • Highlighted controls based on your code.

    Posted by Legacy on 03/31/1999 12:00am

    Originally posted by: Navi Singh

    I added a line to your HasFocus function to fix focus
    
    problems I encountered with comboboxes. GetFocus()
    returns the Edit control nested inside the combo control.
    Consequently, GetDlgCtrlID() returns 1001 and not the
    ID of the combo control.

    BOOL CFtpDlg::HasFocus(const CWnd* pWndCtrl) const
    {
    ASSERT(::IsWindow(pWndCtrl->m_hWnd));
    // if (pWndCtrl->m_hWnd == GetFocus()->m_hWnd) is insufficient, so...

    // Get window with focus
    CWnd* pFocusWnd = GetFocus();

    // added to make comboboxes work just right.
    //=================================================
    if(pFocusWnd->GetParent()->GetDlgCtrlID())
    pFocusWnd = pFocusWnd->GetParent();
    //=================================================

    // Ensure its not null and is a window. Can occur if application loses and regains focus
    if (pFocusWnd == NULL || !::IsWindow(pFocusWnd->m_hWnd))
    return TRUE;

    // Don't update if the control has the focus
    if (pFocusWnd->GetDlgCtrlID() == pWndCtrl->GetDlgCtrlID())
    return TRUE;

    return FALSE;
    }

    In my command handler I have the following:

    void CFtpDlg::OnUpdateHandler(CCmdUI* pCmdUI)
    {
    CWnd *pWnd = pCmdUI->m_pOther;

    BOOL bHasFocus = HasFocus(pWnd);
    COLORREF color = bHasFocus ? RGB(255, 0, 0) : GetSysColor(COLOR_BTNFACE);
    CPen pen(PS_INSIDEFRAME, 2 * GetSystemMetrics(SM_CYBORDER), color);

    CRect rect;
    pWnd->GetWindowRect(rect);

    CClientDC dc(this);
    dc.SaveDC();
    dc.SelectObject(pen); // the pen used to draw the rectangle's border.
    dc.SelectObject(GetStockObject(NULL_BRUSH)); // required to draw a transparent rectangle.
    ScreenToClient(rect); // convert the screen coordinates into client coordinates.
    InflateRect(rect, 2, 2);
    dc.Rectangle(rect); // draw the rect.
    dc.RestoreDC(-1);
    }

    Using your command handler logic, I have highlighted
    controls. The control under focus has a nice red rectangle
    around it.

    Navi

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Instead of only managing projects organizations do need to manage value! "Doing the right things" and "doing things right" are the essential ingredients for successful software and systems delivery. Unfortunately, with distributed delivery spanning multiple disciplines, geographies and time zones, many organizations struggle with teams working in silos, broken lines of communication, lack of collaboration, inadequate traceability, and poor project visibility. This often results in organizations "doing the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds