1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2006 Novell, Inc.
21 //
22 // Authors:
23 //	Jordi Mas i Hernandez, jordi@ximian.com
24 //	Mike Kestner  <mkestner@novell.com>
25 //
26 //
27 
28 using System;
29 using System.Drawing;
30 using System.Collections;
31 using System.ComponentModel;
32 using System.Reflection;
33 using System.Runtime.InteropServices;
34 
35 namespace System.Windows.Forms
36 {
37 	[ClassInterface (ClassInterfaceType.AutoDispatch)]
38 	[ComVisible (true)]
39 	[LookupBindingPropertiesAttribute ()]
40 	public class CheckedListBox : ListBox
41 	{
42 		private CheckedIndexCollection checked_indices;
43 		private CheckedItemCollection checked_items;
44 		private Hashtable check_states = new Hashtable ();
45 		private bool check_onclick = false;
46 		private bool three_dcheckboxes = false;
47 
CheckedListBox()48 		public CheckedListBox ()
49 		{
50 			checked_indices = new CheckedIndexCollection (this);
51 			checked_items = new CheckedItemCollection (this);
52 			SetStyle (ControlStyles.ResizeRedraw, true);
53 		}
54 
55 		#region events
56 		static object ItemCheckEvent = new object ();
57 
58 		[Browsable (true)]
59 		[EditorBrowsable (EditorBrowsableState.Always)]
60 		public new event EventHandler Click {
61 			add { base.Click += value; }
62 			remove { base.Click -= value; }
63 		}
64 
65 		[Browsable (false)]
66 		[EditorBrowsable (EditorBrowsableState.Never)]
67 		public new event EventHandler DataSourceChanged {
68 			add { base.DataSourceChanged += value; }
69 			remove { base.DataSourceChanged -= value; }
70 		}
71 
72 		[Browsable (false)]
73 		[EditorBrowsable (EditorBrowsableState.Never)]
74 		public new event EventHandler DisplayMemberChanged {
75 			add { base.DisplayMemberChanged += value; }
76 			remove { base.DisplayMemberChanged -= value; }
77 		}
78 
79 		[Browsable (false)]
80 		[EditorBrowsable (EditorBrowsableState.Never)]
81 		public new event DrawItemEventHandler DrawItem {
82 			add { base.DrawItem += value; }
83 			remove { base.DrawItem -= value; }
84 		}
85 
86 		[Browsable (false)]
87 		[EditorBrowsable (EditorBrowsableState.Never)]
88 		public new event MeasureItemEventHandler MeasureItem {
89 			add { base.MeasureItem += value; }
90 			remove { base.MeasureItem -= value; }
91 		}
92 
93 		[Browsable (false)]
94 		[EditorBrowsable (EditorBrowsableState.Never)]
95 		public new event EventHandler ValueMemberChanged {
96 			add { base.ValueMemberChanged += value; }
97 			remove { base.ValueMemberChanged -= value; }
98 		}
99 
100 		public event ItemCheckEventHandler ItemCheck {
101 			add { Events.AddHandler (ItemCheckEvent, value); }
102 			remove { Events.RemoveHandler (ItemCheckEvent, value); }
103 		}
104 
105 		[Browsable (true)]
106 		[EditorBrowsable (EditorBrowsableState.Always)]
107 		public new event MouseEventHandler MouseClick {
108 			add { base.MouseClick += value; }
109 			remove { base.MouseClick -= value; }
110 		}
111 		#endregion Events
112 
113 		#region Public Properties
114 
115 		[Browsable (false)]
116 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
117 		public CheckedListBox.CheckedIndexCollection CheckedIndices {
118 			get {return checked_indices; }
119 		}
120 
121 		[Browsable (false)]
122 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
123 		public CheckedListBox.CheckedItemCollection CheckedItems {
124 			get {return checked_items; }
125 		}
126 
127 		[DefaultValue (false)]
128 		public bool CheckOnClick {
129 			get { return check_onclick; }
130 			set { check_onclick = value; }
131 		}
132 
133 		protected override CreateParams CreateParams {
134 			get { return base.CreateParams;}
135 		}
136 
137 		[EditorBrowsable (EditorBrowsableState.Never)]
138 		[Browsable (false)]
139 		public new object DataSource {
140 			get { return base.DataSource; }
141 			// FIXME: docs say you can't use a DataSource with this subclass
142 			set { base.DataSource = value; }
143 		}
144 
145 		[EditorBrowsable (EditorBrowsableState.Never)]
146 		[Browsable (false)]
147 		public new string DisplayMember {
148 			get { return base.DisplayMember; }
149 			set { base.DisplayMember = value; }
150 		}
151 
152 		[Browsable (false)]
153 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
154 		[EditorBrowsable (EditorBrowsableState.Never)]
155 		public override DrawMode DrawMode {
156 			get { return DrawMode.Normal; }
157 			set { /* Not an exception, but has no effect. */ }
158 		}
159 
160 		[Browsable (false)]
161 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
162 		[EditorBrowsable (EditorBrowsableState.Never)]
163 		public override int ItemHeight {
164 			get { return base.ItemHeight; }
165 			set { /* Not an exception, but has no effect. */ }
166 		}
167 
168 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
169 		[Localizable (true)]
170 		[Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
171 		public new CheckedListBox.ObjectCollection Items {
172 			get { return (CheckedListBox.ObjectCollection) base.Items; }
173 		}
174 
175 		public override SelectionMode SelectionMode {
176 			get { return base.SelectionMode; }
177 			set {
178 				if (!Enum.IsDefined (typeof (SelectionMode), value))
179 					throw new InvalidEnumArgumentException ("value", (int) value, typeof (SelectionMode));
180 
181 				if (value == SelectionMode.MultiSimple || value == SelectionMode.MultiExtended)
182 					throw new ArgumentException ("Multi selection not supported on CheckedListBox");
183 
184 				base.SelectionMode = value;
185 			}
186 		}
187 
188 		[DefaultValue (false)]
189 		public bool ThreeDCheckBoxes {
190 			get { return three_dcheckboxes; }
191 			set {
192 				if (three_dcheckboxes == value)
193 					return;
194 
195 				three_dcheckboxes = value;
196 				Refresh ();
197 			}
198 		}
199 
200 		[Browsable (false)]
201 		[EditorBrowsable (EditorBrowsableState.Never)]
202 		public new string ValueMember {
203 			get { return base.ValueMember; }
204 			set { base.ValueMember = value; }
205 		}
206 
207 		[Browsable (false)]
208 		[EditorBrowsable (EditorBrowsableState.Never)]
209 		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
210 		public new Padding Padding {
211 			get { return base.Padding; }
212 			set { base.Padding = value; }
213 		}
214 		#endregion Public Properties
215 
216 		#region Public Methods
217 
CreateAccessibilityInstance()218 		protected override AccessibleObject CreateAccessibilityInstance ()
219 		{
220 			return base.CreateAccessibilityInstance ();
221 		}
222 
CreateItemCollection()223 		protected override ListBox.ObjectCollection CreateItemCollection ()
224 		{
225 			return new ObjectCollection (this);
226 		}
227 
GetItemChecked(int index)228 		public bool GetItemChecked (int index)
229 		{
230 			return check_states.Contains (Items [index]);
231 		}
232 
GetItemCheckState(int index)233 		public CheckState GetItemCheckState (int index)
234 		{
235 			if (index < 0 || index >= Items.Count)
236 				throw new ArgumentOutOfRangeException ("Index of out range");
237 
238 			object o = Items [index];
239 			if (check_states.Contains (o))
240 				return (CheckState) check_states [o];
241 			else
242 				return CheckState.Unchecked;
243 		}
244 
OnBackColorChanged(EventArgs e)245 		protected override void OnBackColorChanged (EventArgs e)
246 		{
247 			base.OnBackColorChanged (e);
248 		}
249 
OnClick(EventArgs e)250 		protected override void OnClick (EventArgs e)
251 		{
252 			base.OnClick (e);
253 		}
254 
OnDrawItem(DrawItemEventArgs e)255 		protected override void OnDrawItem (DrawItemEventArgs e)
256 		{
257 			if (check_states.Contains (Items [e.Index])) {
258 				DrawItemState state = e.State | DrawItemState.Checked;
259 				if (((CheckState) check_states [Items [e.Index]]) == CheckState.Indeterminate)
260 					state |= DrawItemState.Inactive;
261 				e = new DrawItemEventArgs (e.Graphics, e.Font, e.Bounds, e.Index, state, e.ForeColor, e.BackColor);
262 			}
263 			ThemeEngine.Current.DrawCheckedListBoxItem (this, e);
264 		}
265 
OnFontChanged(EventArgs e)266 		protected override void OnFontChanged (EventArgs e)
267 		{
268 			base.OnFontChanged (e);
269 		}
270 
OnHandleCreated(EventArgs e)271 		protected override void OnHandleCreated (EventArgs e)
272 		{
273 			base.OnHandleCreated (e);
274 		}
275 
OnItemCheck(ItemCheckEventArgs ice)276 		protected virtual void OnItemCheck (ItemCheckEventArgs ice)
277 		{
278 			ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
279 			if (eh != null)
280 				eh (this, ice);
281 		}
282 
OnKeyPress(KeyPressEventArgs e)283 		protected override void OnKeyPress (KeyPressEventArgs e)
284 		{
285 			base.OnKeyPress (e);
286 
287 			if (e.KeyChar == ' ' && FocusedItem != -1)
288 				SetItemChecked (FocusedItem, !GetItemChecked (FocusedItem));
289 		}
290 
OnMeasureItem(MeasureItemEventArgs e)291 		protected override void OnMeasureItem (MeasureItemEventArgs e)
292 		{
293 			base.OnMeasureItem (e);
294 		}
295 
OnSelectedIndexChanged(EventArgs e)296 		protected override void OnSelectedIndexChanged (EventArgs e)
297 		{
298 			base.OnSelectedIndexChanged (e);
299 		}
300 
RefreshItems()301 		protected override void RefreshItems ()
302 		{
303 			base.RefreshItems ();
304 		}
305 
SetItemChecked(int index, bool value)306 		public void SetItemChecked (int index, bool value)
307 		{
308 			SetItemCheckState (index, value ? CheckState.Checked : CheckState.Unchecked);
309 		}
310 
SetItemCheckState(int index, CheckState value)311 		public void SetItemCheckState (int index, CheckState value)
312 		{
313 			if (index < 0 || index >= Items.Count)
314 				throw new ArgumentOutOfRangeException ("Index of out range");
315 
316 			if (!Enum.IsDefined (typeof (CheckState), value))
317 				throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for CheckState", value));
318 
319 			CheckState old_value = GetItemCheckState (index);
320 
321 			if (old_value == value)
322 				return;
323 
324 			ItemCheckEventArgs icea = new ItemCheckEventArgs (index, value, old_value);
325     			OnItemCheck (icea);
326 
327 			switch (icea.NewValue) {
328 			case CheckState.Checked:
329 			case CheckState.Indeterminate:
330 				check_states[Items[index]] = icea.NewValue;
331     				break;
332 			case CheckState.Unchecked:
333 				check_states.Remove (Items[index]);
334 				break;
335 			default:
336 				break;
337 			}
338 
339 			UpdateCollections ();
340 
341     			InvalidateCheckbox (index);
342 		}
343 
WmReflectCommand(ref Message m)344 		protected override void WmReflectCommand (ref Message m)
345 		{
346 			base.WmReflectCommand (ref m);
347 		}
348 
WndProc(ref Message m)349 		protected override void WndProc (ref Message m)
350 		{
351 			base.WndProc (ref m);
352 		}
353 
354 		#endregion Public Methods
355 
356 		#region Private Methods
357 
358 		int last_clicked_index = -1;
359 
OnItemClick(int index)360 		internal override void OnItemClick (int index)
361 		{
362 			if ((CheckOnClick || last_clicked_index == index) && index > -1) {
363 				if (GetItemChecked (index))
364 					SetItemCheckState (index, CheckState.Unchecked);
365 				else
366 					SetItemCheckState (index, CheckState.Checked);
367 			}
368 
369 			last_clicked_index = index;
370 			base.OnItemClick (index);
371 		}
372 
CollectionChanged()373 		internal override void CollectionChanged ()
374 		{
375 			base.CollectionChanged ();
376 			UpdateCollections ();
377 		}
378 
InvalidateCheckbox(int index)379 		private void InvalidateCheckbox (int index)
380 		{
381 			Rectangle area = GetItemDisplayRectangle (index, TopIndex);
382 			area.X += 2;
383 			area.Y += (area.Height - 11) / 2;
384 			area.Width = 11;
385 			area.Height = 11;
386 			Invalidate (area);
387 		}
388 
UpdateCollections()389 		private void UpdateCollections ()
390 		{
391 			CheckedItems.Refresh ();
392 			CheckedIndices.Refresh ();
393 		}
394 
395 		#endregion Private Methods
396 
397 		public new class ObjectCollection : ListBox.ObjectCollection
398 		{
399 			private CheckedListBox owner;
400 
ObjectCollection(CheckedListBox owner)401 			public ObjectCollection (CheckedListBox owner) : base (owner)
402 			{
403 				this.owner = owner;
404 			}
405 
Add(object item, bool isChecked)406 			public int Add (object item, bool isChecked)
407 			{
408 				return Add (item, isChecked ? CheckState.Checked : CheckState.Unchecked);
409 			}
410 
Add(object item, CheckState check)411 			public int Add (object item, CheckState check)
412 			{
413 				int idx = Add (item);
414 
415 				ItemCheckEventArgs icea = new ItemCheckEventArgs (idx, check, CheckState.Unchecked);
416 
417 				if (check == CheckState.Checked)
418 					owner.OnItemCheck (icea);
419 
420 				if (icea.NewValue != CheckState.Unchecked)
421 					owner.check_states[item] = icea.NewValue;
422 
423 				owner.UpdateCollections ();
424 				return idx;
425 			}
426 		}
427 
428 		public class CheckedIndexCollection : IList, ICollection, IEnumerable
429 		{
430 			private CheckedListBox owner;
431 			private ArrayList indices = new ArrayList ();
432 
CheckedIndexCollection(CheckedListBox owner)433 			internal CheckedIndexCollection (CheckedListBox owner)
434 			{
435 				this.owner = owner;
436 			}
437 
438 			#region Public Properties
439 			public int Count {
440 				get { return indices.Count; }
441 			}
442 
443 			public bool IsReadOnly {
444 				get { return true;}
445 			}
446 
447 			bool ICollection.IsSynchronized {
448 				get { return false; }
449 			}
450 
451 			bool IList.IsFixedSize{
452 				get { return true; }
453 			}
454 
455 			object ICollection.SyncRoot {
456 				get { return this; }
457 			}
458 
459 			[Browsable (false)]
460 			[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
461 			public int this[int index] {
462 				get {
463 					if (index < 0 || index >= Count)
464 						throw new ArgumentOutOfRangeException ("Index of out range");
465 
466 					return (int) indices[index];
467 				}
468 			}
469 			#endregion Public Properties
470 
Contains(int index)471 			public bool Contains (int index)
472 			{
473 				return indices.Contains (index);
474 			}
475 
476 
CopyTo(Array dest, int index)477 			public void CopyTo (Array dest, int index)
478 			{
479 				indices.CopyTo (dest, index);
480 			}
481 
GetEnumerator()482 			public IEnumerator GetEnumerator ()
483 			{
484 				return indices.GetEnumerator ();
485 			}
486 
IList.Add(object value)487 			int IList.Add (object value)
488 			{
489 				throw new NotSupportedException ();
490 			}
491 
IList.Clear()492 			void IList.Clear ()
493 			{
494 				throw new NotSupportedException ();
495 			}
496 
IList.Contains(object index)497 			bool IList.Contains (object index)
498 			{
499 				return Contains ((int)index);
500 			}
501 
IList.IndexOf(object index)502 			int IList.IndexOf (object index)
503 			{
504 				return IndexOf ((int) index);
505 			}
506 
IList.Insert(int index, object value)507 			void IList.Insert (int index, object value)
508 			{
509 				throw new NotSupportedException ();
510 			}
511 
IList.Remove(object value)512 			void IList.Remove (object value)
513 			{
514 				throw new NotSupportedException ();
515 			}
516 
IList.RemoveAt(int index)517 			void IList.RemoveAt (int index)
518 			{
519 				throw new NotSupportedException ();
520 			}
521 
522 			object IList.this[int index]{
523 				get {return indices[index]; }
524 				set {throw new NotImplementedException (); }
525 			}
526 
IndexOf(int index)527 			public int IndexOf (int index)
528 			{
529 				return indices.IndexOf (index);
530 			}
531 
532 			#region Private Methods
Refresh()533 			internal void Refresh ()
534 			{
535 				indices.Clear ();
536 				for (int i = 0; i < owner.Items.Count; i++)
537 					if (owner.check_states.Contains (owner.Items [i]))
538 						indices.Add (i);
539 			}
540 			#endregion Private Methods
541 
542 		}
543 
544 		public class CheckedItemCollection : IList, ICollection, IEnumerable
545 		{
546 			private CheckedListBox owner;
547 			private ArrayList list = new ArrayList ();
548 
CheckedItemCollection(CheckedListBox owner)549 			internal CheckedItemCollection (CheckedListBox owner)
550 			{
551 				this.owner = owner;
552 			}
553 
554 			#region Public Properties
555 			public int Count {
556 				get { return list.Count; }
557 			}
558 
559 			public bool IsReadOnly {
560 				get { return true; }
561 			}
562 
563 			[Browsable (false)]
564 			[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
565 			public object this [int index] {
566 				get {
567 					if (index < 0 || index >= Count)
568 						throw new ArgumentOutOfRangeException ("Index of out range");
569 
570 					return list[index];
571 				}
572 				set {throw new NotSupportedException ();}
573 			}
574 
575 			bool ICollection.IsSynchronized {
576 				get { return true; }
577 			}
578 
579 			object ICollection.SyncRoot {
580 				get { return this; }
581 			}
582 
583 			bool IList.IsFixedSize {
584 				get { return true; }
585 			}
586 
587 			#endregion Public Properties
588 
589 			#region Public Methods
Contains(object item)590 			public bool Contains (object item)
591 			{
592 				return list.Contains (item);
593 			}
594 
CopyTo(Array dest, int index)595 			public void CopyTo (Array dest, int index)
596 			{
597 				list.CopyTo (dest, index);
598 			}
599 
IList.Add(object value)600 			int IList.Add (object value)
601 			{
602 				throw new NotSupportedException ();
603 			}
604 
IList.Clear()605 			void IList.Clear ()
606 			{
607 				throw new NotSupportedException ();
608 			}
609 
IList.Insert(int index, object value)610 			void IList.Insert (int index, object value)
611 			{
612 				throw new NotSupportedException ();
613 			}
614 
IList.Remove(object value)615 			void IList.Remove (object value)
616 			{
617 				throw new NotSupportedException ();
618 			}
619 
IList.RemoveAt(int index)620 			void IList.RemoveAt (int index)
621 			{
622 				throw new NotSupportedException ();
623 			}
624 
IndexOf(object item)625 			public int IndexOf (object item)
626 			{
627 				return list.IndexOf (item);
628 			}
629 
GetEnumerator()630 			public IEnumerator GetEnumerator ()
631 			{
632 				return list.GetEnumerator ();
633 			}
634 
635 			#endregion Public Methods
636 
637 			#region Private Methods
Refresh()638 			internal void Refresh ()
639 			{
640 				list.Clear ();
641 				for (int i = 0; i < owner.Items.Count; i++)
642 					if (owner.check_states.Contains (owner.Items [i]))
643 						list.Add (owner.Items[i]);
644 			}
645 			#endregion Private Methods
646 		}
647 		[DefaultValue (false)]
648 		public bool UseCompatibleTextRendering {
649 			get { return use_compatible_text_rendering; }
650 			set { use_compatible_text_rendering = value; }
651 		}
652 	}
653 }
654 
655