Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / WTL
Article

WTL Icon Edit

Rate me:
Please Sign up or sign in to vote.
4.72/5 (14 votes)
28 Oct 20044 min read 64.2K   2.8K   22   5
An article on WTL edit control with Icon.

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 /*uMsg*/, WPARAM /*wParam*/,
  LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
  // subclass edit control
  c_edTest.SubclassWindow(GetDlgItem(IDC_ED_TEST));
  // set the icon
  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 /*lParam*/, BOOL& /*bHandled*/)
 {
  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 /*uMsg*/, WPARAM /*wParam*/,
  LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
  // Subclass edit contol
  c_edBlack.SubclassWindow(GetDlgItem(IDC_ED_BLACK));

  // Set the Icon for 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Other
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
waxmachine2-Oct-10 4:41
waxmachine2-Oct-10 4:41 
QuestionHow about a transparent icon? Pin
magenof21-Jul-06 11:26
magenof21-Jul-06 11:26 
QuestionHow about using it with CComboBoxEx? Pin
dEuS24-Jun-06 22:50
dEuS24-Jun-06 22:50 
Generalanother approach Pin
.dan.g.30-Sep-04 13:24
professional.dan.g.30-Sep-04 13:24 
an alternative approach which i favour and which can be appied more easily to other controls is to draw in the non-client area of the control after handling WM_NCCALCSIZE to decrease the client area on the side(s) on which you want to draw.

rgds

.dan.g.

AbstractSpoon Software
GeneralRe: another approach Pin
JaeWook Choi1-Oct-04 5:10
JaeWook Choi1-Oct-04 5:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.