Click here to Skip to main content
Click here to Skip to main content
Go to top

A Complete Read Only ComboBox

, 18 Jun 2012
Rate this:
Please Sign up or sign in to vote.
A ComboBox with a read-only property that allows text copy and drop-down viewing

Introduction

Like so many others, our developers were taken in with the look and feel of the TextBox's ReadOnly property. Compared to a disabled TextBox, it is legible yet conveys a change in the state of the control. You cannot change its value but if needed you can copy the text out of it. Since the ComboBox is an enhanced TextBox, it was natural to look for a ReadOnly property, but as you all know, it's not there. Oh, but it is there. It's not exposed in the Framework but it lies underneath and can be accessed with a single line of code. Well... sort of.

The essence of giving the look and feel of the ReadOnly property does take one line of code, but plugging the holes around it takes a few more. There are a couple of other articles here on The Code Project by Dan Anatoli and Claudio Grazioli that create the look of a ReadOnly state and some of its feel, but their solutions didn't give me what I expected in a read only ComboBox. What I expected was that it should still present the dropdown list in case the user wanted to see what the other selections were. It should allow copying text out of the box either with a Ctrl+C key press or by using a right click context menu. Of course, when in ReadOnly mode, it should not allow a change.

A Single Line of Code

Searching in Google has become a job requirement. I began the hunt for that magic combination of keywords that would lead me to a post where someone would give me a little known property or method that would solve my problem. Hopefully it would be a single line of code. In this case, it started out looking like it might be that simple.

SendMessage(GetWindow(Me.Handle, GW_CHILD), _
                      EM_SETREADONLY, Value, 0)

You can interact with Windows controls by sending messages to them. These messages are documented on MSDN. In the case of the ComboBox, you can send it the EM_SETREADONLY message. As soon as you do, the TextBox portion of the control looks and acts just like you'd expect; the color changes, you can't type in the box anymore, you can't paste into it, you can't use any of the editing keys, but... (and this is a big but) the ListBox portion of the control doesn't know a thing about what you have just done. It still allows you to select new values, changing the text in the box and the selected index of the list. This is where you have to start plugging the holes.

Plugging the Holes

There are four holes that are left after the TextBox within the combo has been placed in the ReadOnly state. They are:

  • Drop down list mouse selections
  • Undo in the right mouse button context menu
  • Using cursor keys to select from the list
  • Bringing up the list with repeated F4 presses

Drop Down List Mouse Selections

I wanted the user to be able to view the list even if she or he could not change the selection. There were two things that happen when you click on an item in the list. It changes the text in the text box and it changes the SelectedIndex. To stop the text from being changed I found that if I intercepted Windows message 273 when in a dropped down state, that would keep the text from changing. Message 273 also caused the list to be pulled up. So I had to send a message to the combo to tell it to bring up the list.

Protected Overrides Sub WndProc(_
              ByRef m As System.Windows.Forms.Message)
    If _ReadOnly AndAlso _DroppedDown Then
        If m.Msg = 273 Then
            _DroppedDown = False
            SendMessage(Me.Handle, CB_SHOWDROPDOWN, _
                       System.Convert.ToInt32(False), 0)
            Exit Sub
        End If
    End If
    MyBase.WndProc(m)
End Sub

That stopped the text from changing but not the SelectedIndex. To handle that, I had to take control of the SelectedIndex property. When message 273 is absorbed in the WndProc override the OnSelectedIndexChanged method is not called. By saving the value of SelectedIndex locally, I can have the last selected value to pass back in a shadowed SelectedIndex property. I had to shadow the property or else the OnSelectedIndexChanged method would not get called.

Protected Overrides Sub OnSelectedIndexChanged(_
                        ByVal e As System.EventArgs)
    _SelectedIndex = MyBase.SelectedIndex
    MyBase.OnSelectedIndexChanged(e)
End Sub

Public Shadows Property SelectedIndex() As Integer
    Get
        Return _SelectedIndex
    End Get
    Set(ByVal Value As Integer)
        _SelectedIndex = Value
        MyBase.SelectedIndex = Value
    End Set
End Property

Undo in the Right Mouse Button Context Menu

The right click context menu of the TextBox has an undo option. If the user were to paste in a value or hand type a change, the undo option will be enabled in the context menu. If there was an undo in progress when the control was placed in a ReadOnly state, it would be possible to get around the ReadOnly and revert to the previous value. But luckily this was one of those one line wonders.

SendMessage(GetWindow(Me.Handle, _
          GW_CHILD), EM_EMPTYUNDOBUFFER, Value, 0)

The SendMessage function causes the text box in the control to empty its undo buffer and disable the undo context menu selection. Next was the cursor keys.

Using Cursor Keys to Select from the List

Whether the list is dropped down or not, both the up and down cursor keys and the page up and down keys will allow you to change the selected value. This can be handled with a little override of the OnKeyDown method. Note that the Alt + Cursor Down key is allowed as it can be used to drop the list box.

Protected Overrides Sub OnKeyDown(_
        ByVal e As System.Windows.Forms.KeyEventArgs)
    If _ReadOnly Then
        If e.KeyCode = Keys.Up OrElse _
             e.KeyCode = Keys.PageUp OrElse _
             e.KeyCode = Keys.PageDown OrElse _
             (e.KeyCode = Keys.Down And _
               ((Me.ModifierKeys And Keys.Alt) <> Keys.Alt)) Then
           e.Handled = True
        End If
    End If
    MyBase.OnKeyDown(e)
End Sub

Bringing up the List with Repeated F4 Presses

Last was an odd bug in the ComboBox itself. If you press F4 the list will drop. If you press it again, it will bring up the list and fire the OnSelectedChangeCommitted event even though nothing has changed. Putting in the following code stops the event when the control is in the ReadOnly state. This does nothing for the inappropriate firing of the event when the control is writable.

Protected Overrides Sub OnSelectionChangeCommitted(_
                           ByVal e As System.EventArgs)
    If _ReadOnly Then
    Else
        MyBase.OnSelectionChangeCommitted(e)
    End If
End Sub

Using the Code 

The code was written as a class that can be added into a class library, compiled, and added to the toolbox. When I first tried this class out on the other developers where I work, I named the control ReadOnlyComboBox. I was immediately assaulted with comments such as "Why would you want a ComboBox that can only be read?" and "What's the point of giving them a list if they can never change the value?" OK, so it's not a ComboBox that is ReadOnly. It's a ComboBox with a ReadOnly property. I resorted to the time honored technique of adding an "X" to the inherited class name. In the sample project it is named xboXComboBox. All of our controls are prefixed with a three character designation, then a significant name after that.

By the way, the ZIP file contains the source and a test project. You may need to build the solution before you can view the test form in the designer.

2012 Update

As we've used the control on my team and as Code Project users have submitted comments there have been various fixes and enhancements.  When this article was written the current version of the .Net framework didn't include an auto complete feature in the ComboBox. It came as a surprise that  AutoCompleteMode opened a hole where the control could be changed when ReadOnly. The fix was simple by just setting AutoCompleteMode to None when going to ReadOnly and then restoring the previous value when coming back out.

There were several issues related to clearing the selection. The most complicated case came when the control was bound to a numeric property in a class. Clearing the control would place a -1 in SelectedIndex. The control would require the user to select an item from the list before they would be allowed to exit. The fix was to override the -1 with a zero so that the first item in the list is selected. This means that when binding to a numeric property it is not possible to clear the selection. Another option is to use a Nullable(Of) numeric property.  This will allow the ComboBox to properly handle clearing the selection. 

Summary

In putting this class together I thought I had something special. It wasn't large or complex but I did have to define a call to user32.dll and I did have to intercept and handle Windows messages. My understanding of Windows messages came from watching them and seeing what happened if I didn't pass them along. Does that make the code vulnerable? I don't think so. I look forward to your thoughts. So far our implementation has been successful and we are very pleased. It was great to find a way to interact with the underlying control and pull a few more horsepower out of it. For me, this was a more complete solution, handling all of the look and behavior that I expected from a ReadOnly property of a ComboBox

History 

  • 09/08/05
    • Initial release
  • 08/14/06
    • Ported from Visual Studio 2003 to Visual Studio 2005
    • Added support for DropDownStyle = DropDownList
    • Added workaround for setting SelectedIndex to -1 when data-bound, see KB327244
  • 01/23/08
    • Fixed bug which lost DropDownStyle when ReadOnly set to true two times in a row
  • 06/15/12
    • Disabled AutoCompleteMode when ReadOnly to prevent updates via the keyboard 
    • Force FormattingEnabled to prevent selection of the first item in the list after clearing  
    • Fixed roach motel behavior after clearing when control bound to a numeric property 
    • Converted comments to XML Summary and Remarks 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Thomas Wells
Software Developer (Senior) Iowa Department of Transportation
United States United States
Tom works for the DOT as a developer and project leader. Besides designing VB.Net SQL Server applications he specializes in creating tools, classes, and controls for the development team where he works.

Comments and Discussions

 
Questionit seems that the readonly feature would lost if set to autocomplete... Pinmemberjtxd31-May-12 2:00 
AnswerRe: it seems that the readonly feature would lost if set to autocomplete... PinmemberThomas Wells31-May-12 12:25 
AnswerRe: it seems that the readonly feature would lost if set to autocomplete... PinmemberThomas Wells19-Jun-12 4:24 
QuestionDatabound and SelectionLength [modified] PinmemberYoyosam27-Jan-12 10:55 
AnswerRe: Databound and SelectionLength PinmemberThomas Wells27-Jan-12 12:21 
GeneralRe: Databound and SelectionLength PinmemberYoyosam27-Jan-12 15:09 
GeneralRe: Databound and SelectionLength PinmemberThomas Wells30-Jan-12 7:32 
SuggestionI added the faeture not to show Dropdown and readonly change event PinmemberKOUKOSDD31-Aug-11 2:38 
GeneralProblem appears when style is ComboBoxStyle.DropDownList and toggling ReadOnly Pinmemberjtxd17-Feb-11 19:58 
GeneralRe: Problem appears when style is ComboBoxStyle.DropDownList and toggling ReadOnly Pinmemberjtxd17-Feb-11 22:04 
QuestionA fix to your code when press AltGr and press down key with control set ReadOnly = true Pinmemberplaguebreath29-Jul-10 4:16 
AnswerRe: A fix to your code when press AltGr and press down key with control set ReadOnly = true PinmemberThomas Wells29-Jul-10 7:57 
GeneralProblem with Strict set to On and browsable and defaultvalue Pinmembermgardiner29-Aug-08 10:03 
GeneralRe: Problem with Strict set to On and browsable and defaultvalue PinmemberThomas Wells29-Aug-08 10:24 
GeneralI do not understand... Pinmemberpbnec18-Aug-08 0:10 
GeneralRe: I do not understand... PinmemberThomas Wells18-Aug-08 4:21 
AnswerRe: I do not understand... Pinmemberpbnec19-Aug-08 20:49 
QuestionIs it possible to copy the text when... Pinmemberpbnec24-Jul-08 18:29 
AnswerRe: Is it possible to copy the text when... PinmemberThomas Wells25-Jul-08 10:07 
Generalcreated C# version of your control PinmemberMotero25-Apr-08 2:16 
GeneralRe: created C# version of your control PinmemberThomas Wells25-Apr-08 3:28 
AnswerMy Inherited ComboBox Pinmemberpbnec18-Aug-08 15:59 
GeneralRe: created C# version of your control PinmemberFelix Todd3-Nov-08 12:52 
GeneralA small patch to handle setting the value of the ReadOnly property PinmemberJeffrey Bradley16-Jan-08 1:27 
GeneralRe: A small patch to handle setting the value of the ReadOnly property PinmemberThomas Wells24-Jan-08 3:10 
GeneralThanks for a great custom control PinmemberJeffrey Bradley14-Jan-08 2:33 
GeneralRe: Thanks for a great custom control PinmemberThomas Wells14-Jan-08 3:21 
GeneralGreat control but... PinmemberTowerIT12-Nov-07 6:44 
GeneralRe: Great control but... PinmemberThomas Wells13-Nov-07 3:23 
QuestionRead Only ComboBox Pinmemberkk_upadhyay3-Oct-07 0:13 
AnswerRe: Read Only ComboBox PinmemberThomas Wells3-Oct-07 3:30 
QuestionDropDownStyle? PinmemberJoshua Muller15-Feb-07 11:57 
AnswerRe: DropDownStyle? PinmemberThomas Wells16-Feb-07 3:49 
GeneralRe: DropDownStyle? PinmemberIain Clarke27-Jan-08 21:59 
GeneralRe: DropDownStyle? PinmemberThomas Wells28-Jan-08 7:17 
QuestionIt's great, any way we can specify the backcolor? Pinmembernickxiaow9-Feb-07 3:35 
AnswerRe: It's great, any way we can specify the backcolor? PinmemberThomas Wells9-Feb-07 3:51 
GeneralRe: It's great, any way we can specify the backcolor? Pinmembernickxiaow9-Feb-07 4:29 
GeneralRe: It's great, any way we can specify the backcolor? Pinmemberakaz3-Jun-08 4:57 
GeneralRe: It's great, any way we can specify the backcolor? PinmemberNathan Brown23-Oct-12 6:03 
Generalautoellipse PinmemberBasatwar8-Jan-07 23:06 
GeneralRe: autoellipse PinmemberThomas Wells9-Jan-07 8:20 
GeneralInconsistent results at DesignTime PinmemberSBendBuckeye4-Jan-07 9:18 
AnswerRe: Inconsistent results at DesignTime PinmemberThomas Wells4-Jan-07 10:28 
GeneralRe: Inconsistent results at DesignTime PinmemberSBendBuckeye4-Jan-07 10:46 
GeneralClearing SelectionLength PinmemberSBendBuckeye4-Jan-07 7:16 
AnswerRe: Clearing SelectionLength PinmemberThomas Wells4-Jan-07 8:13 
GeneralSelectedIndex = -1 problem PinmemberRusken1-Jun-06 23:57 
GeneralRe: SelectedIndex = -1 problem PinmemberThomas Wells14-Aug-06 4:52 
QuestionRe: SelectedIndex = -1 problem PinmemberJason Law7-Feb-07 17:37 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 18 Jun 2012
Article Copyright 2005 by Thomas Wells
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid