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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
23 // Pedro MartÃnez Juliá <pedromj@gmail.com>
24 // Ivan N. Zlatev <contact@i-nz.net>
28 using System.Collections;
29 using System.ComponentModel;
32 namespace System.Windows.Forms {
34 public class DataGridViewComboBoxCell : DataGridViewCell {
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;
46 private string valueMember;
47 private DataGridViewComboBoxColumn owningColumnTemlate;
49 public DataGridViewComboBoxCell () : base() {
52 displayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;
53 displayStyleForCurrentCellOnly = false;
55 flatStyle = FlatStyle.Standard;
56 items = new ObjectCollection(this);
59 owningColumnTemlate = null;
63 public virtual bool AutoComplete {
64 get { return autoComplete; }
65 set { autoComplete = value; }
68 public virtual object DataSource {
69 get { return dataSource; }
71 if (value is IList || value is IListSource || value == null) {
75 throw new Exception("Value is no IList, IListSource or null.");
80 public virtual string DisplayMember {
81 get { return displayMember; }
82 set { displayMember = value; }
85 [DefaultValue (DataGridViewComboBoxDisplayStyle.DropDownButton)]
86 public DataGridViewComboBoxDisplayStyle DisplayStyle {
87 get { return displayStyle; }
88 set { displayStyle = value; }
91 [DefaultValue (false)]
92 public bool DisplayStyleForCurrentCellOnly {
93 get { return displayStyleForCurrentCellOnly; }
94 set { displayStyleForCurrentCellOnly = value; }
98 public virtual int DropDownWidth {
99 get { return dropDownWidth; }
102 throw new ArgumentOutOfRangeException("Value is less than 1.");
104 dropDownWidth = value;
108 public override Type EditType {
109 get { return typeof(DataGridViewComboBoxEditingControl); }
112 [DefaultValue (FlatStyle.Standard)]
113 public FlatStyle FlatStyle {
114 get { return flatStyle; }
116 if (!Enum.IsDefined(typeof(FlatStyle), value)) {
117 throw new InvalidEnumArgumentException("Value is not valid FlatStyle.");
123 public override Type FormattedValueType {
124 get { return typeof(string); }
128 public virtual ObjectCollection Items {
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);
145 public virtual int MaxDropDownItems {
146 get { return maxDropDownItems; }
148 if (value < 1 || value > 100) {
149 throw new ArgumentOutOfRangeException("Value is less than 1 or greater than 100.");
151 maxDropDownItems = value;
155 [DefaultValue (false)]
156 public virtual bool Sorted {
157 get { return sorted; }
161 throw new ArgumentException("Cannot sort a cell attached to a data source.");
169 public virtual string ValueMember {
170 get { return valueMember; }
171 set { valueMember = value; }
174 public override Type ValueType {
175 get { return typeof(string); }
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; }
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;
200 public override void DetachEditingControl () {
201 this.DataGridView.EditingControlInternal = null;
204 public override void InitializeEditingControl (int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) {
205 base.InitializeEditingControl (rowIndex, initialFormattedValue, dataGridViewCellStyle);
207 ComboBox editingControl = DataGridView.EditingControl as ComboBox;
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;
217 if (DataSource != null) {
218 editingControl.DataSource = DataSource;
219 editingControl.ValueMember = ValueMember;
220 editingControl.DisplayMember = DisplayMember;
222 editingControl.Items.AddRange (this.Items);
223 if (initialFormattedValue != null && editingControl.Items.IndexOf (initialFormattedValue) != -1)
224 editingControl.SelectedItem = initialFormattedValue;
228 internal void SyncItems ()
230 if (DataSource != null || OwningColumnTemplate == null)
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;
245 // Push the new items to the column
246 OwningColumnTemplate.SyncItems (Items);
249 public override bool KeyEntersEditMode (KeyEventArgs e)
251 if (e.KeyCode == Keys.Space)
253 if ((int)e.KeyCode >= 48 && (int)e.KeyCode <= 90)
255 if ((int)e.KeyCode >= 96 && (int)e.KeyCode <= 111)
257 if (e.KeyCode == Keys.BrowserSearch || e.KeyCode == Keys.SelectMedia)
259 if ((int)e.KeyCode >= 186 && (int)e.KeyCode <= 229)
261 if (e.KeyCode == Keys.Attn || e.KeyCode == Keys.Packet)
263 if ((int)e.KeyCode >= 248 && (int)e.KeyCode <= 254)
265 if (e.KeyCode == Keys.F4)
267 if ((e.Modifiers == Keys.Alt) && (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up))
273 public override object ParseFormattedValue (object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
275 return base.ParseFormattedValue (formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter);
278 public override string ToString () {
279 return string.Format ("DataGridViewComboBoxCell {{ ColumnIndex={0}, RowIndex={1} }}", ColumnIndex, RowIndex);
282 protected override Rectangle GetContentBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
284 if (DataGridView == null)
285 return Rectangle.Empty;
287 object o = FormattedValue;
291 s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
293 return new Rectangle (1, (OwningRow.Height - s.Height) / 2, s.Width - 3, s.Height);
296 protected override Rectangle GetErrorIconBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
298 if (DataGridView == null || string.IsNullOrEmpty (ErrorText))
299 return Rectangle.Empty;
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);
305 protected override object GetFormattedValue (object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
307 return base.GetFormattedValue (value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
310 protected override Size GetPreferredSize (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize)
312 object o = FormattedValue;
315 Size s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
316 s.Height = Math.Max (s.Height, 22);
320 return new Size (39, 22);
323 protected override void OnDataGridViewChanged () {
324 // Here we're supposed to do something with DataSource, etc, according to MSDN.
325 base.OnDataGridViewChanged ();
328 protected override void OnEnter (int rowIndex, bool throughMouseClick) {
329 base.OnEnter (rowIndex, throughMouseClick);
332 protected override void OnLeave (int rowIndex, bool throughMouseClick) {
333 base.OnLeave (rowIndex, throughMouseClick);
336 protected override void OnMouseDown (DataGridViewCellMouseEventArgs e) {
337 base.OnMouseDown (e);
341 // Any mouse-click on the cell should be passed along to any
342 // combo-box control.
345 DataGridViewComboBoxEditingControl cb
346 = (DataGridView.EditingControl
347 as DataGridViewComboBoxEditingControl);
349 cb.OnMouseDownInternal (e);
354 protected override void OnMouseClick (DataGridViewCellMouseEventArgs e) {
355 base.OnMouseClick (e);
358 protected override void OnMouseEnter (int rowIndex) {
359 base.OnMouseEnter (rowIndex);
362 protected override void OnMouseLeave (int rowIndex) {
363 base.OnMouseLeave (rowIndex);
366 protected override void OnMouseMove (DataGridViewCellMouseEventArgs e) {
367 //Console.WriteLine ("MouseMove (Location: {0}", e.Location);
368 base.OnMouseMove (e);
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)
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);
378 internal override void PaintPartContent (Graphics graphics, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, DataGridViewCellStyle cellStyle, object formattedValue)
380 Color color = Selected ? cellStyle.SelectionForeColor : cellStyle.ForeColor;
381 TextFormatFlags flags = TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.TextBoxControl;
383 Rectangle text_area = ContentBounds;
384 text_area.X += cellBounds.X;
385 text_area.Y += cellBounds.Y;
387 Rectangle button_area = CalculateButtonArea (cellBounds);
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);
394 if (formattedValue != null)
395 TextRenderer.DrawText (graphics, formattedValue.ToString (), cellStyle.Font, text_area, color, flags);
398 private Rectangle CalculateButtonArea (Rectangle cellBounds)
400 Rectangle button_area, text_area;
401 int border = ThemeEngine.Current.Border3DSize.Width;
402 const int button_width = 16;
404 text_area = cellBounds;
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;
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.
419 [ListBindable (false)]
420 public class ObjectCollection : IList, ICollection, IEnumerable {
422 private ArrayList list;
423 private DataGridViewComboBoxCell owner;
425 public ObjectCollection (DataGridViewComboBoxCell owner)
428 list = new ArrayList();
432 get { return list.Count; }
435 bool IList.IsFixedSize {
436 get { return list.IsFixedSize; }
439 public bool IsReadOnly {
440 get { return list.IsReadOnly; }
443 bool ICollection.IsSynchronized {
444 get { return list.IsSynchronized; }
447 object ICollection.SyncRoot {
448 get { return list.SyncRoot; }
451 public virtual object this [int index] {
452 get { return list[index]; }
454 ThrowIfOwnerIsDataBound ();
459 public int Add (object item)
461 ThrowIfOwnerIsDataBound ();
462 int index = AddInternal (item);
467 internal int AddInternal (object item)
469 return list.Add (item);
472 internal void AddRangeInternal (ICollection items)
474 list.AddRange (items);
477 public void AddRange (ObjectCollection value)
479 ThrowIfOwnerIsDataBound ();
480 AddRangeInternal (value);
484 private void SyncOwnerItems ()
486 ThrowIfOwnerIsDataBound ();
491 public void ThrowIfOwnerIsDataBound ()
493 if (owner != null && owner.DataGridView != null && owner.DataSource != null)
494 throw new ArgumentException ("Cannot modify collection if the cell is data bound.");
497 public void AddRange (params object[] items)
499 ThrowIfOwnerIsDataBound ();
500 AddRangeInternal (items);
506 ThrowIfOwnerIsDataBound ();
511 internal void ClearInternal ()
516 public bool Contains (object value)
518 return list.Contains(value);
521 void ICollection.CopyTo (Array destination, int index)
523 CopyTo ((object[]) destination, index);
526 public void CopyTo (object[] destination, int arrayIndex)
528 list.CopyTo (destination, arrayIndex);
531 public IEnumerator GetEnumerator ()
533 return list.GetEnumerator();
536 public int IndexOf (object value)
538 return list.IndexOf(value);
541 public void Insert (int index, object item)
543 ThrowIfOwnerIsDataBound ();
544 InsertInternal (index, item);
548 internal void InsertInternal (int index, object item)
550 list.Insert (index, item);
553 public void Remove (object value)
555 ThrowIfOwnerIsDataBound ();
556 RemoveInternal (value);
560 internal void RemoveInternal (object value)
565 public void RemoveAt (int index)
567 ThrowIfOwnerIsDataBound ();
568 RemoveAtInternal (index);
572 internal void RemoveAtInternal (int index)
574 list.RemoveAt (index);
577 int IList.Add (object item)