Channels ▼
RSS

.NET

How Can I Trap the Enter Key to Control the Cursor on a Dialog?


Some years ago, I was at a conference where Microsoft was talking about the latest version of MFC and how to apply it to different user interface design models. The speaker mentioned two major models—document/view and forms. With possibly the exception of Access, all of Microsoft’s productivity applications such as Excel, Word, and so on are document/view applications. The user works within a document that may have some structure (like a spreadsheet) but is usually free to enter data in whatever manner they see fit. There are also often multiple presentations of the same data available like a graph of spreadsheet data. The speaker later mentioned that sometime in the future Microsoft would develop the forms model alongside the doc/view model. Sadly this never really occurred. Although Microsoft did develop at least one notable form-style application (Microsoft Money) they never really provided much support in the MFC framework (or even in the SDK) to make form applications easy to develop.

If you have ever watched a data entry clerk enter data into an application, it’s amazing how fast some of them can read a paper form, key in the data, and move to the next entry. To make their lives as easy (and productive) as possible, the software application needs to accommodate natural keyboard motions. Unfortunately, using the Tab key to move from one control to another (or worse, using the mouse) does not lend itself to fast data entry. At some point in the distant past, Microsoft decided that the Enter key would be tied to the default command button (i.e., the OK button) on a dialog (unless overridden by the developer). For some dialogs, like property sheets, this may be okay because you don’t typically key data into every field and thus need a mode of entry that is very efficient; using the mouse or tab key in this situation is just fine.

Ironically, in the MS-DOS world that preceded Windows on the PC, use of the Enter key to move around a form input model was very typical. Most form based “frameworks” available at the time provided this capability out-of-the-box. It was only when Windows 3.0 arrived that form developers first faced the loss of this feature. My own experience with this issue occurred when a company I worked for moved an application from MS-DOS to Windows. During the rewrite, we attempted to follow the Microsoft Windows dictums as closely as possible to ensure we had a “compliant” application (see the 8/7/02 Windows Q&A Newsletter on UI standardization). After initial user testing showed that the Windows application was significantly more difficult for a data entry clerk to work with, we went back to the drawing board and incorporated many form-based mechanisms into the application. One such mechanism was forcing the Enter key to work like the Tab key on a dialog (or other form class like CFormView).

By default, the Enter key is handled by the dialog manager of each dialog window. It’s meaning is assumed to be the following:

  1. Determine the ID of the default command button on the dialog (or form).
    Typically this is IDOK (1).
  2. Send a WM_COMMAND message to the parent of the command button with the ID of the command button.
  3. If ID = 1 (the default), dialog manager calls EndDialog(), which closes the dialog.

There are a couple of options here to override this behavior. One of the simplest (in MFC) is to override CWnd::OnCommand(WPARAM,LPARAM) for the parent dialog or form. Here is some code to illustrate:

	BOOL CMyCoolDialog::OnCommand(WPARAM wp,LPARAM lp)
	{
		int defBtnId = <some value>
		if ( wp == IDOK || wp == defBtnId )
		{
			// user hit the Enter key. 
			// Move to next ctl in the tab order..
			::PostMessage( m_hWnd,WM_NEXTDLGCTL,0,0 );
			return TRUE;
		}
		else
			return CDialog::OnCommand(wp,lp);
	}

Pretty simple, eh? Notice the use of the message WM_NEXTDLGCTL to move focus to the next control in the tab order. You might be wondering why I use this somewhat obscure message rather than the more straightforward SetFocus(). The problem with moving focus to a control by using SetFocus() is that you can often get in the middle of the message flow that occurs when Windows tries to be helpful and move focus for you automatically. You may end up with odd behavior where focus moves initially to where you tell it to go, only to be moved somewhere else an instant later because there was another WM_SETFOCUS message somewhere in the window message queue. This can also occur when you try to override a WM_KILLFOCUS message and perform some field level validation and want to keep the user in the control until they fix the problem (but that's a topic for another day).

Another approach is to subclass each control on the dialog (or form). You would need to trap on the following messages: WM_GETDLGCODE (return DLGC_WANTALLKEYS), WM_CHAR, and WM_KEYDOWN. Then you could filter the Enter key directly and move it another control using WM_NEXTDLGCTL. This approach is preferred if you have a custom window that works similar to a traditional dialog, but is based on a different class. However it's a lot more work, particularly if you have many different kinds of controls on the dialog.

If you are using third-party controls, particularly grids or spreadsheets, look for a built-in property that provides Enter-As-Tab behavior. Many provide this capability since it is so commonly needed in a grid-style interface.

It would have been nice if the MFC team would have just created a property on the CDialog and CFormView classes named something like "UseEnterAsTab to automate this behavior for those working with form based input. But as you can see, doing it yourself is not too difficult, once you know the procedure.

Finally, a note of thanks to the readers who are sending in their questions. Feel free to pepper me with ones that are thorny or might be of interest to other readers. I will answer them in future newsletters for everyone's benefit.


Mark M. Baker is the Chief of Research & Development at BNA Software located in Washington, D.C. Send your Windows development questions to mark_baker@mindspring.com. To subscribe, visit http://www.windevnet.com/newsletters/.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video