Fix bug #395
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DataGridViewComboBoxCell.cs
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
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
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
200                 public override void DetachEditingControl () {
201                         this.DataGridView.EditingControlInternal = null;
202                 }
203
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 (FormattedValue != null && editingControl.Items.IndexOf (FormattedValue) != -1)
224                                         editingControl.SelectedItem = FormattedValue;
225                         }
226                 }
227
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
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
273                 public override object ParseFormattedValue (object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
274                 {
275                         return base.ParseFormattedValue (formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter);
276                 }
277
278                 public override string ToString () {
279                         return string.Format ("DataGridViewComboBoxCell {{ ColumnIndex={0}, RowIndex={1} }}", ColumnIndex, RowIndex);
280                 }
281
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
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
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
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
323                 protected override void OnDataGridViewChanged () {
324                         // Here we're supposed to do something with DataSource, etc, according to MSDN.
325                         base.OnDataGridViewChanged ();
326                 }
327
328                 protected override void OnEnter (int rowIndex, bool throughMouseClick) {
329                         base.OnEnter (rowIndex, throughMouseClick);
330                 }
331
332                 protected override void OnLeave (int rowIndex, bool throughMouseClick) {
333                         base.OnLeave (rowIndex, throughMouseClick);
334                 }
335
336                 protected override void OnMouseClick (DataGridViewCellMouseEventArgs e) {
337                         base.OnMouseClick (e);
338                 }
339
340                 protected override void OnMouseEnter (int rowIndex) {
341                         base.OnMouseEnter (rowIndex);
342                 }
343
344                 protected override void OnMouseLeave (int rowIndex) {
345                         base.OnMouseLeave (rowIndex);
346                 }
347
348                 protected override void OnMouseMove (DataGridViewCellMouseEventArgs e) {
349                         //Console.WriteLine ("MouseMove (Location: {0}", e.Location);
350                         base.OnMouseMove (e);
351                 }
352
353                 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)
354                 {
355                         // The internal paint routines are overridden instead of
356                         // doing the custom paint logic here
357                         base.Paint (graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
358                 }
359
360                 internal override void PaintPartContent (Graphics graphics, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, DataGridViewCellStyle cellStyle, object formattedValue)
361                 {
362                         Color color = Selected ? cellStyle.SelectionForeColor : cellStyle.ForeColor;
363                         TextFormatFlags flags = TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.TextBoxControl;
364         
365                         Rectangle text_area = ContentBounds;
366                         text_area.X += cellBounds.X;
367                         text_area.Y += cellBounds.Y;
368
369                         Rectangle button_area = CalculateButtonArea (cellBounds);
370
371                         // The background of the dropdown button should be gray, not
372                         // the background color of the cell.
373                         graphics.FillRectangle (SystemBrushes.Control, button_area);
374                         ThemeEngine.Current.CPDrawComboButton (graphics, button_area, ButtonState.Normal);
375
376                         if (formattedValue != null)
377                                 TextRenderer.DrawText (graphics, formattedValue.ToString (), cellStyle.Font, text_area, color, flags);
378                 }
379                 
380                 private Rectangle CalculateButtonArea (Rectangle cellBounds)
381                 {
382                         Rectangle button_area, text_area;
383                         int border = ThemeEngine.Current.Border3DSize.Width;
384                         const int button_width = 16;
385
386                         text_area = cellBounds;
387
388                         button_area = cellBounds;
389                         button_area.X = text_area.Right - button_width - border;
390                         button_area.Y = text_area.Y + border;
391                         button_area.Width = button_width;
392                         button_area.Height = text_area.Height - 2 * border;
393                         
394                         return button_area;
395                 }
396
397                 // IMPORTANT: Only call the internal methods from within DataGridViewComboBoxCell
398                 // for adding/removing/clearing because the other methods invoke an update of the 
399                 // column items collection and you might end up in an endless loop.
400                 //
401                 [ListBindable (false)]
402                 public class ObjectCollection : IList, ICollection, IEnumerable {
403
404                         private ArrayList list;
405                         private DataGridViewComboBoxCell owner;
406
407                         public ObjectCollection (DataGridViewComboBoxCell owner)
408                         {
409                                 this.owner = owner;
410                                 list = new ArrayList();
411                         }
412
413                         public int Count {
414                                 get { return list.Count; }
415                         }
416
417                         bool IList.IsFixedSize {
418                                 get { return list.IsFixedSize; }
419                         }
420
421                         public bool IsReadOnly {
422                                 get { return list.IsReadOnly; }
423                         }
424
425                         bool ICollection.IsSynchronized {
426                                 get { return list.IsSynchronized; }
427                         }
428
429                         object ICollection.SyncRoot {
430                                 get { return list.SyncRoot; }
431                         }
432
433                         public virtual object this [int index] {
434                                 get { return list[index]; }
435                                 set {
436                                         ThrowIfOwnerIsDataBound ();
437                                         list[index] = value;
438                                 }
439                         }
440
441                         public int Add (object item)
442                         {
443                                 ThrowIfOwnerIsDataBound ();
444                                 int index = AddInternal (item);
445                                 SyncOwnerItems ();
446                                 return index;
447                         }
448                         
449                         internal int AddInternal (object item)
450                         {
451                                 return list.Add (item);
452                         }
453
454                         internal void AddRangeInternal (ICollection items)
455                         {
456                                 list.AddRange (items);
457                         }
458
459                         public void AddRange (ObjectCollection value)
460                         {
461                                 ThrowIfOwnerIsDataBound ();
462                                 AddRangeInternal (value);
463                                 SyncOwnerItems ();
464                         }
465
466                         private void SyncOwnerItems ()
467                         {
468                                 ThrowIfOwnerIsDataBound ();
469                                 if (owner != null)
470                                         owner.SyncItems ();
471                         }
472
473                         public void ThrowIfOwnerIsDataBound ()
474                         {
475                                 if (owner != null && owner.DataGridView != null && owner.DataSource != null)
476                                         throw new ArgumentException ("Cannot modify collection if the cell is data bound.");
477                         }
478
479                         public void AddRange (params object[] items)
480                         {
481                                 ThrowIfOwnerIsDataBound ();
482                                 AddRangeInternal (items);
483                                 SyncOwnerItems ();
484                         }
485
486                         public void Clear ()
487                         {
488                                 ThrowIfOwnerIsDataBound ();
489                                 ClearInternal ();
490                                 SyncOwnerItems ();
491                         }
492
493                         internal void ClearInternal ()
494                         {
495                                 list.Clear ();
496                         }
497
498                         public bool Contains (object value)
499                         {
500                                 return list.Contains(value);
501                         }
502
503                         void ICollection.CopyTo (Array destination, int index)
504                         {
505                                 CopyTo ((object[]) destination, index);
506                         }
507
508                         public void CopyTo (object[] destination, int arrayIndex)
509                         {
510                                 list.CopyTo (destination, arrayIndex);
511                         }
512
513                         public IEnumerator GetEnumerator ()
514                         {
515                                 return list.GetEnumerator();
516                         }
517
518                         public int IndexOf (object value)
519                         {
520                                 return list.IndexOf(value);
521                         }
522
523                         public void Insert (int index, object item)
524                         {
525                                 ThrowIfOwnerIsDataBound ();
526                                 InsertInternal (index, item);
527                                 SyncOwnerItems ();
528                         }
529
530                         internal void InsertInternal (int index, object item)
531                         {
532                                 list.Insert (index, item);
533                         }
534
535                         public void Remove (object value)
536                         {
537                                 ThrowIfOwnerIsDataBound ();
538                                 RemoveInternal (value);
539                                 SyncOwnerItems ();
540                         }
541
542                         internal void RemoveInternal (object value)
543                         {
544                                 list.Remove (value);
545                         }
546
547                         public void RemoveAt (int index)
548                         {
549                                 ThrowIfOwnerIsDataBound ();
550                                 RemoveAtInternal (index);
551                                 SyncOwnerItems ();
552                         }
553
554                         internal void RemoveAtInternal (int index)
555                         {
556                                 list.RemoveAt (index);
557                         }
558
559                         int IList.Add (object item)
560                         {
561                                 return Add (item);
562                         }
563
564                 }
565
566         }
567
568 }
569