What's New
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:
Comments: 1
A Boring Blog Posted at 07:30 AM - 08/11/09 in General by Dennis
This is a boring blog. My brother complained that since I joined Facebook, I don't write any boring blogs. So, here you are.
Comments: 4
I give up! Posted at 06:44 AM - 08/06/09 in General by Dennis
I had a startling revelation last night (My Mom's fault).
Earlier this week, I signed up for facebook. My wife (and my brother, mother, father and brother-in-law) all play a game on facebook called FarmTown. My wife needed another "neighbor", so I got elected. I am now addicted to the maddening site and that blasted game. I have been posting photos, adding friends, and playing games for the past several days. It's aggravating to me because it has cut into my programming time. Arrrgh! I feel so weak. *Sob*
No more Duncan... Posted at 09:21 PM - 07/22/09 in Sports by Dennis
Chris Duncan is no longer a St. Louis Cardinal. The Redbirds traded him (and a player to be named later) to Boston in exchange for Julio Lugo. According to the story on the Cardinal's website says he'll add depth at shortstop. I hope Chris does well in Boston, I think he'd make a pretty good first baseman.
Comments: 1
Vacation: Days 4 & 5 Posted at 05:53 AM - 07/04/09 in General by Dennis
Day 4 Mission: Visit a cheese factory & Relax
7:30am - Oops, we slept in.
8:30am - We're slowly getting up and around (I don't remember it being this difficult to recover from Six Flags).
9:30am - Off to find some real coffee and a bagel.
10:00am - At my brother's house, ready to go.
10:30am - Leaving my brother's to visit the cheese place.
11:00am - The 15min drive is actually a little longer (like 15min longer).
12:15pm - Back in Janesville, eating lunch at Famous Dave's (How famous can he be? I've never heard of him).
1:30pm - He should be famous, they brought us a massive amount of smoked/BBQ'd meat. I'm stuffed.
2:30pm - We hit the pool/hot tub for some relaxation and fun.
6:00pm - We decide that dinner will be at Red Robin (yummmm!).
7:00pm - Over to my brother's for some more visitin'.
10:00pm - Back at the hotel, we're going home in the morning. :(
Day 5 Mission: Head home
8:10am - On the road. This is an all time vacation record!
8:25am - I love toll roads. Did you know that in the Chicago area there's like a bazillion toll roads?
8:36am - Did you know that all of the bazillion toll roads are under construction with signs that say the construction is funded by the stimulus package? (How does that make sense? Where is the 4.5 bazillion dollar revenue going?)
12:30pm - We stop to stretch our legs at the Tanger Outlet Mall in Tuscola, IL.
1:30pm - Grab lunch before getting back on the road
2:30pm - Driving through Central Illinois puts me to sleep, luckily there's a Starbucks in Effingham. I love caramel macchiato.
4:00pm - We arrive at my in-law's house to pick up our spoiled little dog.
5:10pm - Back on the road... again... last leg... almost there...
6:45pm - We pull into the driveway. Ahhh, home. My big old mutt-dog is happy to see me.
All in all, a fun vacation. My nephew is getting big, and I really enjoyed seeing him again. Oh yeah, it was nice seeing my brother and his wife, too.
Happy 4th of July to everyone! Have fun, but be safe.
Comments: 1
