Click here to Skip to main content
13,252,195 members (55,375 online)
Click here to Skip to main content
Add your own
alternative version


30 bookmarked
Posted 7 Nov 2004

A Read-Only Combo with Selectable Text

, 30 Nov 2005
Rate this:
Please Sign up or sign in to vote.
A combo box which behaves as a fully disabled combo box except that it allows you to select the text.



Rarely in the field of coding has so much energy been expended to produce so little. This problem is one of those itches that much be scratched; though trivial, it cannot be left alone until solved - something only true nit-pickers can appreciate. The problem is to create a "disabled" combo box which allows you to select the text in the edit box, or read-only combo box if you prefer.


The fundamental problem is that, once you make a combo read-only by conventional means (disabling), you can't do anything with the text. This is frustrating because sometimes selection-and-copying is useful. There's an article by Tim McColl here which changes the edit color of a disabled combo to make it more readable, but doesn't allow selection. In one of my own projects, I did get as far as making the text in the edit box selectable without actually disabling the combo box; I simply blocked mouse clicks on the arrow and key presses in the edit in "read-only" mode. But then the problem morphed into an aesthetic one: the arrow remained frustratingly "enabled".

The (supposedly definitive) solution which I originally posted to this problem, which lets us face it, was a little bit ugly, and consisted in recreating the combo box as a "simple" combo and indulging in various forms of evil voodoo to make it look like a drop-down one. The idea came from Rich Frank, who had used Paul S. Vickery's RecreateComboBox function to change the combo type between simple and drop-down, and thus hide the arrow when he didn't want any editing - not quite what I wanted, but it didn't take much to imagine the rest - just paint the arrow in and voila.

All very devious, but its lack of elegance suggested that there had to be a better way. There was, and it was blindingly obvious. When the combo paints itself, just mask out the arrow part to prevent the enabled arrow being painted, and then paint in the disabled one instead.

Actually, this solution does create another minor problem, namely that the drop-list-type combos are no longer amenable to the read-only treatment, since they don't have edit controls, or indeed any other sort of child control that might allow selecting. This is pretty easy to solve, but involves a slightly dirty trick. Both drop-down and drop-list combos need to be created with the drop-down style, so that there's always an edit for us to play with. A flag, set in the handler, then determines the combo type. The handling is almost the same: the read-only mode is identical, and for editable drop-lists, the only thing we do is block more keys to stop the text from changing.

If you're wondering what I mean by "handler", it refers to the fact that I decided to avoid implementing this functionality in a derived class and use subclassing instead. See the section No Inheritance Required below.

How it works

First, what we need to do is make the normal "enabled" arrow disappear when we're in read-only mode. It's frighteningly simple once you know how. First, you need to know the exact position and dimensions of the combo arrow, which is not quite as trivial as it sounds. I hacked around for ages with borders and edges, trying to get the damn thing to work with all the Windows XP themes I could throw at it, as well as in the Windows classic mode, but no. It was never quite right. I tried the theme metrics functions, but I could never get them to work at all, for some reason. Then I discovered GetComboBoxInfo(), and proceeded to kick myself quite hard. Where had that function been all my life? It gives you all the dimensions in three lines.

Armed with this, the masking function is a doddle.

 void CReadOnlyComboHandler::MaskComboArrow (CDC& dc)
  CRect rectArrow;
  if (m_bReadOnly)
   rectArrow = GetComboArrowRect();
   // Exclude the arrow area from the paint update region

You just call this before calling the standard paint function, and bob's your uncle. OK, so having made the arrow disappear, all that remains is to paint a new, disabled one. The only complicating factor is the handling of XP themes. To do this, I've made use of a modified version of David Yuheng Zhao's Visual XP Styles class, which safely wraps the calls to the XP themes DLL. I've made some changes, in particular, in the routine which checks whether themes are in use. I've found that you need to check the version of the ComCtl32 DLL loaded by the app in addition to other checks, to be sure that themes are activated. Here, by the way, in GetComboArrowRect(), is where we make use of that nice function GetComboBoxInfo().

void CReadOnlyComboHandler::DrawDisabledDropArrow(CDC* pDC)
 ASSERT(m_pCombo != NULL);
 // If read-only, draw a disabled down-arrow
 if (m_bReadOnly && m_pCombo && pDC)
  CRect rectArrow = GetComboArrowRect();
  // Now make the arrow rect the clipping region
  // and paint the arrow
  CRgn rgnArrow;
  // Draw control at RHS of control
  // If app is themed...
  if (CVisualStylesXP::GetInstance()->IsAppUsingThemes())  
   HTHEME hTheme = 

                         pDC->GetSafeHdc(), CP_DROPDOWNBUTTON, 
                         CBXS_DISABLED, &rectArrow, NULL);
   // Do the draw 
   pDC->DrawFrameControl(rectArrow, DFC_SCROLL, 
                         DFCS_SCROLLDOWN | DFCS_INACTIVE);

Another point worth commenting on is that, to prevent the arrow keys being used to change the combo selection, we also need to install a handler for the edit box and use it to block these key presses when the edit is read-only. Previously, I used a derived edit class, but this is much cleaner. The handling of key presses is a little more refined now too; you can always use the arrow keys for selection, and you can use the up/down keys to change the selection in drop-list mode, even though you're prevented from editing. CTRL-C (or whatever the local combination is) should also work for copying, and I think I managed to stop all backspaces and deletes. As a final touch, the edit handler also blocks the cut and paste commands when in read-only mode.

One caveat: the code is expecting a drop-down or drop-list combo only. Using it with a "simple" combo makes little sense; I'm not sure if the results would be benign or not. I advise against it. In fact, using it with any type other than drop-down is a little pointless, since the drop-list style doesn't give you an edit and thus prevents you from selecting text, which is the whole point of this exercise.

In this update, I've changed the CComboBox pointer to an HWND to avoid problems with window maps when this class is used in a DLL.

No Inheritance Required

From version v1.1, all this is implemented in a "plug-in" handler class, rather than as a derived combo box class. This allows you to add the read-only functionality to any combo class, including your own derived MuchBetterSuperWidgetCombo classes. Well, that's the nice theory anyway. To do this, the handler replaces the combo's WindowProc with its own custom procedure to make the required modifications, doing the same for the combo's edit control. I've essentially copied this idea from GipsySoft's BuddyButton, but implemented it in a class rather than as a global function. I've also included a facility to subclass the combo edit control, because it was something I needed to do. SubclassComboEdit() and UnsubclassComboEdit() will do your subclassing and hook and unhook my handlers accordingly to make everything work. Adding the same functions for the combo would be simple, but I haven't done it.

Using the code

Pretty easy really. You just create an instance of the CReadOnlyComboHandler class and initialize it with your combo by calling Init(pMyCombo). You call SetReadOnly(true) on the handler to make it read-only. If you want a "drop-list"-style combo, create the combo with the drop-down style, and call SetDropListMode(true). You can, of course, include the handler as a member of your own derived combo class if you wish. I suppose I should include a derived combo class with the handler as a member for those who don't have their own derived classes and don't want to have to keep track of combo handlers. Maybe next year :-).

I'd love to hear of people's experiences with it, as well as reports of bugs, problems, improvements, etc.

Debts to pay

I have consulted or used a frightening amount of other people's code to accomplish such a modest objective. In particular:


  • v1.0 - 8 November 2004 - Posted.
  • v1.1 - 21 November 2004 - Re-implemented as an external handler class. Made compatible with drop-list combos.
  • v1.3 - 22 November 2005 - Massively delayed update. Combo no longer recreated, arrow fixed by masking. Now gets arrow dimensions correctly for all XP themes. Made combo into HWND.


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


About the Author

David Pritchard
Software Developer (Senior)
Spain Spain
I'm originally from Leek, Staffordshire in the UK, but I now work as a C++/MFC developer in Madrid, Spain.

I followed an erratic study/career path from German to a PhD in something resembling political science and linguistics, eventually ending up in IT.

I'm still finding bustling streets, warm nights, beer and vitamin D a pretty heady combination.

You may also be interested in...


Comments and Discussions

GeneralEasier solution [modified] Pin
ZoneShader22-Jul-07 1:05
memberZoneShader22-Jul-07 1:05 
A lot of code for a little effect,
no problem for me Wink | ;) but i think a bit more non-XP support is always good, as long as you know how.

I actually wondered that a prof. developer didnt find an easier solution because hey, im still in school ^_^"

I do not know how exactly the following code looks like in MFC, since I use a library developed by myself, so I'll just write it in WinAPI:

use the following code if you want to make your CBS_DROPDOWN combobox be read-only, that means you can still select items from the list, and you can select the text in the combobox but you cant type/change it.

<font color=green>// hComboBox is the HWND of the ComboBox</font>
cbi.cbSize = <font color=blue>sizeof</font>(COMBOBOXINFO);
GetComboBoxInfo(hComboBox, &cbi); <font color=green>// retrieve the HWND to the edit-ctrl insode the combo</font>
SendMessage(cbi.hwndItem, EM_SETREADONLY, <font color=blue>true</font>, NULL); <font color=green>// set the edit-ctrl readonly</font>

use the following code to disable the combobox, but let the user select the disabled text:

<font color=green>// hComboBox is the HWND of the ComboBox, hWnd of the main window</font>
cbi.cbSize = <font color=blue>sizeof</font>(COMBOBOXINFO);
GetComboBoxInfo(hComboBox, &cbi); <font color=green>// Get the edit-ctrl handle "cbi.hwndItem"</font>
<font color=green>// Get the co-ords of the edit ctrl relative to our window, the owner window of the combobox</font>
POINT p = {0, 0};
ClientToScreen(cbi.hwndItem, &p);
ScreenToClient(hWnd, &p);
<font color=green>// change edit-ctrl's owner. Then the edit-ctrl is inside our window and not inside the combobox</font>
SetWindowLongPtr(cbi.hwndItem, GWL_HWNDPARENT, (LONG)hWnd); <font color=green>// use SetWindowLong with older VC++s'</font>
<font color=green>// place the edit-ctrl at same position as it was earlier inside the combobox</font>
<font color=green>// place the edit-ctrl over the combobox in the z-order</font>
<font color=green>// Disable the combobox. If you enable the combobox instead here (change the false to a true) then the code has same effect as the code above</font>
EnableWindow(hComboBox, <font color=blue>false</font>);
<font color=green>// Enable the edit ctrl</font>
EnableWindow(cbi.hwndItem, <font color=blue>true</font>);
<font color=green>// Make the edit ctrl read only</font>
SendMessage(cbi.hwndItem, EM_SETREADONLY, <font color=blue>true</font>, NULL);


-- modified at 6:10 Sunday 22nd July, 2007
GeneralRe: Easier solution Pin
David Pritchard22-Jul-07 4:45
memberDavid Pritchard22-Jul-07 4:45 
GeneralRe: Easier solution Pin
ZoneShader22-Jul-07 23:56
memberZoneShader22-Jul-07 23:56 
GeneralRe: Easier solution Pin
David Pritchard23-Jul-07 4:14
memberDavid Pritchard23-Jul-07 4:14 
GeneralCompiling Pin
cucubau5-Dec-05 21:21
membercucubau5-Dec-05 21:21 
GeneralRe: Compiling Pin
cucubau5-Dec-05 21:51
membercucubau5-Dec-05 21:51 
GeneralRe: Compiling Pin
David Pritchard5-Dec-05 23:52
memberDavid Pritchard5-Dec-05 23:52 
GeneralDropList style Pin
dbucci15-Nov-04 2:34
memberdbucci15-Nov-04 2:34 
GeneralRe: DropList style Pin
David Pritchard15-Nov-04 11:28
memberDavid Pritchard15-Nov-04 11:28 
GeneralRe: DropList style Pin
xufeisjtu15-Sep-05 15:47
memberxufeisjtu15-Sep-05 15:47 
GeneralRe: DropList style Pin
David Pritchard30-Nov-05 13:30
memberDavid Pritchard30-Nov-05 13:30 
GeneralGets my 5 Pin
.dan.g.8-Nov-04 15:56
member.dan.g.8-Nov-04 15:56 
GeneralRe: Gets my 5 Pin
David Pritchard9-Nov-04 5:05
memberDavid Pritchard9-Nov-04 5:05 
GeneralGreat Pin
the-unforgiven8-Nov-04 9:00
memberthe-unforgiven8-Nov-04 9:00 
GeneralRe: Great Pin
David Pritchard8-Nov-04 13:20
memberDavid Pritchard8-Nov-04 13:20 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.171114.1 | Last Updated 30 Nov 2005
Article Copyright 2004 by David Pritchard
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid