Edit boxes and default text selection

While working on an application of mine, I had to display ‘Help’ information. I chose to use a read-only multiline edit box along with SetWindowText() to display the text, sounds simple enough. But I ran into a small but annoying problem. The text displayed was all selected, and I thought why would the text be selected by default!?

Here is a sample program to demonstrate this,

  1. #include <windows.h>
  2. #include <aygshell.h>
  3. #include "resource.h"
  4.  
  5. HINSTANCE g_hInst;
  6.  
  7. BOOL CALLBACK EditBoxSelTextDlgProc(HWND, UINT, WPARAM, LPARAM);
  8. void AlignComponents(HWND hDlg);
  9. void InitializeComponents(HWND hDlg);
  10.  
  11. int WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
  12.             LPTSTR lpCmdLine, int nCmdShow)
  13. {
  14.     g_hInst = hInst;
  15.  
  16.     DialogBox(hInst, MAKEINTRESOURCE(IDD_PPC_EDITBOXTEXTSEL), NULL, EditBoxSelTextDlgProc);
  17.     return 0;
  18. }
  19.  
  20. BOOL CALLBACK EditBoxSelTextDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
  21. {
  22.     int wmID, wmEvent;
  23.     PAINTSTRUCT ps;
  24.     HDC hdc;
  25.  
  26.     switch(uMessage)
  27.     {
  28.         case WM_INITDIALOG:
  29.             {
  30.                 SHINITDLGINFO shidi;
  31.                 SHMENUBARINFO mbi;
  32.  
  33.                 memset(&shidi, 0, sizeof(shidi));
  34.                 memset(&mbi, 0, sizeof(mbi));
  35.  
  36.                 shidi.dwMask = SHIDIM_FLAGS;
  37.                 shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
  38.                 shidi.hDlg = hDlg;
  39.                 SHInitDialog(&shidi);
  40.  
  41.                 mbi.cbSize = sizeof(mbi);
  42.                 mbi.hwndParent = hDlg;
  43.                 mbi.nToolBarId = IDR_MENU_EDITBOXTEXTSEL;
  44.                 mbi.hInstRes = g_hInst;
  45.  
  46.                 if(!SHCreateMenuBar(&mbi))
  47.                 {
  48.                     printf("Error creating menu bar, errcode:0x%x\n", GetLastError());
  49.                 }
  50.  
  51.                 AlignComponents(hDlg);
  52.  
  53.                 InitializeComponents(hDlg);
  54.             }
  55.             return TRUE;
  56.  
  57.         case WM_COMMAND:
  58.             {
  59.                 wmID = LOWORD(wParam);
  60.                 wmEvent = HIWORD(wParam);
  61.  
  62.                 switch(wmID)
  63.                 {
  64.                     case IDM_EXIT:
  65.                         EndDialog(hDlg, uMessage);
  66.                         break;
  67.                 }
  68.             }
  69.             break;
  70.  
  71.         case WM_PAINT:
  72.             {
  73.                 hdc = BeginPaint(hDlg, &ps);
  74.  
  75.                 EndPaint(hDlg, &ps);
  76.             }
  77.             break;
  78.     }
  79.     return FALSE;
  80. }
  81.  
  82. void AlignComponents(HWND hDlg)
  83. {
  84.     HWND hTemp = NULL;
  85.     RECT rect = {0, 0, 0, 0};
  86.     int x=0, y=0, width=0, height=0;
  87.     const int insetX = 3;
  88.     const int insetY = 3;
  89.  
  90.     GetClientRect(hDlg, &rect);
  91.  
  92.     hTemp = GetDlgItem(hDlg, IDC_EDITBOX);
  93.     if (hTemp)
  94.     {
  95.         x = rect.left + insetX;
  96.         y = rect.top + insetY;
  97.         width = (rect.right – rect.left – 2*insetX);
  98.         height = (rect.bottom – rect.top – 2*insetY);
  99.  
  100.         MoveWindow(hTemp, x, y, width, height, FALSE);
  101.     }
  102. }
  103.  
  104. const TCHAR message[] = TEXT("Lorem ipsum dolor sit amet, consectetur adipisicing elit, \
  105. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
  106. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris \
  107. nisi ut aliquip ex ea commodo consequat.\
  108. \r\n\r\n\
  109. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum \
  110. dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non \
  111. proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\
  112. ");
  113.  
  114. void InitializeComponents(HWND hDlg)
  115. {
  116.     HWND hTemp = GetDlgItem(hDlg, IDC_EDITBOX);
  117.  
  118.     if (hTemp)
  119.     {
  120.         SetWindowText(hTemp, message);
  121.     }
  122. }

 

This is usual stuff. Display a dialog box from WinMain(), initialize the dialog and the menubar in dialog proc under the WM_INITDIALOG message. The functions of interest would be AlignComponents() and InitializeComponents(). The dialog contains a single multiline edit box, AlignComponents() resizes the edit box so it occupies the entire screen, and InitializeComponents() calls SetWindowText() to set the text on the edit box control.

If you run this program, here is how it’ll look,

image

So you see the problem there. Skimming through the edit box messages, I came across EM_SETSEL message. This message selects a range of characters in an edit control, and if you pass –1 to it, any current selection is removed. So after the SetWindowText() call in InitializeComponents(), I put this line,

SendMessage(hTemp, EM_SETSEL, (WPARAM)-1, (LPARAM)0);

but if it was this simple I wouldn’t be writing this post, would I? In short, it didn’t work.

I thought maybe I am sending the EM_SETSEL message too early, even before the edit control is ready to process it perhaps? So the next step was to find the right place from where the message could be sent. So, one by one, I went through all the messages sent to dialog proc until the dialog is shown and found WM_ACTIVATE could be the right place. And it indeed was.

Adding the following switch case to dialog proc did the trick,

  1. case WM_ACTIVATE:
  2.     SendMessage(GetDlgItem(hDlg, IDC_EDITBOX), EM_SETSEL,
  3.         (WPARAM)-1, (LPARAM)0);
  4.     break;

 

The edit control now shows up without any selected text.

image

Leave a Reply

Your email address will not be published. Required fields are marked *