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

  • Managing your company's financials is the backbone of your business and is vital to the long-term health and viability of your company. To continue applying the necessary financial rigor to support rapid growth, the accounting department needs the right tools to most efficiently do their job. Read this white paper to understand the 10 essentials of a complete financial management system and how the right solution can help you keep up with the rapidly changing business world.

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds