Introduction
While writing a MS Internet Explorer add-in using Browser Helper Object, I wanted to have an edit control with icon that is similar to the address bar in Internet Explorer. There is an article about edit control with icon in CodeProject which is written in MFC (Controls-in-controls: An edit box with an icon by Johan Rosengren). He had done it pretty well but there were a couple of issues that didn't satisfy me, and that is why I started to implement my own control, and here you have it all!
Background
The formatting rectangle is a construct maintained by the system for formatting the text displayed in the window rectangle. In other words, by changing the formatting rectangle, the area the text is being drawn in the edit control can be constrained. This is a main idea of implementing icon edit in both Johan Rosengren's implementation and mine. The formatting rectangle will be set in the way so that the space for a specified icon drawing will be reserved.
There are three edit messages related with set/get of the formatting rectangle.
EM_GETRECT
, EM_SETRECT
and EM_SETRECTNP
. As implied by their name, we can get a formatting rectangle by sending EM_GETRECT
to any edit control, but we can set it only for a multi line edit control (edit control with ES_MULTILINE
style) by sending either EM_SETRECT
or EM_SETRECTNP
. Unfortunately, we can not use these messages to set a formatting rectangle for a single line edit control.
When I was working on TitleTip for edit control, I happened to find that there is one more edit message which can alter the formatting rectangle of edit control. It is EM_SETMARGIN
message and it works regardless of ES_MULTILINE
style being set or not. This is the first difference between Johan Rosengren's implementation and mine.
To draw an icon in edit control, I didn't want to use another window than edit control itself. Therefore, instead of creating a static window for drawing the icon, I spent some time to investigate how the default edit control handler draws itself and when. Unlike the regular Windows drawing mechanism, edit control doesn't rely on WM_ERASEBKGND
to erase background, and it draws text and its background not only on receiving WM_PAINT
but also on some other messages.
From a quick Internet search, I found that WM_CHAR
could be the first candidate of some other messages. It seemed working quite well till I found that making selection of text in edit control using an arrow key while holding down SHIFT key made the icon disappear. After further experiment and spying messages, I instead decided to intercept and treat WM_GETDLGCODE
, and it worked like charm! At least, I thought! :P
Well, it was working very nicely on Window 2000 machines. But when I tried my control on Window XP machines and if the application is using newer version (version 6) of common control, making selection of text in edit control by dragging mouse left button caused the problem again, thus I needed to intercept and treat WM_MOUSEMOVE
message, especially when mouse left button is held down.
Honestly, I couldn't figure out how exactly and when edit control draws itself, and adding the additional message handler for every icon drawing problem when it happened was the only choice I could have. If someone has a better idea or can tell me which message should be handled, please drop me a note.
Using the code
It is very simple to use my control. First, include "IconEdit.h" in your source code, then subclass edit control and set the icon (HICON
) of your choice.
#include "iconedit.h"
using codeproject::CIconEdit;
class CMainDlg : public CDialogImpl<CMainDlg>
{
public:
enum { IDD = IDD_MAINDLG };
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
...
END_MSG_MAP()
protected:
CIconEdit c_edTest;
public:
LRESULT OnInitDialog(UINT , WPARAM ,
LPARAM , BOOL& )
{
c_edTest.SubclassWindow(GetDlgItem(IDC_ED_TEST));
c_edTest.SetIcon(IDI_MY_ICON);
...
return TRUE;
}
};
And you can subclass your own edit control from CIconEdit
.
#include "iconedit.h"
using codeproject::CIconEdit;
class CIconEditBlack : public CIconEdit
{
typedef CIconEditBlack thisClass;
typedef CIconEdit baseClass;
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(OCM_CTLCOLOREDIT, OnCtlColorEdit)
DEFAULT_REFLECTION_HANDLER()
CHAIN_MSG_MAP(baseClass)
END_MSG_MAP()
LRESULT OnCtlColorEdit(UINT uMsg, WPARAM wParam,
LPARAM , BOOL& )
{
uMsg;
ATLASSERT(OCM_CTLCOLOREDIT == uMsg);
CDCHandle dc((HDC)wParam);
dc.SetTextColor(RGB(255, 255, 255));
dc.SetBkColor(RGB(0, 0, 0));
return (LRESULT)(HBRUSH)::GetStockObject(BLACK_BRUSH);
}
};
class CMainDlg : public CDialogImpl<CMainDlg>
{
public:
enum { IDD = IDD_MAINDLG };
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
...
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
protected:
CIconEditBlack c_edBlack;
public:
LRESULT OnInitDialog(UINT , WPARAM ,
LPARAM , BOOL& )
{
c_edBlack.SubclassWindow(GetDlgItem(IDC_ED_BLACK));
c_edBlack.SetIcon(IDR_MAINFRAME);
...
return TRUE;
}
};
Public Functions
BOOL SubclassWindow(HWND hWnd);
Subclass necessary messages and reserve a space for icon drawing. (default margin.cx
= 2 and margin.cy
= 0).
HICON SetIcon(ATL::_U_STRINGorID icon, HINSTANCE hResourceInstance = NULL); or HICON SetIcon(HICON hIcon);
Set Icon resource (HICON
) to be drawn in the edit control. Icon will be drawn using ::DrawIconEx()
Win32 API function.
void SetIconMargins(USHORT cx, USHORT cy = 0); or void SetIconMargins(LPSIZE szMargins);
Set margins for icon drawing area. Refer to the picture below:
void GetIconMargins(LPSIZE szMargins);
Get margins for icon drawing area.
void ShowIcon(BOOL bShow = TRUE);
Determine whether to show the specified icon or not.
History
Version 1.02 - 10/26/2004
- Added
UnsubclassWindow();
.
- It draws disabled icon when the edit control is disabled.
- It works for multi-line Edit control (
ES_MULTILINE
style) even though the control wasn't intended to be used in that way.
Version 1.01 - 09/29/2004
- removed
EM_GETRECT
and EM_GETMARGINS
message handlers.
- changed
EM_SETRECT
, EM_SETRECTNP
and EM_SETMARGINS
message handlers.
- added
GetIconMargins()
function.
- updated WTL demo project and added mix-in class (for dialog) to support title tip for edit control. (Refer to
CTitleTipEdit
class and CMainDlg
class for details).
New upload - 09/28/2004
- added Win32 version source code (no WTL/ATL/MFC required) and demo project to show how to use it in MFC. (Note: Win32 version's public function names are changed to avoid conflictions with
CWnd
function names).
Version 1.0 - 09/27/2004
- Initial release on CodeProject.