News Archive
Sort posts by the categories below:
| All Categories | General | Movies | Sports | Programming | Woodworking |
Read-Only Combo Boxes
Posted at 07:09 AM - 10/06/09 in Programming by Dennis
Last year, I was writing a program where I needed some combo boxes that were "read-only". What I mean by that is, if the user is authorized to make changes then the combo-box would operate as normal. If not, then a read-only property was set, and the text portion of the combo couldn't be typed in and the drop down was disabled. I finally found some code to help me out, I can't remember where it came from but it is very well documented: Here's the implementation: 1: using System;
2: using System.Runtime.InteropServices;
3: using System.Windows.Forms;
4: using System.ComponentModel;
5:
6: namespace ReadOnlyComboBoxTestApp
7: {
8: public class exComboBox : ComboBox
9: {
10:
11: #region "-- Declarations --"
12: private bool readOnly;
13: private bool droppedDown = false;
14:
15: private int selectedIndex = -1;
16:
17: private ComboBoxStyle dropDownStyle = ComboBoxStyle.DropDown;
18:
19: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true,
20: ExactSpelling = true)]
21: private static extern IntPtr GetWindow(IntPtr hwnd, UInt32 wCmd);
22:
23: [DllImport("user32.dll", CharSet = CharSet.Auto)]
24: private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg,
25: bool wParam, Int32 lParam);
26:
27: private const UInt32 WM_COMMAND = 0x111;
28: private const UInt32 EM_SETREADONLY = 0xcf;
29: private const UInt32 EM_EMPTYUNDOBUFFER = 0xcd;
30: private const UInt32 CB_SHOWDROPDOWN = 0x14f;
31: private const UInt32 GW_CHILD = 5;
32:
33: #endregion
34:
35: #region "-- Properties --"
36:
37: [Browsable(true), DefaultValue(false)]
38: public bool ReadOnly
39: {
40: get { return readOnly; }
41: set
42: {
43: // In design mode we don't want setting the read only property
44: // to alter the dropdown style.
45: if (!DesignMode)
46: {
47: // If the DropDownStyle is anything other than DropDown then setting
48: // ReadOnly to true will have no affect. Therefore we'll force the style
49: // to DropDown as it goes to ReadOnly and restore it when it's turned off.
50: if (value)
51: {
52: // If the value is changing then we want to save the dropdown style.
53: // In case the value gets set to true more than once we don't want
54: // to lose the saved drop down style.
55: if (readOnly != value)
56: {
57: dropDownStyle = base.DropDownStyle;
58: base.DropDownStyle = ComboBoxStyle.DropDown;
59: }
60: }
61: // restore the saved drop down style
62: else
63: {
64: base.DropDownStyle = dropDownStyle;
65: }
66: }
67: readOnly = value;
68:
69: // If readonly then don't let the user tab to the field
70: base.TabStop = !value;
71:
72: // Setting TabStop to false causes the text in the box to be selected if it matches
73: // an entry in the list. Setting selection length to zero removes the selection.
74: base.SelectionLength = 0;
75:
76: // Send the textbox portion of the combo the readonly message.
77: // It will change the color and behavior.
78: IntPtr windowHandle = GetWindow(this.Handle, GW_CHILD);
79: SendMessage(windowHandle, EM_SETREADONLY, value, 0);
80:
81: // If text was typed or pasted into the textbox, the context menu will
82: // have the undo activated. When the text box is in the readonly state
83: // the undo will still be active from the right click context menu
84: // allowing the user to restore the previous value. This sendmessage
85: // will clear the undo buffer which will clear the undo.
86: SendMessage(windowHandle, EM_EMPTYUNDOBUFFER, value, 0);
87:
88: // the dropdown may have been dropped before the readonly is set
89: droppedDown = false;
90: this.Refresh();
91: }
92: }
93:
94: // Saving and returning a local copy of the selected index keeps a
95: // changed value from being returned when the control is in the
96: // readonly state. The OnSelectedIndexChanged event captures the
97: // index value that is returned here. Must be shadows or else the
98: // value passed won't cause the OnSelectedIndexChanged method to fire
99: // and the text value to be displayed, won't.
100: //
101: public new int SelectedIndex
102: {
103: get { return selectedIndex; }
104: set
105: {
106: selectedIndex = value;
107: base.SelectedIndex = value;
108: // Set it twice to work around databound bug KB327244
109: if (value == -1)
110: {
111: selectedIndex = value;
112: base.SelectedIndex = value;
113: }
114: }
115: }
116: #endregion
117:
118: #region "-- Overrides --"
119:
120: // Intercepting message 273 when readonly and the listbox is dropped
121: // keeps the user from selecting an item in the list and having it update
122: // the text value of the combo as well as firing the associated changed events.
123: // Since we intercept a windows message we will have to manually bring up
124: // the listbox.
125: //
126: // msg 305 (0x131) = an item was clicked from the dropdown list
127: // msg 273 (0x111) = (WM_COMMAND) follows dropdown list click and all other actions?
128: // msg 8465 (0x2111) = (WM_REFLECT + WM_COMMAND) subsequent command after the 273
129: //
130: protected override void WndProc(ref Message m)
131: {
132: // Cannot use me.DroppedDown, it causes a System.StackOverflowException.
133: // Asking for it's value must produce windows messages for the combobox
134: // and thus create a recursive loop.
135: if (readOnly & m.Msg == WM_COMMAND) return;
136:
137:
138: base.WndProc(ref m);
139: }
140:
141: // This event will not fire when msg 273 is intercepted in WndProc. When in the
142: // readonly state clicking on an item in the listbox appears to have no effect. It
143: // does in fact change the value of MyBase.SelectedIndex. Saving the last good index
144: // value locally allows the overriden Index property to supply the proper index value.
145: //
146: protected override void OnSelectedIndexChanged(System.EventArgs e)
147: {
148: selectedIndex = base.SelectedIndex;
149: base.OnSelectedIndexChanged(e);
150: }
151:
152: // We must manually track dropped state. Asking the control if it's
153: // dropped from within WndProc will cause a System.StackOverflowException.
154: //
155: protected override void OnDropDown(System.EventArgs e)
156: {
157: droppedDown = true;
158: base.OnDropDown(e);
159: }
160:
161: // The up and down arrow keys cause the combobox to change selection to the next
162: // or previous in the list. The page up and page down keys change the selection
163: // by one page at a time as defined by the size of the dropdown list. Setting
164: // e.Handled to true if any of these keys is pressed stops the selection change
165: // when readonly. The alt down arrow combination is allowed since it drops the listbox.
166: //
167: protected override void OnKeyDown( KeyEventArgs e)
168: {
169: if (readOnly)
170: {
171: if (e.KeyCode == Keys.Up || e.KeyCode == Keys.PageUp ||
172: e.KeyCode == Keys.PageDown || (e.KeyCode == Keys.Down &
173: ((Control.ModifierKeys & Keys.Alt) != Keys.Alt)))
174: {
175: e.Handled = true;
176: }
177: }
178: base.OnKeyDown(e);
179: }
180:
181: // The combobox default behavior when pressing F4 is to drop the listbox.
182: // If F4 is immediately pressed a second time the OnSelectionChangeCommitted
183: // event fires regardless of whether a change has been made or not. When
184: // readonly we don't want a change event to fire. This code will stop it.
185: //
186: protected override void OnSelectionChangeCommitted(System.EventArgs e)
187: {
188: if (!readOnly)
189: {
190: base.OnSelectionChangeCommitted(e);
191: }
192: }
193: #endregion
194: }
195:
196: }
1: private void button1_Click(object sender, EventArgs e)
2: {
3: exComboBox1.ReadOnly = true;
4: }
5:
6: private void button2_Click(object sender, EventArgs e)
7: {
8: exComboBox1.ReadOnly = false;
9: }
10:
| Title | Date |
| A Boring Blog | 07:30 AM - 08/11/09 |
| I give up! | 06:44 AM - 08/06/09 |
| No more Duncan... | 09:21 PM - 07/22/09 |
| Vacation: Days 4 & 5 | 05:53 AM - 07/04/09 |
| Vacation: Days 2 & 3 | 05:59 PM - 07/02/09 |
| Vacation: Day 1 | 07:20 AM - 06/30/09 |
| Happy Father's Day! | 01:01 PM - 06/21/09 |
| See, I'm not the only one | 06:55 AM - 06/20/09 |
| Just Plain Mad! | 09:07 PM - 06/06/09 |
| Irritated | 09:09 AM - 06/04/09 |
| More flags! More Fun! | 02:26 PM - 06/02/09 |
| I've been Taken! | 08:35 PM - 05/24/09 |
| Let's Celebrate! | 05:57 AM - 05/20/09 |
| Happy Mother's Day! | 05:46 AM - 05/11/09 |
| Up in smoke | 09:39 PM - 04/30/09 |
| He is risen! | 02:01 PM - 04/12/09 |
| Proof that whiners get their way | 09:06 PM - 04/02/09 |
| A few more Josephs | 04:40 PM - 03/29/09 |
| Bad Dog! No Biscuit! | 08:39 AM - 03/27/09 |
| OMG! They killed Kenny! | 08:12 PM - 03/25/09 |
| Is it just me? | 06:14 AM - 03/24/09 |
| Whatcha been up to? | 08:11 PM - 03/08/09 |
| Mmmmhhhhhmmmm! | 09:34 PM - 02/15/09 |
| A Question... | 07:35 PM - 02/13/09 |
| Am I the only one? | 09:08 PM - 02/09/09 |
