Home
Fractals
Tutorials
Books
My blog
My LinkedIn Profile

BOOKS i'm reading

Napoleon Hill Keys to Success: The 17 Principles of Personal Achievement, Napoleon Hill, ISBN: 978-0452272811
The 4-Hour Workweek: Escape 9-5, Live Anywhere, and Join the New Rich (Expanded and Updated), Timothy Ferriss, ISBN: 978-0307465351
The Fountainhead, Ayn Rand, ISBN: 0452273331

Avoid trapping the focus in dialog windows in C++

Olivier Langlois, IT superstar coach, Dominate LinkedIn Formula author
by Olivier Langlois

C++ trapped focus in dialog windows demo program

Contents





Introduction

I though that I knew all I had to knew about creating a dialog window with all the windows programming books that I have read. Oh boy, I was wrong. This is a very short article on a discovery that I made about the focus handling in dialog windows. It is how easy you can trap the focus into one of the dialog control and goes unnoticed until you try to navigate the dialog with the keyboard. I have looked back in my reference Windows programming books that are Programming Windows from Charles Petzold and Programming Windows With MFC from Jeff Prosise to be sure that this was not something that I overlooked and I was kinda relieved to find out only a short sentence in the Programming Windows book on the topic: 'Don't disable a control that has the input focus' on page 513. It is kinda easy to forget a short remark with no explanation from a book that has over a 1000 pages! So here we are, I have learn this little pearl of wisdom the hard way by experimenting it and not knowing what was wrong with my dialog. If you found this C++ Windows programming tutorial, it is probably because you are in the same situation than I was, so read on.

The Problem

A type of dialog window that I commonly write is a dialog window used to start and stop some process. So at creation, there is 2 push buttons both disabled, one button for starting the process and one button to stop the process. I let the user type in some info needed for starting the process in some edit boxes and when all the boxes contain valid data, the start button gets enabled. On pressing the start button, this button gets disabled and the stop button gets enabled. When the user clicks on stop, the dialog does the reverse. That is disable the stop button and reenable the start button. I am sure that you get the idea as you have already probably seen tons of dialog that works under this principle.

The intuitive way to achieve the design described and at the same time a sure way to get a bug is something like that:

void Ctrapped_focusDlg::OnBnClickedStart()
{
    m_bStarted = TRUE;
    GetDlgItem(IDC_STOP)->EnableWindow();
    GetDlgItem(IDC_START)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT1)->EnableWindow(FALSE);
}

void Ctrapped_focusDlg::OnBnClickedStop()
{
    m_bStarted = FALSE;
    GetDlgItem(IDC_START)->EnableWindow();
    GetDlgItem(IDC_EDIT1)->EnableWindow();
    GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
}

That is all it takes to trap the focus inside a disabled control. A control, in order to generate the BN_CLICKED notification, must first receive the focus no matter how it has been clicked be it with the keyboard or with the mouse. Actually, the focus is not really trapped. By capturing messages received by the start button with Spy++, you would see that calling the EnableWindow(FALSE) sends a sequence of messages to the control and one of the message is WM_KILLFOCUS with the hwndGetFocus parameter set to NULL. However, even if the focus is not "really" captured, that is what the user will experience. To work around this wrong behavior, you simply have to move the focus on another control inside the BN_CLICKED handler. To do so, you can use the Windows message WM_NEXTDLGCTL or the following MFC functions that wrap the WM_NEXTDLGCTL that are offered in the CDialog class:

  • GotoDlgCtrl()
  • NextDlgCtrl()
  • PrevDlgCtrl()

So by revisiting the previous example, the correct way to implement the BN_CLICKED handlers should be:

void Ctrapped_focusDlg::OnBnClickedStart()
{
    m_bStarted = TRUE;
    CWnd *pStop = GetDlgItem(IDC_STOP);
    pStop->EnableWindow();
    GotoDlgCtrl(pStop);
    GetDlgItem(IDC_START)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT1)->EnableWindow(FALSE);
}

void Ctrapped_focusDlg::OnBnClickedStop()
{
    m_bStarted = FALSE;
    CWnd *pStart = GetDlgItem(IDC_START);
    pStart->EnableWindow();
    GetDlgItem(IDC_EDIT1)->EnableWindow();
    GotoDlgCtrl(pStart);
    GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
}

The demo program

The demo program is just a very simple dialog based application to let experience first hand the concept explained in this article. There is checkbox to let you switch between the buggy behavior and the correct one. In the buggy mode, as soon as you will click on a button, you will not be able to navigate into the dialog by using the keyboard.

Conclusion

That is it! I hope you enjoyed this C++ Windows programming tutorial on trapped focus in a dialog with controls that can become disabled at run time and I hope that the source code will be helpful to you in your projects. In the next section, you will find the books that I have consulted to build this C++ Windows programming tutorial. Those books are great and filled with Windows programming gems that you should know. It is strongly recommended that you get yourself a copy of these books especially since from time to time, you can find these books at a bargain price. Take the time to check the prices. This is maybe your lucky day today!

Bibliography

History

  • 08-30-2006:
    • Original article.


Back to the tutorials list

Home :: Fractals :: Tutorials :: Books :: My blog :: My LinkedIn Profile :: Contact