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>
30 using System.Collections;
31 using System.ComponentModel;
34 namespace System.Windows.Forms {
36 public class DataGridViewComboBoxCell : DataGridViewCell {
38 private bool autoComplete;
39 private object dataSource;
40 private string displayMember;
41 private DataGridViewComboBoxDisplayStyle displayStyle;
42 private bool displayStyleForCurrentCellOnly;
43 private int dropDownWidth;
44 private FlatStyle flatStyle;
45 private ObjectCollection items;
46 private int maxDropDownItems;
48 private string valueMember;
49 private DataGridViewComboBoxColumn owningColumnTemlate;
51 public DataGridViewComboBoxCell () : base() {
54 displayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;
55 displayStyleForCurrentCellOnly = false;
57 flatStyle = FlatStyle.Standard;
58 items = new ObjectCollection(this);
61 owningColumnTemlate = null;
65 public virtual bool AutoComplete {
66 get { return autoComplete; }
67 set { autoComplete = value; }
70 public virtual object DataSource {
71 get { return dataSource; }
73 if (value is IList || value is IListSource || value == null) {
77 throw new Exception("Value is no IList, IListSource or null.");
82 public virtual string DisplayMember {
83 get { return displayMember; }
84 set { displayMember = value; }
87 [DefaultValue (DataGridViewComboBoxDisplayStyle.DropDownButton)]
88 public DataGridViewComboBoxDisplayStyle DisplayStyle {
89 get { return displayStyle; }
90 set { displayStyle = value; }
93 [DefaultValue (false)]
94 public bool DisplayStyleForCurrentCellOnly {
95 get { return displayStyleForCurrentCellOnly; }
96 set { displayStyleForCurrentCellOnly = value; }
100 public virtual int DropDownWidth {
101 get { return dropDownWidth; }
104 throw new ArgumentOutOfRangeException("Value is less than 1.");
106 dropDownWidth = value;
110 public override Type EditType {
111 get { return typeof(DataGridViewComboBoxEditingControl); }
114 [DefaultValue (FlatStyle.Standard)]
115 public FlatStyle FlatStyle {
116 get { return flatStyle; }
118 if (!Enum.IsDefined(typeof(FlatStyle), value)) {
119 throw new InvalidEnumArgumentException("Value is not valid FlatStyle.");
125 public override Type FormattedValueType {
126 get { return typeof(string); }
130 public virtual ObjectCollection Items {
132 if (DataGridView != null && DataGridView.BindingContext != null
133 && DataSource != null && !String.IsNullOrEmpty (ValueMember)) {
134 items.ClearInternal ();
135 CurrencyManager dataManager = (CurrencyManager) DataGridView.BindingContext[DataSource];
136 if (dataManager != null && dataManager.Count > 0) {
137 foreach (object item in dataManager.List)
138 items.AddInternal (item);
147 public virtual int MaxDropDownItems {
148 get { return maxDropDownItems; }
150 if (value < 1 || value > 100) {
151 throw new ArgumentOutOfRangeException("Value is less than 1 or greater than 100.");
153 maxDropDownItems = value;
157 [DefaultValue (false)]
158 public virtual bool Sorted {
159 get { return sorted; }
163 throw new ArgumentException("Cannot sort a cell attached to a data source.");
171 public virtual string ValueMember {
172 get { return valueMember; }
173 set { valueMember = value; }
176 public override Type ValueType {
177 get { return typeof(string); }
180 // Valid only for template Cells and used as a bridge to push items
181 internal DataGridViewComboBoxColumn OwningColumnTemplate {
182 get { return owningColumnTemlate; }
183 set { owningColumnTemlate = value; }
186 public override object Clone () {
187 DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell) base.Clone();
188 cell.autoComplete = this.autoComplete;
189 cell.dataSource = this.dataSource;
190 cell.displayStyle = this.displayStyle;
191 cell.displayMember = this.displayMember;
192 cell.valueMember = this.valueMember;
193 cell.displayStyleForCurrentCellOnly = this.displayStyleForCurrentCellOnly;
194 cell.dropDownWidth = this.dropDownWidth;
195 cell.flatStyle = this.flatStyle;
196 cell.items.AddRangeInternal(this.items);
197 cell.maxDropDownItems = this.maxDropDownItems;
198 cell.sorted = this.sorted;
202 public override void DetachEditingControl () {
203 this.DataGridView.EditingControlInternal = null;
206 public override void InitializeEditingControl (int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) {
207 base.InitializeEditingControl (rowIndex, initialFormattedValue, dataGridViewCellStyle);
209 ComboBox editingControl = DataGridView.EditingControl as ComboBox;
211 editingControl.DropDownStyle = ComboBoxStyle.DropDownList;
212 editingControl.Sorted = Sorted;
213 editingControl.DataSource = null;
214 editingControl.ValueMember = null;
215 editingControl.DisplayMember = null;
216 editingControl.Items.Clear();
217 editingControl.SelectedIndex = -1;
219 if (DataSource != null) {
220 editingControl.DataSource = DataSource;
221 editingControl.ValueMember = ValueMember;
222 editingControl.DisplayMember = DisplayMember;
224 editingControl.Items.AddRange (this.Items);
225 if (FormattedValue != null && editingControl.Items.IndexOf (FormattedValue) != -1)
226 editingControl.SelectedItem = FormattedValue;
230 internal void SyncItems ()
232 if (DataSource != null || OwningColumnTemplate == null)
235 if (OwningColumnTemplate.DataGridView != null) {
236 DataGridViewComboBoxEditingControl editor = OwningColumnTemplate.DataGridView.EditingControl
237 as DataGridViewComboBoxEditingControl;
238 if (editor != null) {
239 object selectedItem = editor.SelectedItem;
240 editor.Items.Clear ();
241 editor.Items.AddRange (items);
242 if (editor.Items.IndexOf (selectedItem) != -1)
243 editor.SelectedItem = selectedItem;
247 // Push the new items to the column
248 OwningColumnTemplate.SyncItems (Items);
251 public override bool KeyEntersEditMode (KeyEventArgs e)
253 if (e.KeyCode == Keys.Space)
255 if ((int)e.KeyCode >= 48 && (int)e.KeyCode <= 90)
257 if ((int)e.KeyCode >= 96 && (int)e.KeyCode <= 111)
259 if (e.KeyCode == Keys.BrowserSearch || e.KeyCode == Keys.SelectMedia)
261 if ((int)e.KeyCode >= 186 && (int)e.KeyCode <= 229)
263 if (e.KeyCode == Keys.Attn || e.KeyCode == Keys.Packet)
265 if ((int)e.KeyCode >= 248 && (int)e.KeyCode <= 254)
267 if (e.KeyCode == Keys.F4)
269 if ((e.Modifiers == Keys.Alt) && (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up))
275 public override object ParseFormattedValue (object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
277 return base.ParseFormattedValue (formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter);
280 public override string ToString () {
281 return string.Format ("DataGridViewComboBoxCell {{ ColumnIndex={0}, RowIndex={1} }}", ColumnIndex, RowIndex);
284 protected override Rectangle GetContentBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
286 if (DataGridView == null)
287 return Rectangle.Empty;
289 object o = FormattedValue;
293 s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
295 return new Rectangle (1, (OwningRow.Height - s.Height) / 2, s.Width - 3, s.Height);
298 protected override Rectangle GetErrorIconBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
300 if (DataGridView == null || string.IsNullOrEmpty (ErrorText))
301 return Rectangle.Empty;
303 Size error_icon = new Size (12, 11);
304 return new Rectangle (new Point (Size.Width - error_icon.Width - 23, (Size.Height - error_icon.Height) / 2), error_icon);
307 protected override object GetFormattedValue (object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
309 return base.GetFormattedValue (value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
312 protected override Size GetPreferredSize (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize)
314 object o = FormattedValue;
317 Size s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
318 s.Height = Math.Max (s.Height, 22);
322 return new Size (39, 22);
325 protected override void OnDataGridViewChanged () {
326 // Here we're supposed to do something with DataSource, etc, according to MSDN.
327 base.OnDataGridViewChanged ();
330 protected override void OnEnter (int rowIndex, bool throughMouseClick) {
331 base.OnEnter (rowIndex, throughMouseClick);
334 protected override void OnLeave (int rowIndex, bool throughMouseClick) {
335 base.OnLeave (rowIndex, throughMouseClick);
338 protected override void OnMouseClick (DataGridViewCellMouseEventArgs e) {
339 base.OnMouseClick (e);
342 protected override void OnMouseEnter (int rowIndex) {
343 base.OnMouseEnter (rowIndex);
346 protected override void OnMouseLeave (int rowIndex) {
347 base.OnMouseLeave (rowIndex);
350 protected override void OnMouseMove (DataGridViewCellMouseEventArgs e) {
351 //Console.WriteLine ("MouseMove (Location: {0}", e.Location);
352 base.OnMouseMove (e);
355 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)
357 // The internal paint routines are overridden instead of
358 // doing the custom paint logic here
359 base.Paint (graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
362 internal override void PaintPartContent (Graphics graphics, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, DataGridViewCellStyle cellStyle, object formattedValue)
364 Color color = Selected ? cellStyle.SelectionForeColor : cellStyle.ForeColor;
365 TextFormatFlags flags = TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.TextBoxControl;
367 Rectangle text_area = ContentBounds;
368 text_area.X += cellBounds.X;
369 text_area.Y += cellBounds.Y;
371 Rectangle button_area = CalculateButtonArea (cellBounds);
373 // The background of the dropdown button should be gray, not
374 // the background color of the cell.
375 graphics.FillRectangle (SystemBrushes.Control, button_area);
376 ThemeEngine.Current.CPDrawComboButton (graphics, button_area, ButtonState.Normal);
378 if (formattedValue != null)
379 TextRenderer.DrawText (graphics, formattedValue.ToString (), cellStyle.Font, text_area, color, flags);
382 private Rectangle CalculateButtonArea (Rectangle cellBounds)
384 Rectangle button_area, text_area;
385 int border = ThemeEngine.Current.Border3DSize.Width;
386 const int button_width = 16;
388 text_area = cellBounds;
390 button_area = cellBounds;
391 button_area.X = text_area.Right - button_width - border;
392 button_area.Y = text_area.Y + border;
393 button_area.Width = button_width;
394 button_area.Height = text_area.Height - 2 * border;
399 // IMPORTANT: Only call the internal methods from within DataGridViewComboBoxCell
400 // for adding/removing/clearing because the other methods invoke an update of the
401 // column items collection and you might end up in an endless loop.
403 [ListBindable (false)]
404 public class ObjectCollection : IList, ICollection, IEnumerable {
406 private ArrayList list;
407 private DataGridViewComboBoxCell owner;
409 public ObjectCollection (DataGridViewComboBoxCell owner)
412 list = new ArrayList();
416 get { return list.Count; }
419 bool IList.IsFixedSize {
420 get { return list.IsFixedSize; }
423 public bool IsReadOnly {
424 get { return list.IsReadOnly; }
427 bool ICollection.IsSynchronized {
428 get { return list.IsSynchronized; }
431 object ICollection.SyncRoot {
432 get { return list.SyncRoot; }
435 public virtual object this [int index] {
436 get { return list[index]; }
438 ThrowIfOwnerIsDataBound ();
443 public int Add (object item)
445 ThrowIfOwnerIsDataBound ();
446 int index = AddInternal (item);
451 internal int AddInternal (object item)
453 return list.Add (item);
456 internal void AddRangeInternal (ICollection items)
458 list.AddRange (items);
461 public void AddRange (ObjectCollection value)
463 ThrowIfOwnerIsDataBound ();
464 AddRangeInternal (value);
468 private void SyncOwnerItems ()
470 ThrowIfOwnerIsDataBound ();
475 public void ThrowIfOwnerIsDataBound ()
477 if (owner != null && owner.DataGridView != null && owner.DataSource != null)
478 throw new ArgumentException ("Cannot modify collection if the cell is data bound.");
481 public void AddRange (params object[] items)
483 ThrowIfOwnerIsDataBound ();
484 AddRangeInternal (items);
490 ThrowIfOwnerIsDataBound ();
495 internal void ClearInternal ()
500 public bool Contains (object value)
502 return list.Contains(value);
505 void ICollection.CopyTo (Array destination, int index)
507 CopyTo ((object[]) destination, index);
510 public void CopyTo (object[] destination, int arrayIndex)
512 list.CopyTo (destination, arrayIndex);
515 public IEnumerator GetEnumerator ()
517 return list.GetEnumerator();
520 public int IndexOf (object value)
522 return list.IndexOf(value);
525 public void Insert (int index, object item)
527 ThrowIfOwnerIsDataBound ();
528 InsertInternal (index, item);
532 internal void InsertInternal (int index, object item)
534 list.Insert (index, item);
537 public void Remove (object value)
539 ThrowIfOwnerIsDataBound ();
540 RemoveInternal (value);
544 internal void RemoveInternal (object value)
549 public void RemoveAt (int index)
551 ThrowIfOwnerIsDataBound ();
552 RemoveAtInternal (index);
556 internal void RemoveAtInternal (int index)
558 list.RemoveAt (index);
561 int IList.Add (object item)