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) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Author:
23 //	Pedro Martínez Juliá <pedromj@gmail.com>
24 //	Ivan N. Zlatev <contact@i-nz.net>
25 //
26 
27 
28 using System.Collections;
29 using System.ComponentModel;
30 using System.Drawing;
31 
32 namespace System.Windows.Forms {
33 
34 	public class DataGridViewComboBoxCell : DataGridViewCell {
35 
36 		private bool autoComplete;
37 		private object dataSource;
38 		private string displayMember;
39 		private DataGridViewComboBoxDisplayStyle displayStyle;
40 		private bool displayStyleForCurrentCellOnly;
41 		private int dropDownWidth;
42 		private FlatStyle flatStyle;
43 		private ObjectCollection items;
44 		private int maxDropDownItems;
45 		private bool sorted;
46 		private string valueMember;
47 		private DataGridViewComboBoxColumn owningColumnTemlate;
48 
DataGridViewComboBoxCell()49 		public DataGridViewComboBoxCell () : base() {
50 			autoComplete = true;
51 			dataSource = null;
52 			displayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;
53 			displayStyleForCurrentCellOnly = false;
54 			dropDownWidth = 1;
55 			flatStyle = FlatStyle.Standard;
56 			items = new ObjectCollection(this);
57 			maxDropDownItems = 8;
58 			sorted = false;
59 			owningColumnTemlate = null;
60 		}
61 
62 		[DefaultValue (true)]
63 		public virtual bool AutoComplete {
64 			get { return autoComplete; }
65 			set { autoComplete = value; }
66 		}
67 
68 		public virtual object DataSource {
69 			get { return dataSource; }
70 			set {
71 				if (value is IList || value is IListSource || value == null) {
72 					dataSource = value;
73 					return;
74 				}
75 				throw new Exception("Value is no IList, IListSource or null.");
76 			}
77 		}
78 
79 		[DefaultValue ("")]
80 		public virtual string DisplayMember {
81 			get { return displayMember; }
82 			set { displayMember = value; }
83 		}
84 
85 		[DefaultValue (DataGridViewComboBoxDisplayStyle.DropDownButton)]
86 		public DataGridViewComboBoxDisplayStyle DisplayStyle {
87 			get { return displayStyle; }
88 			set { displayStyle = value; }
89 		}
90 
91 		[DefaultValue (false)]
92 		public bool DisplayStyleForCurrentCellOnly {
93 			get { return displayStyleForCurrentCellOnly; }
94 			set { displayStyleForCurrentCellOnly = value; }
95 		}
96 
97 		[DefaultValue (1)]
98 		public virtual int DropDownWidth {
99 			get { return dropDownWidth; }
100 			set {
101 				if (value < 1) {
102 					throw new ArgumentOutOfRangeException("Value is less than 1.");
103 				}
104 				dropDownWidth = value;
105 			}
106 		}
107 
108 		public override Type EditType {
109 			get { return typeof(DataGridViewComboBoxEditingControl); }
110 		}
111 
112 		[DefaultValue (FlatStyle.Standard)]
113 		public FlatStyle FlatStyle {
114 			get { return flatStyle; }
115 			set {
116 				if (!Enum.IsDefined(typeof(FlatStyle), value)) {
117 					throw new InvalidEnumArgumentException("Value is not valid FlatStyle.");
118 				}
119 				flatStyle = value;
120 			}
121 		}
122 
123 		public override Type FormattedValueType {
124 			get { return typeof(string); }
125 		}
126 
127 		[Browsable (false)]
128 		public virtual ObjectCollection Items {
129 			get {
130 				if (DataGridView != null && DataGridView.BindingContext != null
131 				    && DataSource != null && !String.IsNullOrEmpty (ValueMember)) {
132 					items.ClearInternal ();
133 					CurrencyManager dataManager = (CurrencyManager) DataGridView.BindingContext[DataSource];
134 					if (dataManager != null && dataManager.Count > 0) {
135 						foreach (object item in dataManager.List)
136 							items.AddInternal (item);
137 					}
138 				}
139 
140 				return items;
141 			}
142 		}
143 
144 		[DefaultValue (8)]
145 		public virtual int MaxDropDownItems {
146 			get { return maxDropDownItems; }
147 			set {
148 				if (value < 1 || value > 100) {
149 					throw new ArgumentOutOfRangeException("Value is less than 1 or greater than 100.");
150 				}
151 				maxDropDownItems = value;
152 			}
153 		}
154 
155 		[DefaultValue (false)]
156 		public virtual bool Sorted {
157 			get { return sorted; }
158 			set {
159 				/*
160 				if () {
161 					throw new ArgumentException("Cannot sort a cell attached to a data source.");
162 				}
163 				*/
164 				sorted = value;
165 			}
166 		}
167 
168 		[DefaultValue ("")]
169 		public virtual string ValueMember {
170 			get { return valueMember; }
171 			set { valueMember = value; }
172 		}
173 
174 		public override Type ValueType {
175 			get { return typeof(string); }
176 		}
177 
178 		// Valid only for template Cells and used as a bridge to push items
179 		internal DataGridViewComboBoxColumn OwningColumnTemplate {
180 			get { return owningColumnTemlate; }
181 			set { owningColumnTemlate = value; }
182 		}
183 
Clone()184 		public override object Clone () {
185 			DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell) base.Clone();
186 			cell.autoComplete = this.autoComplete;
187 			cell.dataSource = this.dataSource;
188 			cell.displayStyle = this.displayStyle;
189 			cell.displayMember = this.displayMember;
190 			cell.valueMember = this.valueMember;
191 			cell.displayStyleForCurrentCellOnly = this.displayStyleForCurrentCellOnly;
192 			cell.dropDownWidth = this.dropDownWidth;
193 			cell.flatStyle = this.flatStyle;
194 			cell.items.AddRangeInternal(this.items);
195 			cell.maxDropDownItems = this.maxDropDownItems;
196 			cell.sorted = this.sorted;
197 			return cell;
198 		}
199 
DetachEditingControl()200 		public override void DetachEditingControl () {
201 			this.DataGridView.EditingControlInternal = null;
202 		}
203 
InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)204 		public override void InitializeEditingControl (int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) {
205 			base.InitializeEditingControl (rowIndex, initialFormattedValue, dataGridViewCellStyle);
206 
207 			ComboBox editingControl = DataGridView.EditingControl as ComboBox;
208 
209 			editingControl.DropDownStyle = ComboBoxStyle.DropDownList;
210 			editingControl.Sorted = Sorted;
211 			editingControl.DataSource = null;
212 			editingControl.ValueMember = null;
213 			editingControl.DisplayMember = null;
214 			editingControl.Items.Clear();
215 			editingControl.SelectedIndex = -1;
216 
217 			if (DataSource != null) {
218 				editingControl.DataSource = DataSource;
219 				editingControl.ValueMember = ValueMember;
220 				editingControl.DisplayMember = DisplayMember;
221 			} else {
222 				editingControl.Items.AddRange (this.Items);
223 				if (initialFormattedValue != null && editingControl.Items.IndexOf (initialFormattedValue) != -1)
224 					editingControl.SelectedItem = initialFormattedValue;
225 			}
226 		}
227 
SyncItems()228 		internal void SyncItems ()
229 		{
230 			if (DataSource != null || OwningColumnTemplate == null)
231 				return;
232 
233 			if (OwningColumnTemplate.DataGridView != null) {
234 				DataGridViewComboBoxEditingControl editor = OwningColumnTemplate.DataGridView.EditingControl
235 									    as DataGridViewComboBoxEditingControl;
236 				if (editor != null) {
237 					object selectedItem = editor.SelectedItem;
238 					editor.Items.Clear ();
239 					editor.Items.AddRange (items);
240 					if (editor.Items.IndexOf (selectedItem) != -1)
241 						editor.SelectedItem = selectedItem;
242 				}
243 			}
244 
245 			// Push the new items to the column
246 			OwningColumnTemplate.SyncItems (Items);
247 		}
248 
KeyEntersEditMode(KeyEventArgs e)249 		public override bool KeyEntersEditMode (KeyEventArgs e)
250 		{
251 			if (e.KeyCode == Keys.Space)
252 				return true;
253 			if ((int)e.KeyCode >= 48 && (int)e.KeyCode <= 90)
254 				return true;
255 			if ((int)e.KeyCode >= 96 && (int)e.KeyCode <= 111)
256 				return true;
257 			if (e.KeyCode == Keys.BrowserSearch || e.KeyCode == Keys.SelectMedia)
258 				return true;
259 			if ((int)e.KeyCode >= 186 && (int)e.KeyCode <= 229)
260 				return true;
261 			if (e.KeyCode == Keys.Attn || e.KeyCode == Keys.Packet)
262 				return true;
263 			if ((int)e.KeyCode >= 248 && (int)e.KeyCode <= 254)
264 				return true;
265 			if (e.KeyCode == Keys.F4)
266 				return true;
267 			if ((e.Modifiers == Keys.Alt) && (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up))
268 				return true;
269 
270 			return false;
271 		}
272 
ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)273 		public override object ParseFormattedValue (object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
274 		{
275 			return base.ParseFormattedValue (formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter);
276 		}
277 
ToString()278 		public override string ToString () {
279 			return string.Format ("DataGridViewComboBoxCell {{ ColumnIndex={0}, RowIndex={1} }}", ColumnIndex, RowIndex);
280 		}
281 
GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)282 		protected override Rectangle GetContentBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
283 		{
284 			if (DataGridView == null)
285 				return Rectangle.Empty;
286 
287 			object o = FormattedValue;
288 			Size s = Size.Empty;
289 
290 			if (o != null)
291 				s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
292 
293 			return new Rectangle (1, (OwningRow.Height - s.Height) / 2, s.Width - 3, s.Height);
294 		}
295 
GetErrorIconBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)296 		protected override Rectangle GetErrorIconBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
297 		{
298 			if (DataGridView == null || string.IsNullOrEmpty (ErrorText))
299 				return Rectangle.Empty;
300 
301 			Size error_icon = new Size (12, 11);
302 			return new Rectangle (new Point (Size.Width - error_icon.Width - 23, (Size.Height - error_icon.Height) / 2), error_icon);
303 		}
304 
GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)305 		protected override object GetFormattedValue (object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
306 		{
307 			return base.GetFormattedValue (value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
308 		}
309 
GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize)310 		protected override Size GetPreferredSize (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize)
311 		{
312 			object o = FormattedValue;
313 
314 			if (o != null) {
315 				Size s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
316 				s.Height = Math.Max (s.Height, 22);
317 				s.Width += 25;
318 				return s;
319 			} else
320 				return new Size (39, 22);
321 		}
322 
OnDataGridViewChanged()323 		protected override void OnDataGridViewChanged () {
324 			// Here we're supposed to do something with DataSource, etc, according to MSDN.
325 			base.OnDataGridViewChanged ();
326 		}
327 
OnEnter(int rowIndex, bool throughMouseClick)328 		protected override void OnEnter (int rowIndex, bool throughMouseClick) {
329 			base.OnEnter (rowIndex, throughMouseClick);
330 		}
331 
OnLeave(int rowIndex, bool throughMouseClick)332 		protected override void OnLeave (int rowIndex, bool throughMouseClick) {
333 			base.OnLeave (rowIndex, throughMouseClick);
334 		}
335 
OnMouseDown(DataGridViewCellMouseEventArgs e)336 		protected override void OnMouseDown (DataGridViewCellMouseEventArgs e) {
337 			base.OnMouseDown (e);
338 
339 			if (!ReadOnly)
340 			{
341 				// Any mouse-click on the cell should be passed along to any
342 				// combo-box control.
343 				if (IsInEditMode)
344 				{
345 					DataGridViewComboBoxEditingControl cb
346 						= (DataGridView.EditingControl
347 							as DataGridViewComboBoxEditingControl);
348 					if (cb != null)
349 						cb.OnMouseDownInternal (e);
350 				}
351 			}
352 		}
353 
OnMouseClick(DataGridViewCellMouseEventArgs e)354 		protected override void OnMouseClick (DataGridViewCellMouseEventArgs e) {
355 			base.OnMouseClick (e);
356 		}
357 
OnMouseEnter(int rowIndex)358 		protected override void OnMouseEnter (int rowIndex) {
359 			base.OnMouseEnter (rowIndex);
360 		}
361 
OnMouseLeave(int rowIndex)362 		protected override void OnMouseLeave (int rowIndex) {
363 			base.OnMouseLeave (rowIndex);
364 		}
365 
OnMouseMove(DataGridViewCellMouseEventArgs e)366 		protected override void OnMouseMove (DataGridViewCellMouseEventArgs e) {
367 			//Console.WriteLine ("MouseMove (Location: {0}", e.Location);
368 			base.OnMouseMove (e);
369 		}
370 
Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)371 		protected override void Paint (Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
372 		{
373 			// The internal paint routines are overridden instead of
374 			// doing the custom paint logic here
375 			base.Paint (graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
376 		}
377 
PaintPartContent(Graphics graphics, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, DataGridViewCellStyle cellStyle, object formattedValue)378 		internal override void PaintPartContent (Graphics graphics, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, DataGridViewCellStyle cellStyle, object formattedValue)
379 		{
380 			Color color = Selected ? cellStyle.SelectionForeColor : cellStyle.ForeColor;
381 			TextFormatFlags flags = TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.TextBoxControl;
382 
383 			Rectangle text_area = ContentBounds;
384 			text_area.X += cellBounds.X;
385 			text_area.Y += cellBounds.Y;
386 
387 			Rectangle button_area = CalculateButtonArea (cellBounds);
388 
389 			// The background of the dropdown button should be gray, not
390 			// the background color of the cell.
391 			graphics.FillRectangle (SystemBrushes.Control, button_area);
392 			ThemeEngine.Current.CPDrawComboButton (graphics, button_area, ButtonState.Normal);
393 
394 			if (formattedValue != null)
395 				TextRenderer.DrawText (graphics, formattedValue.ToString (), cellStyle.Font, text_area, color, flags);
396 		}
397 
CalculateButtonArea(Rectangle cellBounds)398 		private Rectangle CalculateButtonArea (Rectangle cellBounds)
399 		{
400 			Rectangle button_area, text_area;
401 			int border = ThemeEngine.Current.Border3DSize.Width;
402 			const int button_width = 16;
403 
404 			text_area = cellBounds;
405 
406 			button_area = cellBounds;
407 			button_area.X = text_area.Right - button_width - border;
408 			button_area.Y = text_area.Y + border;
409 			button_area.Width = button_width;
410 			button_area.Height = text_area.Height - 2 * border;
411 
412 			return button_area;
413 		}
414 
415 		// IMPORTANT: Only call the internal methods from within DataGridViewComboBoxCell
416 		// for adding/removing/clearing because the other methods invoke an update of the
417 		// column items collection and you might end up in an endless loop.
418 		//
419 		[ListBindable (false)]
420 		public class ObjectCollection : IList, ICollection, IEnumerable {
421 
422 			private ArrayList list;
423 			private DataGridViewComboBoxCell owner;
424 
ObjectCollection(DataGridViewComboBoxCell owner)425 			public ObjectCollection (DataGridViewComboBoxCell owner)
426 			{
427 				this.owner = owner;
428 				list = new ArrayList();
429 			}
430 
431 			public int Count {
432 				get { return list.Count; }
433 			}
434 
435 			bool IList.IsFixedSize {
436 				get { return list.IsFixedSize; }
437 			}
438 
439 			public bool IsReadOnly {
440 				get { return list.IsReadOnly; }
441 			}
442 
443 			bool ICollection.IsSynchronized {
444 				get { return list.IsSynchronized; }
445 			}
446 
447 			object ICollection.SyncRoot {
448 				get { return list.SyncRoot; }
449 			}
450 
451 			public virtual object this [int index] {
452 				get { return list[index]; }
453 				set {
454 					ThrowIfOwnerIsDataBound ();
455 					list[index] = value;
456 				}
457 			}
458 
Add(object item)459 			public int Add (object item)
460 			{
461 				ThrowIfOwnerIsDataBound ();
462 				int index = AddInternal (item);
463 				SyncOwnerItems ();
464 				return index;
465 			}
466 
AddInternal(object item)467 			internal int AddInternal (object item)
468 			{
469 				return list.Add (item);
470 			}
471 
AddRangeInternal(ICollection items)472 			internal void AddRangeInternal (ICollection items)
473 			{
474 				list.AddRange (items);
475 			}
476 
AddRange(ObjectCollection value)477 			public void AddRange (ObjectCollection value)
478 			{
479 				ThrowIfOwnerIsDataBound ();
480 				AddRangeInternal (value);
481 				SyncOwnerItems ();
482 			}
483 
SyncOwnerItems()484 			private void SyncOwnerItems ()
485 			{
486 				ThrowIfOwnerIsDataBound ();
487 				if (owner != null)
488 					owner.SyncItems ();
489 			}
490 
ThrowIfOwnerIsDataBound()491 			public void ThrowIfOwnerIsDataBound ()
492 			{
493 				if (owner != null && owner.DataGridView != null && owner.DataSource != null)
494 					throw new ArgumentException ("Cannot modify collection if the cell is data bound.");
495 			}
496 
AddRange(params object[] items)497 			public void AddRange (params object[] items)
498 			{
499 				ThrowIfOwnerIsDataBound ();
500 				AddRangeInternal (items);
501 				SyncOwnerItems ();
502 			}
503 
Clear()504 			public void Clear ()
505 			{
506 				ThrowIfOwnerIsDataBound ();
507 				ClearInternal ();
508 				SyncOwnerItems ();
509 			}
510 
ClearInternal()511 			internal void ClearInternal ()
512 			{
513 				list.Clear ();
514 			}
515 
Contains(object value)516 			public bool Contains (object value)
517 			{
518 				return list.Contains(value);
519 			}
520 
ICollection.CopyTo(Array destination, int index)521 			void ICollection.CopyTo (Array destination, int index)
522 			{
523 				CopyTo ((object[]) destination, index);
524 			}
525 
CopyTo(object[] destination, int arrayIndex)526 			public void CopyTo (object[] destination, int arrayIndex)
527 			{
528 				list.CopyTo (destination, arrayIndex);
529 			}
530 
GetEnumerator()531 			public IEnumerator GetEnumerator ()
532 			{
533 				return list.GetEnumerator();
534 			}
535 
IndexOf(object value)536 			public int IndexOf (object value)
537 			{
538 				return list.IndexOf(value);
539 			}
540 
Insert(int index, object item)541 			public void Insert (int index, object item)
542 			{
543 				ThrowIfOwnerIsDataBound ();
544 				InsertInternal (index, item);
545 				SyncOwnerItems ();
546 			}
547 
InsertInternal(int index, object item)548 			internal void InsertInternal (int index, object item)
549 			{
550 				list.Insert (index, item);
551 			}
552 
Remove(object value)553 			public void Remove (object value)
554 			{
555 				ThrowIfOwnerIsDataBound ();
556 				RemoveInternal (value);
557 				SyncOwnerItems ();
558 			}
559 
RemoveInternal(object value)560 			internal void RemoveInternal (object value)
561 			{
562 				list.Remove (value);
563 			}
564 
RemoveAt(int index)565 			public void RemoveAt (int index)
566 			{
567 				ThrowIfOwnerIsDataBound ();
568 				RemoveAtInternal (index);
569 				SyncOwnerItems ();
570 			}
571 
RemoveAtInternal(int index)572 			internal void RemoveAtInternal (int index)
573 			{
574 				list.RemoveAt (index);
575 			}
576 
IList.Add(object item)577 			int IList.Add (object item)
578 			{
579 				return Add (item);
580 			}
581 
582 		}
583 
584 	}
585 
586 }
587 
588