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