Edit Controls

Kyle Marsh
Microsoft Developer Network Technology Group

Created: January 31, 1992

Revised: October 16, 1992. Added GMEM_ZEROINIT to code in step 3 in "Edit Controls and Memory" section.

Click to open or copy the files in the EDALIGN sample application for this technical article.

Abstract

This article is a complete reference for edit controls. It describes all edit control styles, messages, and notifications in more detail than those in the Microsoft® Windows™ versions 3.0 and 3.1 Software Development Kit (SDK) documentation. Techniques for solving common problems with edit controls are also described in this article.

Introduction

Edit controls are a way for an application to receive input from a user. They also provide a way for the application to display information, especially when the information requires more than one line. There are two types of edit controls:

Edit controls will not accept virtual-key code less than 0x20. The only exception is that multiple-line edit controls will accept TAB and ENTER key codes. Single-line edit controls do not accept TAB and ENTER keys.

Limits of Edit Controls

Edit controls were designed to enter, display, and edit small amounts of text. They were not meant to be the basis for large-scale text editors. Edit controls in Microsoft® Windows™ have the following limits:

Edit Controls and Memory

An edit control allocates its control structure, tab stop settings, and text buffer from a local heap by calling LocalAlloc or LocalRealloc. A multiple-line edit control allocates its character-width buffer and line-break array from the local heap as well. The Undo buffer is allocated in the global heap. Because a local heap has a maximum size of 64K bytes, and the text buffer for an edit control plus the edit control's support information are all allocated from one local heap, the actual amount of text that an edit control can hold is always less than 64K.

Table 1 shows some guidelines on the amount of local heap an edit control uses. These rules are not likely to be accurate for versions of Windows later than version 3.1.

Table 1. Local Heap Guidelines

Element Amount of local heap used
Control structure Windows version 3.0: 100 bytes

Windows version 3.1: 100 bytes

As many as 14 more bytes for Windows versions that utilize double characters, such as kanji and Korean versions.

Tab stop setting Not allocated unless set by the application with a WM_SETTABSTOPS message.

The number of tab stops specified plus 1, times the size of an integer.

Character-width buffer 512 bytes. (265 * sizeof(int))
Line-break array Initially two times the size of an integer.

The number of lines plus two times the size of an integer.

Text buffer The number of characters in the control. Windows allocates an extra 32 bytes each time it allocates memory for the text buffer so that a reallocation is not needed each time a character is inserted into the edit control.

Edit controls that are not in a dialog box use the data segment (DS) of their parent for the edit control's local heap. The amount of text an edit control can store is affected by the amount of memory that has already been used in this heap. To maximize the amount of text that an edit control can contain, the application should supply a heap for the edit control that has not been used for any other allocations. To do this, an application should perform the following steps:

  1. Allocate a global memory block 256 bytes in length. This block becomes the local heap for the edit control. In real mode, this handle must be less than 0x2000. If the handle returned is greater than 0x2000 and Windows is running in real mode, you cannot use this global memory block as a local heap.

  2. Pass the handle to the newly allocated global memory block to CreateWindow (or CreateWindowEx) in the hInstance parameter.

  3. Send the edit control an EM_LIMITTEXT message to set the maximum number of characters in the edit control. A zero value passed with EM_LIMITTEXT allows for the maximum number of characters as shown in the following sample code:
          // Assuming PROTECTED MODE ONLY
          //
          // Real mode would need to check for 0x2000 returned.
          //
          hEditDS = GlobalAlloc(GMEM_MOVEABLE |
                                GMEM_ZEROINIT | GMEM_SHARE, 256L);
          if (hEditDS == NULL) {
             // Allocation failed; use default.
             hEditDS = hInst;
          }
       
          //
          // Create the edit control.
          // Pass the local heap handle in hInstance.
          hwndEdit = CreateWindow("edit", NULL,
             WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL |
             WS_VSCROLL | ES_MULTILINE | ES_AUTOHSCROLL |
             ES_AUTOVSCROLL,
                 10, 10, 250, 200, hWnd, IDEDITCTL, hEditDS, NULL);
    
          // Now limit the text to the maximum possible amount.
          //
          SendMessage(hwndEdit, EM_LIMITTEXT, 0, 0L);
    

When an edit control is in a dialog box, the dialog manager controls which local heap the dialog box will use. By default, Windows allocates a local heap for each dialog box that contains an edit control. All the edit controls in a dialog box share the same local heap. If the dialog box is created with the DS_LOCALEDIT style, the edit controls use the data segment of the parent window for their local heap. It is not possible for edit controls within a dialog box to each use a different local heap.

In real mode, a local heap must have a handle of less than 0x2000. If, when the dialog manager attempts to allocate a local heap for a dialog box, a handle greater than or equal to 0x2000 is returned, it uses one of its own local heap handles. Windows keeps 10 handles to use as local heaps. If all 10 of these handles have been used by other dialog boxes, and if Windows cannot allocate a handle less than 0x2000, Windows is unable to create the dialog box. In protected mode, the problem will not occur.

Edit Control Styles

Edit controls implement the styles in the sections that follow.

ES_AUTOHSCROLL

ES_AUTOHSCROLL tells the edit control to scroll the text horizontally, when necessary, as the user enters text. However, if ES_AUTOHSCROLL is not specified, the edit control cannot scroll horizontally. For single-line edit controls without ES_AUTOHSCROLL, only the characters that fill the visible area of the control are accepted. For multiple-line edit controls without ES_AUTOHSCROLL, the text wraps to the next line when the user enters more text than can be displayed on a single line. If ES_AUTOHSCROLL is specified for a multiple-line edit control, the control scrolls horizontally when the user enters more text than can be displayed on a single line; the text will not wrap.

ES_AUTOHSCROLL is automatically applied to an edit control that has a WS_HSCROLL style. In other words, any edit control that has a horizontal scroll bar automatically scrolls horizontally.

ES_AUTOHSCROLL is ignored when an edit control is not left-aligned. Centered and right-aligned multiple-line edit controls cannot be horizontally scrolled.

ES_AUTOVSCROLL

ES_AUTOVSCROLL tells the edit control to scroll the text vertically when the user enters more text than can be displayed within the edit control. This style only applies to multiple-line edit controls. If this style is not specified for a multiple-line edit control, the edit control will not accept the input when more text is entered than can be displayed.

Important   Windows uses the Undo buffer to ensure that any displayable text is accepted. If an edit control does not have ES_AUTOVSCROLL specified, using the undo command will not work correctly in that only the last character entered can be undone. Edit controls with ES_AUTOVSCROLL can undo all the characters entered since the last cut, paste, or replace.

ES_AUTOVSCROLL is automatically applied to an edit control that has a WS_VSCROLL style. In other words, any edit control that has a vertical scroll bar automatically scrolls vertically.

ES_LEFT, ES_RIGHT, and ES_CENTER

ES_LEFT, ES_RIGHT, and ES_CENTER specify the alignment the text in an edit control should have. Single-line edit controls can only be left-aligned. ES_LEFT is the default for both single-line and multiple-line edit controls. Single-line edit controls ignore ES_RIGHT and ES_CENTER. Multiple-line edit controls can be right-aligned, left-aligned, or centered. Right-aligned and centered edit boxes cannot have horizontal scroll bars and cannot have the ES_AUTOHSCROLL style.

Although it's not possible to change the alignment style of an edit control dynamically, two techniques are commonly used to work around this restriction:

The Knowledge Base has article Q66942, which describes the details of these techniques. EDALIGN, a sample application, demonstrates the code required.

ES_LOWERCASE

ES_LOWERCASE causes all uppercase characters entered into the edit control to be converted to lowercase.

ES_MULTILINE

ES_MULTILINE causes the edit control to be a multiple-line edit control.

ES_NOHIDESEL

When ES_NOHIDESEL is specified for an edit control, selected text remains highlighted when the edit control does not have the focus. Normally, without ES_NOHIDESEL specified, an edit control "hides" the selection when it loses focus. In other words, the selection is not highlighted, but the text still appears as normal.

ES_OEMCONVERT

ES_OEMCONVERT causes text entered into the edit control to be converted from ANSI to OEM and then back to ANSI. This ensures proper character conversion when the application calls the AnsiToOem function to convert a Windows string in the edit control to OEM characters. ES_OEMCONVERT is most useful for edit controls that contain filenames.

ES_PASSWORD

ES_PASSWORD causes all characters entered into the edit control to be displayed as an asterisk (*). An application can use the EM_SETPASSWORDCHAR message to change the character that is displayed. In Windows version 3.1, edit controls with the ES_PASSWORD style do not copy their text to the Clipboard.

ES_READONLY (Windows Version 3.1)

ES_READONLY causes the edit control to not allow any entering or editing of text by the user. This style is only recognized by Windows version 3.1 or later. The user cannot add or modify any text in the edit control, but copying text from the edit control is allowed. The technique to make a read-only edit control in Windows version 3.0 is described in the "Common Techniques" section, later in this article.

ES_UPPERCASE

ES_UPPERCASE causes all lowercase characters entered into the edit control to be converted to uppercase.

ES_WANTRETURN

The default behavior for the ENTER key in a multiple-line edit control within a dialog box is to perform the default button of the dialog box, usually the OK button, which accepts any changes made in the dialog box and then dismisses the dialog box. The ES_WANTRETURN style changes the behavior of the edit control so that the ENTER key advances the cursor to the next line. When a user is editing text in a multiple-line edit control, it may be preferred to have the ENTER key advance to the next line of the edit control. By default, without ES_WANTRETURN, multiple-line edit controls accept CTRL+ENTER to advance to the next line.

ES_WANTRETURN only applies to multiple-line edit controls in Windows version 3.1 or later. The "Common Techniques" section, later in this article, describes how to implement this behavior in Windows version 3.0.

Edit Control Messages

Edit controls implement the messages in the sections that follow.

EM_CANUNDO

The EM_CANUNDO message is sent by an application to an edit control to determine if an Undo operation is possible. The edit control returns a TRUE to the application if an Undo is possible.

EM_EMPTYUNDOBUFFER

The EM_EMPTYUNDOBUFFER message is sent by an application to an edit control to clear the Undo buffer for an edit control. Once an edit control processes this message, an Undo operation is not possible until the user modifies the contents of the edit control again.

EM_FMTLINES

By default, an edit control does not store end-of-line characters—those caused by wordwrapping and not specified by the user with ENTER or CTRL+ENTER. Hard carriage returns, specified by the user with ENTER or CTRL+ENTER, are stored as a CR LF character combination (0x0D 0x0A).

An application sends an EM_FMTLINES message to an edit control to control end-of-line characters. If the wParam parameter is non-zero, the edit control adds CR CR LF (0x0D 0x0D 0x0A) character sequences to mark the end of lines. If the wParam parameter is zero, the edit control removes any CR CR LF character sequences. The display of the text in the edit control is unaffected by either setting, but the amount of memory required to store the text will change.

EM_GETFIRSTVISIBLELINE (Windows version 3.1 and later)

The EM_GETFIRSTVISIBLELINE message is sent by an application to a multiple-line edit control to determine what the topmost visible line in the edit control is. The "Common Techniques" section, later in this article, describes a way to determine what the topmost visible line is in Windows versions earlier than version 3.1.

EM_GETLINE

The EM_GETLINE message is sent by an application to an edit control to retrieve a line of text from an edit control. The wParam parameter specifies the line number to retrieve. The first line of the edit control is line 0. Single-line edit controls always return the first line because that is all they have. The lParam parameter points to where the line will be copied. When an application sends this message, it must first place the maximum number of bytes that the buffer can receive into the first WORD of the buffer.

The edit control returns the number of bytes actually copied or zero if the line number specified by the wParam parameter is greater than the number of lines in the edit control.

The copied line does not contain a null-termination character.

For example:

unsigned char szBuf[128];
WORD cbText;

*(WORD *)szBuf = sizeof(szBuf); /* Sets the buffer size.     */
cbText = (WORD) SendDlgItemMessage(hdlg, ID_MYEDITCONTROL,
    EM_GETLINE,
    0,                          /* Line number.              */
    (DWORD) (LPSTR) szBuf);     /* Buffer address.           */
szBuf[cbText] = '\0';           /* Null-terminates the line. */

EM_GETLINECOUNT

The EM_GETLINECOUNT message is sent by an application to an edit control to retrieve the number of lines in a multiple-line edit control. This message is processed only by multiple-line edit controls.

EM_GETMODIFY

The EM_GETMODIFY message is sent by an application to an edit control to determine whether the contents of an edit control have been modified.

The edit control returns TRUE if the edit control contents have been modified or FALSE if they have remained unchanged. Windows maintains an internal flag indicating whether the contents of the edit control have changed. This flag is cleared when the edit control is first created and may also be cleared by sending an EM_SETMODIFY message.

EM_GETSEL

The EM_GETSEL message is sent by an application to an edit control to get the starting and ending character positions of the current selection in an edit control. The edit control returns the starting position in the low-order word and the position of the first nonselected character after the end of the selection in the high-order word. The starting position is always less than the ending position; thus, no indication is given of which end the caret occupies.

EM_GETTHUMB

The EM_GETTHUMB message is sent by an application to an edit control to get the current thumb position for a multiple-line edit control's vertical scroll bar. A multiple-line edit control returns a value between 0 and 100, which represents the current position of the edit control's vertical scroll bar's thumb.

EM_GETRECT

The EM_GETRECT message is sent by an application to an edit control to get the formatting rectangle of an edit control. The formatting rectangle is the rectangle into which the text of the edit control will be drawn. Usually this is the same size as the edit control, including the border of the edit control (if there is one). An application can set the formatting rectangle to be different (see "EM_SETRECT," later in this article).

To get the edit control's formatting rectangle, an application sends a far pointer to a RECT structure in the lParam parameter of the EM_GETRECT message.

For example:

RECT rcl;
SendMessage(hWnd, EM_GETRECT, 0, (DWORD) ((LPRECT) &rcl));

EM_LIMITTEXT

The EM_LIMITTEXT message is sent by an application to an edit control to limit the length of the text that the user may enter into an edit control.

An application specifies the maximum number of characters for the edit control in the wParam parameter sent with the EM_LIMITTEXT message. If this parameter is zero, the text length is set to the maximum number of bytes possible.

The EM_LIMITTEXT message only limits the text the user can enter. It has no effect on any text already in the edit control when the message is sent, nor does it affect the length of the text copied to the edit control by the WM_SETTEXT message.

If an application uses the WM_SETTEXT message to place more text into an edit control than is specified in the EM_LIMITTEXT message, the user can edit the entire contents of the edit control.

EM_LINEFROMCHAR

The EM_LINEFROMCHAR message is sent by an application to a multiple-line edit control to retrieve the line number of the line that contains the specified character index. A character index is the number of characters from the beginning of the edit control.

This message is only processed by multiple-line edit controls.

An application sends the character index to the edit control in the wParam parameter. The edit control returns the number of the line that contains the specified character.

Line numbers for an edit control begin with zero.

If the character index sent to the edit control is –1, the line number of the line containing the caret is returned. If there is a selection, the line number containing the beginning of the selection is returned.

EM_LINEINDEX

The EM_LINEINDEX message is sent by an application to a multiple-line edit control to retrieve the character index of a line within the edit control. The character index is the number of characters from the beginning of the edit control to the specified line.

This message is only processed by multiple-line edit controls.

An application sends the line number desired to the edit control in the wParam parameter. The edit control returns the character index of the line. If the line specified is larger than the number of lines in the edit control, the edit control returns –1. If the application specifies –1 as the line number in wParam, the character index of the line containing the caret is returned. A selection does not affect the return value.

Line numbers for an edit control begin with zero.

EM_LINELENGTH

The EM_LINELENGTH message is sent by an application to an edit control to retrieve the length of a line in the edit control.

For a single-line edit control, the edit control returns the length of the line it contains.

For a multiple-line edit control, the application specifies the character index of a character in the line whose length is desired in the wParam parameter. If the application specifies –1 in the wParam, the line containing the caret is used. If characters are selected on the current line, the length of the line does not include the length of the selection.

EM_LINESCROLL

The EM_LINESCROLL message is sent by an application to a multiple-line edit control to scroll the text of a multiple-line edit control.

This message is processed only by multiple-line edit controls.

An application specifies the number of lines to scroll vertically in the low-order word of the lParam and the number of characters to scroll horizontally in the high-order word.

The edit control will not scroll vertically past the last line of text in the edit control. If the current line plus the number of lines specified by the low-order word of the lParam parameter exceed the total number of lines in the edit control, the value is adjusted such that the last line of the edit control is scrolled to the top of the edit-control window.

The EM_LINESCROLL message can be used to scroll horizontally past the last character of any line.

This message is ignored if the multiple-line edit control was created with either the ES_RIGHT or the ES_CENTER style.

EM_REPLACESEL

The EM_REPLACESEL message is sent by an application to an edit control to replace the current selection in an edit control. If there is no current selection, the text is inserted at the current caret position. An application specifies the new text by passing a far pointer to the text in the lParam parameter. The new text must be null-terminated. This message is useful to replace a portion of the contents of an edit control.

EM_SETMODIFY

The EM_SETMODIFY message is sent by an application to an edit control to set or clear the modification flag for an edit control. The modification flag indicates whether the text within the edit control has been modified. It is automatically set whenever the user changes the text. The value of the modification flag may be retrieved by sending an EM_GETMODIFY message.

An application specifies the new value for the modification flag in the wParam parameter.

EM_SETPASSWORDCHAR

The EM_SETPASSWORDCHAR message is sent by an application to an edit control to set or remove a password character displayed in an edit control when the user enters text. When a password character is set, that character is displayed for each character the user types.

This message has no effect on a multiple-line edit control.

An application specifies the new password character in the wParam parameter. If the value is zero, the actual characters typed by the user are displayed. This message can be used to effectively set or remove the ES_PASSWORD style on an edit control.

When the EM_SETPASSWORDCHAR message is received by an edit control, it redraws all visible characters using the character specified.

EM_SETREADONLY (Windows Version 3.1 or Later)

The EM_SETREADONLY message is sent by an application to an edit control to set the read-only state of an edit control. This message sets or removes the ES_READONLY style on an edit box. An application specifies the new state in the wParam parameter of the message.

EM_SETRECT

The EM_SETRECT message is sent by an application to an edit control to set the formatting rectangle of a multiple-line edit control. The formatting rectangle is the rectangle into which the text of the edit control will be drawn. Initially, this is the same size as the edit control. By using the EM_SETRECT message, an application can make the formatting rectangle larger or smaller than the edit-control window.

This message is processed only by multiple-line edit controls.

To set a new formatting rectangle, an application sends a far pointer to a RECT structure that contains the new rectangle points.

The EM_SETRECT message causes the text of the edit control to be redrawn. To change the size of the formatting rectangle without redrawing the text, an application must use the EM_SETRECTNP message.

If the edit control does not have a horizontal scroll bar, and the application sets the formatting rectangle to be larger than the edit-control window, Windows clips lines of text exceeding the width of the edit-control window (but smaller than the width of the format rectangle).

If the edit control contains a border, the formatting rectangle is reduced by the size of the border. If the application adjusts the rectangle returned by an EM_GETRECT message, the application must remove the size of the border before using the rectangle with the EM_SETRECT message.

EM_SETRECTNP

The EM_SETRECTNP message is sent by an application to an edit control to set the formatting rectangle of a multiple-line edit control without having the edit control redrawn. This message causes the same actions as the EM_SETRECT message, except that the EM_SETRECT message causes the edit control to redraw the text while EM_SETRECTNP does not.

EM_SETSEL

The EM_SETSEL message is sent by the application to an edit control to select a range of characters within the edit control or to place the caret within the edit control. The lParam argument for the EM_SETSEL message is used to specify the starting and ending character for the selection. The low-order word specifies the starting character position of the selection, and the high-order word specifies the ending character position. These character positions are specified with their offsets from the first character in the edit control, with the first character at offset 0. When the ending selection is –1 (0xffff) and the starting position is 0, all the text in the edit control is selected. When the starting position is –1 ( (0xffff), any current selection is unselected. When the starting and ending positions are the same, the caret is placed after the specified position and no text is selected.

For example, use these lines of code to accomplish these actions:

The order of the start and end positions is not important. The edit control always places the caret at the high end of the selection. If the selection is too large to be displayed within the edit control, the text is scrolled as far to the left as necessary to display the caret.

In Windows version 3.1, an application can set the wParam to 1 to cause the edit control not to scroll the caret into view.

EM_SETTABSTOPS

An application sends an EM_SETTABSTOPS message to set the tab stops in a multiple-line edit control. When text is copied to a multiple-line edit control, any tab character in the text causes space to be generated up to the next tab stop.

This message is processed only by multiple-line edit controls.

An application specifies the number of tab stops contained in the wParam parameter. If this parameter is 0, the lParam parameter is ignored, and default tab stops are set at every 32 dialog box units. If this parameter is 1, tab stops are set at every n dialog box units, where n is the distance pointed to by the lParam parameter. If this parameter is greater than 1, lParam points to an array of tab stops, given in dialog box units.

The low-order and high-order words of lParam point to an array of unsigned integers specifying the tab stops. If the wParam parameter is 1, lParam points to an unsigned integer containing the distance between all tab stops, given in dialog box units.

The EM_SETTABSTOPS message does not automatically redraw the edit-control window. If the application is changing the tab stops for text already in the edit control, it should call the InvalidateRect function to redraw the edit-control window.

The example that follows sends an EM_SETTABSTOPS message to set tab stops at every 64 dialog box units. It then calls InvalidateRect to redraw the edit window.

WORD wTabSpacing = 64;
SendDlgItemMessage(hdlg, ID_MYEDITCONTROL, EM_SETTABSTOPS, 1,
            (DWORD) (LPWORD) &wTabSpacing);
InvalidateRect(GetDlgItem(hdlg, ID_MYEDITCONTROL), NULL, TRUE);

In Windows version 3.0, edit controls calculate tab-stop positions on the size of the default system font. This can cause incompatibilities between tab stops set for edit controls and list boxes that base tab-stop calculations on the size of the font used in the list box. When an array of tab-stop positions is used to set the tab stops for an edit control and a list box in Windows version 3.0, the tab positions may not line up the same for both the edit control and the list box. Windows version 3.1 calculates the tab-stop positions using the control's font in both list boxes and edit controls.

EM_SETWORDBREAK

EM_SETWORDBREAK is documented for Windows version 3.0 but, unfortunately, is not implemented. This message was dropped from Windows version 3.1 and replaced with EM_SETWORDBREAKPROC.

EM_SETWORDBREAKPROC

The EM_SETWORDBREAKPROC message is sent by an application to an edit control to replace the default word-break function with an application-defined word-break function.

An application sends the procedure-instance address of the new word-break function in the lParam parameter. This address is obtained with the MakeProcInstance function, and the new word-break function must be exported in the application module definition file.

Word-break function

A word-break function scans a text buffer, which contains text to be sent to the display, looking for the first word that will not fit on the current display line. The word-break function places this word at the beginning of the next line on the display. A word-break function defines at what point Windows should break a line of text for multiple-line edit controls, usually at a blank character that separates two words. Either a multiple-line or a single-line edit control might call this function when the end user presses arrow keys in combination with the CTRL key to move the cursor to the next word or previous word. The default word-break function breaks a line of text at a blank character. The application-defined function may define a word break to be a hyphen or a character other than the blank character.

There are three action codes that a word-break function must respond to:

EM_UNDO

The EM_UNDO message is sent by an application to an edit control to undo the last edit control operation. Undo operations can also be undone.

WM_GETTEXT

The WM_GETTEXT message is sent by an application to get the contents of an edit control. An application could use GetWindowText, SendDlgItemMessage, or GetDlgItemText to get the contents of an edit control as well.

The following example shows how these functions can be used to get the contents of an edit control:

char szText[128];
SendMessage(hEditWnd, WM_GETTEXT, 128, (LPSTR) szText);
SendDlgItemMessage(hWndDlg, ID_EDITCTL, WM_GETTEXT, 128,
   (LPSTR)szText);
GetWindowText(hEditWnd, (LPSTR) szText, 128);
GetDlgItemText(hWndDlg, ID_EDITCTL, (LPSTR) szText, 128 );

WM_SETTEXT

The WM_SETTEXT message is sent by an application to set the contents of an edit control. An application could use SetWindowText, SendDlgItemMessage, or SetDlgItemText to set the contents of an edit control as well.

The following example shows how these functions can be used to set the contents of an edit control:

char szText[] = "This is sample Text";
SendMessage(hEditWnd, WM_SETTEXT, 0, (LPSTR) szText);
SendDlgItemMessage(hWndDlg, ID_EDITCTL, WM_SETTEXT, 0,
   (LPSTR)szText);
SetWindowText(hEditWnd, (LPSTR) szText);
SetDlgItemText(hWndDlg, ID_EDITCTL, (LPSTR) szText );

When an application sends a WM_SETTEXT message to an edit control to set the text, or uses any of the other techniques above to set the text, more text than is allowed by an EM_LIMITTEXT message can be added to the control. This is a designed behavior for an edit control, allowing an application to have an edit control where only the first part is entered by the user while the rest is specified by the application. Unfortunately, when the application adds more text to a control than is allowed with the EM_LIMITTEXT message, the user can edit the entire text. To work around this problem, the application must either clear the edit control before allowing the user to enter text or truncate the added text to less than the limit set by EM_LIMITTEXT.

Using a Multiple-Line Edit Control's Text Buffer Handle

Windows stores the contents of a multiple-line edit control in a buffer that Windows allocates by calling LocalAlloc. An application can use the return value of LocalAlloc to read the contents of an edit control without the overhead of having the multiple-line edit control copy the text (using GetWindowText, for example) into another buffer supplied by the application. It is also possible for an application to change the contents of the multiple-line edit control in place or to quickly change the contents of a multiple-line edit control by setting the text buffer handle.

The return value of LocalAlloc is the handle to a memory block allocated in the local heap used by the multiple-line edit control. For a multiple-line edit control outside of a dialog box, this local heap will be in the application's data segment; thus, the text buffer is allocated from the application's local heap, and the application can use the local heap functions LocalLock and LocalUnlock to establish a pointer by which the application can access the text.

The dialog manager sets up a local heap for each dialog box that has an edit control. Text buffers for edit controls in dialog boxes are not in the application's local heap; therefore, the local heap functions cannot be used directly to access the text. Edit controls that are created as child windows and are given an alternate local heap to use also have their text buffers outside of the application's local heap and cannot use the local heap functions. See the "Edit Controls and Memory" section, earlier in this article.

There are two ways for an application to make use of a multiple-line edit control's text buffer handle:

EM_GETHANDLE

An application sends the EM_GETHANDLE message to a multiple-line edit control to get the edit control's text buffer handle. Each time an application needs to access the multiple-line edit control's contents, it must send this message to the edit control. There are two reasons for this:

EM_SETHANDLE

An application sends the EM_SETHANDLE message to a multiple-line edit control to set the edit control's text buffer handle. This handle must be allocated from the local heap that the edit control is using. If the edit control is using a local heap other than the application's default local heap, the application must switch the "current local heap" to the local heap used by the edit control before allocating a new handle.

If an application is using the DS_LOCALEDIT style on a dialog box (or the edit control is a child window without its own local heap) and the application gets the text buffer handle using the EM_GETHANDLE message and then replaces the handle using the EM_SETHANDLE message, the application must be sure to free the memory block when it is no longer needed. If the application fails to free the block, it continues to use memory until the application is terminated.

The following example switches an edit control's text buffer between two buffers:

BOOL FAR PASCAL DialogMsgProc(HWND hWndDlg, WORD Message, WORD
   wParam, LONG lParam)
{
 static int    nHandleNum = -1;
 static HANDLE hMem[2];
        char   *pBufText;
        WORD   wEditCtlDS;

 switch(Message)
   {
    case WM_CLOSE:
        // No need to free extra buffer because heap it is from
        // will be freed.
        //
         EndDialog(hWndDlg, FALSE);

         break; // End of WM_CLOSE

    case WM_COMMAND:
         switch(wParam)
           {
            case 202: // Switch buffers.
                 {
                     HANDLE hNewMem;
                     WORD   wEditSelector;

                     if (nHandleNum == -1 ) {
                        /* Get the handle to the original memory */
                        hMem[0] = (HANDLE) LOWORD
                            (SendMessage(GetDlgItem(hWndDlg,101),
                             EM_GETHANDLE, 0, 0L));
                        wEditCtlDS =
                        (WORD)GetWindowWord(GetDlgItem(hWndDlg,101),
                         GWW_HINSTANCE);
                        wEditSelector =
                         HIWORD(GlobalHandle(wEditCtlDS));
                        _asm {
                                 push DS
                                 mov  AX,wEditSelector
                                 mov  DS,AX
                             }
                        /* Allocate new memory. */
                        hNewMem = LocalAlloc(LMEM_MOVEABLE |
                                  LMEM_ZEROINIT, 64);

                        _asm {
                                 pop  DS
                             }

                        hMem[1] = hNewMem;
                        nHandleNum = 0;
                     }
                     //
                     // Get the old handle.
                     // We do this EACH TIME for two reasons:
                     // 1. It may have changed with a realloc.
                     // 2. Windows needs to put the NULL byte at the
                     // end of the string. EM_GETHANDLE will ensure
                     // this.
                     //
                     hMem[nHandleNum] = (HANDLE)LOWORD(SendMessage(
                     GetDlgItem(hWndDlg,101), EM_GETHANDLE, 0, 0L));

                     //
                     // Switch DS and lock handle.
                     //
                     hNewMem = hMem[nHandleNum];
                     wEditCtlDS =
                        (WORD)GetWindowWord(GetDlgItem(hWndDlg,101),
                            GWW_HINSTANCE);
                     wEditSelector =
                         HIWORD(GlobalHandle(wEditCtlDS));
                     _asm {
                              push DS
                              mov  AX,wEditSelector
                              mov  DS,AX
                          }


                     //
                     // Do something with the text...count spaces.
                     //
                     {
                        int i, count;
                        pBufText = LocalLock(hNewMem);
                        for ( i =0; i < strlen(pBufText); i++)
                             if ( pBufText[i] == ' ')
                                count++;
                        pBufText = LocalUnlock(hNewMem);
                     }
                     //
                     // Switch DS back.
                     //

                     _asm {
                              pop  DS
                          }


                     if ( nHandleNum == 0 )
                        nHandleNum = 1;
                     else
                        nHandleNum = 0;

                     /* Set the handle to the new buffer. */
                        SendMessage(GetDlgItem(hWndDlg,101),
                            EM_SETHANDLE,
                  hMem[nHandleNum], 0L);

                 }
                 break;

           }
         break;    /* End of WM_COMMAND

    default:
        return FALSE;
   }
 return TRUE;
} /* End of DialogMsgProc                                      */

Notification Messages

The edit control's parent window receives notification messages through WM_COMMAND messages.

The wParam parameter specifies the identifier of the edit control, and the lParam parameter specifies the handle to the edit control in the low-order word and the notification code in the high-order word.

EN_CHANGE

An edit control sends the EN_CHANGE notification code when the user takes an action that may have altered text in an edit control. This notification code is sent after Windows updates the display (unlike the EN_UPDATE notification code). In Windows version 3.0, this message was sent at the wrong time during an Undo operation. When an edit control received an EM_UNDO message, the operation was performed in two steps:

  1. The edit control removed any text that was added.

  2. The edit control inserted any text that had been deleted.

Windows version 3.0 would send an EN_CHANGE message after the first step instead of after the second step. If an application used GetWindowText to get the contents of the edit control in response to the EN_CHANGE message, the application could receive incorrect text. This problem has been corrected in Windows version 3.1.

EN_ERRSPACE

The EN_ERRSPACE notification code is sent when an edit control cannot allocate enough memory to meet a specific request.

EN_HSCROLL

The EN_HSCROLL notification code is sent when the user clicks an edit control's horizontal scroll bar. The parent window is notified before the display is updated.

EN_KILLFOCUS

The EN_KILLFOCUS notification code is sent when an edit control loses the input focus.

EN_MAXTEXT

The EN_MAXTEXT notification code is sent when any of these conditions exist:

In Windows version 3.1, the EN_MAXTEXT message is sent by a multiple-line edit control when an attempt is made to insert text into an edit control where the maximum number of characters has already been reached or will be reached with the addition of new characters. Single-line edit controls and multiple-line edit controls in Windows version 3.0 only send EN_MAXTEXT messages when an insertion is unable to add all its characters because that insertion causes the maximum number of characters to be reached.

EN_SETFOCUS

The EN_SETFOCUS notification code is sent when an edit control receives the input focus.

EN_UPDATE

The EN_UPDATE notification code is sent when the edit control is about to display altered text. This notification is sent after the control has formatted the text but before it displays the text. This makes it possible to alter the window size, if necessary.

EN_VSCROLL

The EN_VSCROLL notification code is sent when the user clicks an edit control's vertical scroll bar. The parent window is notified before the display is updated.

Common Techniques

The following techniques help to solve some common problems with edit controls.

Finding the Caret Position

An application can perform the following steps to determine the current position of the caret:

{
   WORD  wLineNumber;
   WORD  wLineIndex;
   WORD  dwGetSel;
   WORD  wStart;
   WORD  wEnd;
   WORD  wColNumber;
   char  szBuffer[128];

   wLineNumber = (WORD)SendMessage(GetDlgItem(hWndDlg,IDEDIT),
                    EM_LINEFROMCHAR, -1, 0L);

   // Send the EM_GETSEL message to the edit control.
   // The low-order word of the return value is the character
   // position of the caret relative to the first character in the
   // edit control.
   dwGetSel = (WORD)SendMessage(GetDlgItem(hWndDlg,101), EM_GETSEL,
               0, 0L);
   wStart = LOWORD(dwGetSel);

   // Send the EM_LINEINDEX message with the value of -1 in wParam.
   // The return value is the absolute number of characters
   // that precede the first character in the line containing
   // the caret.
   wLineIndex = (WORD)SendMessage(GetDlgItem(hWndDlg,101),
                 EM_LINEINDEX, -1, 0L);

   // Subtract the LineIndex from the start of the selection,
   // and then add 1 (since the column is zero-based).
   // This result is the column number of the caret position.
   wColNumber = wStart - wLineIndex;

   sprintf(szBuffer,"Caret at Line:%d Column:%d", wLineNumber+1,
             wColNumber+1);
   MessageBox(hWndDlg, szBuffer, "Current Caret Position",
          MB_APPLMODAL);
}

Finding the First Visible Line

Windows versions 3.1 and later supply the edit-control message EM_GETFIRSTVISIBLELINE, which an application can use to obtain the topmost visible line. Windows version 3.0 did not supply this edit-control message. An application can perform the following steps to determine the topmost visible line in Windows version 3.0:

    {
    TEXTMETRIC   tm;
    HDC          hDC;
    HFONT        hFont;
    WORD         iLine;
    WORD         iSel;
    WORD         iTopLine;
    POINT        pt;

   iSel=(WORD)SendMessage(hWndEdit, EM_GETSEL, NULL, 0L);
   // Get the line number; add 1 to correct for zero-based.
   iLine=(WORD)SendMessage(hWndEdit, EM_LINEFROMCHAR, iSel, 0L)+1;

   // Call GetCaretPos() to fill a POINT structure with the caret
   // coordinates relative to the client area of the edit control.
   // (The client area is inside the border.)
   GetCaretPos(&pt);

   // Call GetDC using the handle to the edit control. The return
   // value is the handle to a display context for subsequent
   // operations.
   hDC=GetDC(hWndEdit);

   // Send the WM_GETFONT message to the edit control. The return
   // value is a handle to the font currently used by the edit
   // control.
   hFont=SendMessage(hWndEdit, WM_GETFONT, 0, 0L);

   // If the font used is not the system font, select it.
   if (hFont != NULL)
       SelectObject(hDC, hFont);

   // Call GetTextMetrics using hDC to fill a TEXTMETRIC structure
   // with information about the font used by the edit control,
   // which is selected into hDC. The field of interest is
   // tmHeight.
   GetTextMetrics(hDC, &tm);
   ReleaseDC(hWndEdit, hDC);

   cyLine=tm.tmHeight;

   // Calculate the first visible line.
   // While the vertical coordinate of the caret is greater than
   // tmHeight, subtract tmHeight from the vertical coordinate and
   // subtract 1 from the line number of the caret.
   // The value remaining in the line number variable is the line
   // number of the first visible line in the edit control.
   iTopLine=iLine;
   while (pt.y > cyLine) {
      pt.y -=cyLine;
      iTopLine--;
   }
}

Coloring an Edit Control

Each time an edit control is about to be painted to the screen, Windows sends the parent window a WM_CTLCOLOR message. The wParam parameter contains the handle to the display context of the edit control, the low-order word of the lParam parameter identifies the edit control, and the high-order word of lParam contains CTLCOLOR_EDIT. The application can set the color or pattern for the edit control by returning a handle to a brush. Brush handles can be obtained by selecting a stock object or by building a brush from a pattern bitmap. The application can also set the text foreground and background colors.

The following code sets the colors for an edit control to white text on a gray background. The colors for other controls use the defaults.

.
.
.
   case WM_CTLCOLOR:
      if ( HIWORD(lParam) == CTLCOLOR_EDIT ) {
         // Set the text background color.
            SetBkColor(wParam, RGB(128,128,128));
         // Set the text foreground color.
            SetTextColor(wParam, RGB(255, 255, 255) );
         // Return the control background brush.
            return GetStockObject(LTGRAY_BRUSH);
                  }
      else
         return GetStockObject(WHITE_BRUSH);
.
.
.

In Windows version 3.0, there is a bug that caused a single-line edit control to be colored incorrectly. Windows would draw the control with the colors and brush from a WM_CTLCOLOR message and then again with the standard colors. Here are two workarounds to this problem:

SetBkColor does not support dithered colors. This is not specific to edit controls, but the application developer needs to be aware of the situation when attempting to color edit controls. Windows uses the closest solid color for background color of a control. Thus both of the following statements produce a gray control background with a 16-color driver:

   SetBkColor(wParam, RGB(64,128,128));
   SetBkColor(wParam, RGB(128,128,128));

With a 256-color driver the first statement creates a gray/green background, and the second statement produces a gray background.

Using an Edit Control for Clipboard I/O

Edit controls have built-in functionality to perform cut, copy, and paste operations between the control and the Clipboard. Using these built-in functions instead of writing the Clipboard I/O from scratch can be convenient, especially during the prototype and early development phases of a project or when time is extremely short. To take advantage of this built-in functionality, applications can create an edit control just for the purpose of Clipboard I/O. This technique results in an application that is slower and takes more memory than if you had developed the Clipboard I/O, but it is a quick way to program Clipboard I/O. Details on implementing this are available in the Knowledge Base under article Q35100.

TAB and ENTER Keys for Multiple-Line Edit Controls in Dialog Boxes

When a multiple-line edit control is used in a dialog box, Windows assigns a default behavior for the ENTER and TAB keys. The ENTER key is used to perform the default function of the dialog box. Usually this is to accept any changes made in the dialog box and then dismiss the dialog box. When a user is editing text in a multiple-line edit control, it may be preferred to have the ENTER key advance to the next line of the edit control. By default, multiple-line edit controls accept CTRL+ENTER to advance to the next line.

Similarly, by default the TAB key moves the input focus to the next control in the tabbing sequence. However, it might be useful for the user to be able to enter TAB characters in an edit control. By default, multiple-line edit controls accept CTRL+TAB to enter TAB characters in an edit control.

The following sections describe four methods that can be used to implement alternative behavior for the ENTER and TAB keys. It should be noted that some users may object if the ENTER and TAB keys no longer act in the default fashion.

Use ES_WANTRETURN in Windows version 3.1

Windows version 3.1 supplies a new edit control style, ES_WANTRETURN, which causes an ENTER key to advance to a new line and not be passed to the dialog manager. ES_WANTRETURN does not affect TAB keys.

Subclass the edit control

An edit control subclass procedure could respond to the WM_GETDLGCODE message with DLGC_WANTALLKEYS to receive all keyboard input. This causes the edit control to be sent all key input, including ENTER and TAB keys. The following subclass procedure could be used:

LONG FAR PASCAL AllKeyFunc(HWND hWnd, WORD Message, WORD wParam,
   LONG lParam)
{
   //
   // This subclass function makes pressing an ENTER key
   // create a new line when the focus is in an edit control.
   //

   if ( Message == WM_GETDLGCODE )
      return DLGC_WANTALLKEYS;

   return CallWindowProc(lpfnOldClassProc, hWnd, Message, wParam,
      lParam);
}

Process the DM_GETDEFID message

Windows sends a DM_GETDEFID message to a dialog box procedure when the user presses ENTER in a dialog box. By intercepting this message and keeping track of whether the focus is in an edit control, an application can change the behavior of the ENTER key. This does not work for TAB keys. This technique takes these three steps:

  1. Process WM_COMMAND messages with the HIWORD(lParam) set to EN_SETFOCUS and EN_KILLFOCUS to determine if a multiple-line edit control has the focus. When EN_SETFOCUS signals that a multiple-line edit control has the focus, set a static flag to TRUE. When EN_KILLFOCUS signals that a multiple-line edit control has lost the focus, reset the flag to FALSE.

  2. When the dialog procedure receives a DM_GETDEFID message, an edit control has the focus, and if the ENTER key is down, then post a WM_CHAR message with wParam set to 0x0a to the edit control with the focus.

  3. If an edit control has the focus, the dialog function should return TRUE; otherwise, the function must return FALSE. Failing to do so keeps the ENTER key from behaving properly when an edit control does not have the focus.

The following code fragment demonstrates this procedure:

     static fEditFocus;
     switch (msg)
         {
         case WM_COMMAND:
             // ID_EDIT is a multiline edit control.
             if (ID_EDIT == wParam)
                 {
                 if (EN_KILLFOCUS == HIWORD(lParam))
                     fEditFocus = FALSE;
                 if (EN_SETFOCUS == HIWORD(lParam))
                     fEditFocus = TRUE;
                 }
             else
                 ...
             break;
         case DM_GETDEFID:
             /*
              * Check to see that an edit control has the focus
              * and that the ENTER key is down. DM_GETDEFID may be
              * sent in other situations when the user did not press
              * the ENTER key.
              */
             if (fEditFocus && (0x8000 & GetKeyState(VK_RETURN)))
                 {
                 PostMessage(hEdit, WM_CHAR, 0x0A, 0L);
                 return TRUE;
                 }
             break;
         }
     return FALSE;

Intercept messages before calling IsDialogMessage

Applications that use modeless dialog boxes are required to filter messages through the IsDialogMessage function. IsDialogMessage modifies certain messages to implement dialog box behavior. For example, the ENTER key message is modified to generate a WM_COMMAND message with wParam set to IDOK.

An application is, however, free to modify the message before passing it to IsDialogMessage. The code example below modifies the WM_KEYDOWN message containing a VK_RETURN to be an EM_REPLACESEL message with a carriage return (CR) and linefeed (LF) combination.

The disadvantage to this method is that it places additional code in the main message loop for the application, slowing the processing of every message. In addition, code in the message loop is far removed from the dialog procedure and is therefore harder to maintain.

The following code fragment demonstrates this method:

// hWndEditControl is the handle to the multiline edit control.
// hWndModeless is the handle to the modeless dialog box.
while (GetMessage(&msg, NULL, 0, 0)) {
   if (msg.hWnd == hWndEditControl
           && msg.message == WM_KEYDOWN
           && msg.wParam == VK_RETURN) {
      // Normally, Windows will translate this to IDOK.
      // Perform a custom translation to something more
      // useful (replace selection with carriage-return,
      // line feed).
      msg.message = EM_REPLACESEL;
       msg.wParam  = 0;
        msg.lParam  = (long)(LPSTR)"\015\012";
       }
   if (!IsDialogMessage(hWndModeless, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
}

If the functionality of a modal dialog box is desired in an application but you still want to use this interception method, it can be simulated by using a modeless dialog box that disables its parent window when the dialog box is created.

Read-Only Edit Control

Windows versions 3.1 and later supply an ES_READONLY edit control style and the EM_SETREADONLY edit control message to make an edit control read-only. Windows version 3.0 did not supply these features. The easiest way to make an edit control read-only in Windows version 3.0 is to subclass the edit control and have the subclass function absorb any messages that would cause the edit control to change its contents.

Here is how to make an edit control read-only in Windows version 3.0:

// Declare the subclass function.
long FAR PASCAL SubEditWndProc(HWND hWnd, unsigned wMessage,
                WORD wParam, LONG lParam);

//
// Create an edit control.
//
hWndEdit=CreateWindow("EDIT",
                  "Edit Control Line Numbers",
                  WS_OVERLAPPEDWINDOW,
                  35, 35, 400, 150,
                  NULL, NULL, hInstance, NULL);
//
// Subclass the edit control.
//
static FARPROC     lpSubClassProc;

lpSubClassProc=MakeProcInstance((FARPROC)SubEditWndProc,hInstance);
     SetWindowLong(hWndEdit, GWL_WNDPROC, (LONG)lpSubClassProc);
.
.
.

//
// Read-only subclass function for an edit control.
//
long FAR PASCAL SubEditWndProc(HWND hWnd, unsigned wMessage, WORD
   wParam, LONG lParam)
{
   switch ( wMessage ) {
      case WM_CUT:
      case WM_KEYDOWN:
      case WM_PASTE:
      case WM_CHAR:
         // Do nothing here.
      break;

      default:
         CallWindowProc (lpPrevWndFunc, hWnd, wMessage, wParam,
                         lParam );
   }
}

Final Warnings

In working with edit controls, you need to be aware of some situations to avoid.

Don't Use getc with Edit Controls

When an application uses getc in a Windows edit control, data corruption can occur. When an application uses an edit control and needs getc functionality, the fgetc function should be used because it does not produce the data corruption.

Single-Line Edit Controls and the BACKSPACE Key

In Windows version 3.0, single-line edit controls that have a font larger than the default system font do not correctly implement the BACKSPACE key. When the user uses the BACKSPACE key to delete characters, the edit control does not remove the lower half of each character from the screen. This problem can be avoided by creating the edit control with the ES_MULTILINE attribute. If you do not specify the ES_AUTOVSCROLL attribute, the edit control will be a multiple-line edit control that behaves like a single-line edit control but will not exhibit this problem.