2 // System.ComponentModel.Design.CollectionEditor
5 // Martin Willemoes Hansen (mwh@sysrq.dk)
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 // Ivan N. Zlatev (contact@i-nz.net)
9 // (C) 2003 Martin Willemoes Hansen
10 // (C) 2007 Andreas Nahr
11 // (C) 2007 Ivan N. Zlatev
12 // (C) 2008 Novell, Inc
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 using System.Reflection;
39 using System.Collections;
40 using System.ComponentModel;
41 using System.Drawing.Design;
42 using System.Windows.Forms;
43 using System.Windows.Forms.Design;
45 namespace System.ComponentModel.Design
47 public class CollectionEditor : UITypeEditor
49 protected abstract class CollectionForm : Form
51 private CollectionEditor editor;
52 private object editValue;
54 public CollectionForm (CollectionEditor editor)
59 protected Type CollectionItemType
61 get { return editor.CollectionItemType; }
64 protected Type CollectionType
66 get { return editor.CollectionType; }
69 protected ITypeDescriptorContext Context
71 get { return editor.Context; }
74 public object EditValue
76 get { return editValue; }
80 OnEditValueChanged ();
84 protected object[] Items
86 get { return editor.GetItems (editValue); }
88 if (editValue == null) {
89 object newEmptyCollection = null;
91 if (typeof (Array).IsAssignableFrom (CollectionType))
92 newEmptyCollection = Array.CreateInstance (CollectionItemType, 0);
94 newEmptyCollection = Activator.CreateInstance (CollectionType);
97 object val = editor.SetItems (newEmptyCollection, value);
98 if (val != newEmptyCollection)
101 object val = editor.SetItems (editValue, value);
102 if (val != editValue)
108 protected Type[] NewItemTypes
110 get { return editor.NewItemTypes; }
113 protected bool CanRemoveInstance (object value)
115 return editor.CanRemoveInstance (value);
118 protected virtual bool CanSelectMultipleInstances ()
120 return editor.CanSelectMultipleInstances ();
123 protected object CreateInstance (Type itemType)
125 return editor.CreateInstance (itemType);
128 protected void DestroyInstance (object instance)
130 editor.DestroyInstance (instance);
133 protected virtual void DisplayError (Exception e)
135 MessageBox.Show (e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
138 protected override object GetService (Type serviceType)
140 return editor.GetService (serviceType);
143 protected abstract void OnEditValueChanged ();
145 protected internal virtual DialogResult ShowEditorDialog (IWindowsFormsEditorService edSvc)
147 return edSvc.ShowDialog (this);
151 private class ConcreteCollectionForm : CollectionForm
153 internal class ObjectContainerConverter : TypeConverter
155 private class ObjectContainerPropertyDescriptor : TypeConverter.SimplePropertyDescriptor
157 private AttributeCollection attributes;
159 public ObjectContainerPropertyDescriptor (Type componentType, Type propertyType)
160 : base (componentType, "Value", propertyType)
162 CategoryAttribute cat = new CategoryAttribute (propertyType.Name);
163 attributes = new AttributeCollection (new Attribute[] { cat });
166 public override object GetValue (object component)
168 ObjectContainer container = (ObjectContainer)component;
169 return container.Object;
172 public override void SetValue (object component, object value)
174 ObjectContainer container = (ObjectContainer)component;
175 container.Object = value;
178 public override AttributeCollection Attributes
180 get { return attributes; }
184 public override PropertyDescriptorCollection GetProperties (ITypeDescriptorContext context, object value, Attribute[] attributes)
186 ObjectContainer container = (ObjectContainer)value;
187 ObjectContainerPropertyDescriptor desc = new ObjectContainerPropertyDescriptor (value.GetType (), container.editor.CollectionItemType);
188 PropertyDescriptor[] properties = new PropertyDescriptor[] { desc };
189 PropertyDescriptorCollection pc = new PropertyDescriptorCollection (properties);
193 public override bool GetPropertiesSupported (ITypeDescriptorContext context)
199 [TypeConverter (typeof (ObjectContainerConverter))]
200 private class ObjectContainer
202 internal object Object;
203 internal CollectionEditor editor;
205 public ObjectContainer (object obj, CollectionEditor editor)
208 this.editor = editor;
211 internal string Name {
212 get { return editor.GetDisplayText (Object); }
215 public override string ToString ()
221 private class UpdateableListbox : ListBox
223 public void DoRefreshItem (int index)
225 base.RefreshItem (index);
229 private CollectionEditor editor;
231 private System.Windows.Forms.Label labelMember;
232 private System.Windows.Forms.Label labelProperty;
233 private UpdateableListbox itemsList;
234 private System.Windows.Forms.PropertyGrid itemDisplay;
235 private System.Windows.Forms.Button doClose;
236 private System.Windows.Forms.Button moveUp;
237 private System.Windows.Forms.Button moveDown;
238 private System.Windows.Forms.Button doAdd;
239 private System.Windows.Forms.Button doRemove;
240 private System.Windows.Forms.Button doCancel;
241 private System.Windows.Forms.ComboBox addType;
243 public ConcreteCollectionForm (CollectionEditor editor)
246 this.editor = editor;
248 this.labelMember = new System.Windows.Forms.Label ();
249 this.labelProperty = new System.Windows.Forms.Label ();
250 this.itemsList = new UpdateableListbox ();
251 this.itemDisplay = new System.Windows.Forms.PropertyGrid ();
252 this.doClose = new System.Windows.Forms.Button ();
253 this.moveUp = new System.Windows.Forms.Button ();
254 this.moveDown = new System.Windows.Forms.Button ();
255 this.doAdd = new System.Windows.Forms.Button ();
256 this.doRemove = new System.Windows.Forms.Button ();
257 this.doCancel = new System.Windows.Forms.Button ();
258 this.addType = new System.Windows.Forms.ComboBox ();
259 this.SuspendLayout ();
263 this.labelMember.Location = new System.Drawing.Point (12, 9);
264 this.labelMember.Size = new System.Drawing.Size (55, 13);
265 this.labelMember.Text = "Members:";
269 this.labelProperty.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
270 | System.Windows.Forms.AnchorStyles.Right)));
271 this.labelProperty.Location = new System.Drawing.Point (172, 9);
272 this.labelProperty.Size = new System.Drawing.Size (347, 13);
273 this.labelProperty.Text = "Properties:";
277 this.itemsList.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
278 | System.Windows.Forms.AnchorStyles.Left)));
279 this.itemsList.HorizontalScrollbar = true;
280 this.itemsList.Location = new System.Drawing.Point (12, 25);
281 this.itemsList.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
282 this.itemsList.Size = new System.Drawing.Size (120, 290);
283 this.itemsList.TabIndex = 0;
284 this.itemsList.SelectedIndexChanged += new System.EventHandler (this.itemsList_SelectedIndexChanged);
288 this.itemDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
289 | System.Windows.Forms.AnchorStyles.Left)
290 | System.Windows.Forms.AnchorStyles.Right)));
291 this.itemDisplay.HelpVisible = false;
292 this.itemDisplay.Location = new System.Drawing.Point (175, 25);
293 this.itemDisplay.Size = new System.Drawing.Size (344, 314);
294 this.itemDisplay.TabIndex = 6;
295 this.itemDisplay.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler (this.itemDisplay_PropertyValueChanged);
299 this.doClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
300 this.doClose.Location = new System.Drawing.Point (341, 345);
301 this.doClose.Size = new System.Drawing.Size (86, 26);
302 this.doClose.TabIndex = 7;
303 this.doClose.Text = "OK";
304 this.doClose.Click += new System.EventHandler (this.doClose_Click);
308 this.moveUp.Location = new System.Drawing.Point (138, 25);
309 this.moveUp.Size = new System.Drawing.Size (31, 28);
310 this.moveUp.TabIndex = 4;
311 this.moveUp.Enabled = false;
312 this.moveUp.Text = "Up";
313 this.moveUp.Click += new System.EventHandler (this.moveUp_Click);
317 this.moveDown.Location = new System.Drawing.Point (138, 59);
318 this.moveDown.Size = new System.Drawing.Size (31, 28);
319 this.moveDown.TabIndex = 5;
320 this.moveDown.Enabled = false;
321 this.moveDown.Text = "Dn";
322 this.moveDown.Click += new System.EventHandler (this.moveDown_Click);
326 this.doAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
327 this.doAdd.Location = new System.Drawing.Point (12, 346);
328 this.doAdd.Size = new System.Drawing.Size (59, 25);
329 this.doAdd.TabIndex = 1;
330 this.doAdd.Text = "Add";
331 this.doAdd.Click += new System.EventHandler (this.doAdd_Click);
335 this.doRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
336 this.doRemove.Location = new System.Drawing.Point (77, 346);
337 this.doRemove.Size = new System.Drawing.Size (55, 25);
338 this.doRemove.TabIndex = 2;
339 this.doRemove.Text = "Remove";
340 this.doRemove.Click += new System.EventHandler (this.doRemove_Click);
344 this.doCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
345 this.doCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
346 this.doCancel.Location = new System.Drawing.Point (433, 345);
347 this.doCancel.Size = new System.Drawing.Size (86, 26);
348 this.doCancel.TabIndex = 8;
349 this.doCancel.Text = "Cancel";
350 this.doCancel.Click += new System.EventHandler (this.doCancel_Click);
354 this.addType.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
355 this.addType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
356 this.addType.Location = new System.Drawing.Point (12, 319);
357 this.addType.Size = new System.Drawing.Size (120, 21);
358 this.addType.TabIndex = 3;
362 this.AcceptButton = this.doClose;
363 this.CancelButton = this.doCancel;
364 this.ClientSize = new System.Drawing.Size (531, 381);
365 this.ControlBox = false;
366 this.Controls.Add (this.addType);
367 this.Controls.Add (this.doCancel);
368 this.Controls.Add (this.doRemove);
369 this.Controls.Add (this.doAdd);
370 this.Controls.Add (this.moveDown);
371 this.Controls.Add (this.moveUp);
372 this.Controls.Add (this.doClose);
373 this.Controls.Add (this.itemDisplay);
374 this.Controls.Add (this.itemsList);
375 this.Controls.Add (this.labelProperty);
376 this.Controls.Add (this.labelMember);
377 this.HelpButton = true;
378 this.MaximizeBox = false;
379 this.MinimizeBox = false;
380 this.MinimumSize = new System.Drawing.Size (400, 300);
381 this.ShowInTaskbar = false;
382 this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
383 this.ResumeLayout (false);
385 if (editor.CollectionType.IsGenericType)
386 this.Text = editor.CollectionItemType.Name + " Collection Editor";
388 this.Text = editor.CollectionType.Name + " Collection Editor";
389 foreach (Type type in editor.NewItemTypes)
390 addType.Items.Add (type.Name);
391 if (addType.Items.Count > 0)
392 addType.SelectedIndex = 0;
395 private void UpdateItems ()
397 object[] items = editor.GetItems (EditValue);
399 itemsList.BeginUpdate ();
400 itemsList.Items.Clear ();
401 foreach (object o in items)
402 this.itemsList.Items.Add (new ObjectContainer (o, editor));
403 if (itemsList.Items.Count > 0)
404 itemsList.SelectedIndex = 0;
405 itemsList.EndUpdate ();
409 private void doClose_Click (object sender, EventArgs e)
415 private void SetEditValue ()
417 object[] items = new object[itemsList.Items.Count];
418 for (int i = 0; i < itemsList.Items.Count; i++)
419 items[i] = ((ObjectContainer)itemsList.Items[i]).Object;
423 private void doCancel_Click (object sender, EventArgs e)
425 editor.CancelChanges ();
429 private void itemsList_SelectedIndexChanged (object sender, EventArgs e)
431 if (itemsList.SelectedIndex == -1) {
432 itemDisplay.SelectedObject = null;
436 if (itemsList.SelectedIndex <= 0 || itemsList.SelectedItems.Count > 1)
437 moveUp.Enabled = false;
439 moveUp.Enabled = true;
440 if (itemsList.SelectedIndex > itemsList.Items.Count - 2 || itemsList.SelectedItems.Count > 1)
441 moveDown.Enabled = false;
443 moveDown.Enabled = true;
445 if (itemsList.SelectedItems.Count == 1)
447 ObjectContainer o = (ObjectContainer)itemsList.SelectedItem;
448 if (Type.GetTypeCode (o.Object.GetType ()) != TypeCode.Object)
449 itemDisplay.SelectedObject = o;
451 itemDisplay.SelectedObject = o.Object;
455 object[] items = new object[itemsList.SelectedItems.Count];
456 for (int i = 0; i < itemsList.SelectedItems.Count; i++)
458 ObjectContainer o = (ObjectContainer)itemsList.SelectedItem;
459 if (Type.GetTypeCode (o.Object.GetType ()) != TypeCode.Object)
460 items[i] = ((ObjectContainer)itemsList.SelectedItems[i]);
462 items[i] = ((ObjectContainer)itemsList.SelectedItems[i]).Object;
464 itemDisplay.SelectedObjects = items;
466 labelProperty.Text = ((ObjectContainer)itemsList.SelectedItem).Name + " properties:";
469 private void itemDisplay_PropertyValueChanged (object sender, EventArgs e)
471 int[] selected = new int[itemsList.SelectedItems.Count];
472 for (int i = 0; i < itemsList.SelectedItems.Count; i++)
473 selected[i] = itemsList.Items.IndexOf (itemsList.SelectedItems[i]);
475 // The list might be repopulated if a new instance of the collection edited
476 // is created during the update. This happen for example for Arrays.
479 // Restore current selection in case the list gets repopulated.
480 // Refresh the item after that to reflect possible value change.
482 itemsList.BeginUpdate ();
483 itemsList.ClearSelected ();
484 foreach (int index in selected) {
485 itemsList.DoRefreshItem (index);
486 itemsList.SetSelected (index, true);
488 itemsList.SelectedIndex = selected[0];
489 itemsList.EndUpdate ();
492 private void moveUp_Click (object sender, EventArgs e)
494 if (itemsList.SelectedIndex <= 0)
497 object selected = itemsList.SelectedItem;
498 int index = itemsList.SelectedIndex;
499 itemsList.Items.RemoveAt (index);
500 itemsList.Items.Insert (index - 1, selected);
501 itemsList.SelectedIndex = index - 1;
504 private void moveDown_Click (object sender, EventArgs e)
506 if (itemsList.SelectedIndex > itemsList.Items.Count - 2)
509 object selected = itemsList.SelectedItem;
510 int index = itemsList.SelectedIndex;
511 itemsList.Items.RemoveAt (index);
512 itemsList.Items.Insert (index + 1, selected);
513 itemsList.SelectedIndex = index + 1;
516 private void doAdd_Click (object sender, EventArgs e)
520 o = editor.CreateInstance (editor.NewItemTypes[addType.SelectedIndex]);
521 } catch (Exception ex) {
525 itemsList.Items.Add (new ObjectContainer (o, editor));
526 itemsList.SelectedIndex = -1;
527 itemsList.SelectedIndex = itemsList.Items.Count - 1;
530 private void doRemove_Click (object sender, EventArgs e)
532 if (itemsList.SelectedIndex != -1) {
533 int[] selected = new int[itemsList.SelectedItems.Count];
534 for (int i=0; i < itemsList.SelectedItems.Count; i++)
535 selected[i] = itemsList.Items.IndexOf (itemsList.SelectedItems[i]);
537 for (int i = selected.Length - 1; i >= 0; i--)
538 itemsList.Items.RemoveAt (selected[i]);
540 itemsList.SelectedIndex = Math.Min (selected[0], itemsList.Items.Count-1);
544 // OnEditValueChanged is called only if the EditValue has changed,
545 // which is only in the case when a new instance of the collection is
546 // required, e.g for arrays.
548 protected override void OnEditValueChanged ()
555 private Type collectionItemType;
556 private Type[] newItemTypes;
557 private ITypeDescriptorContext context;
558 private IServiceProvider provider;
559 private IWindowsFormsEditorService editorService;
561 public CollectionEditor (Type type)
564 this.collectionItemType = CreateCollectionItemType ();
565 this.newItemTypes = CreateNewItemTypes ();
568 protected Type CollectionItemType
570 get { return collectionItemType; }
573 protected Type CollectionType
578 protected ITypeDescriptorContext Context
580 get { return context; }
583 protected virtual string HelpTopic
585 get { return "CollectionEditor"; }
588 protected Type[] NewItemTypes
590 get { return newItemTypes; }
593 protected virtual void CancelChanges ()
597 protected virtual bool CanRemoveInstance (object value)
602 protected virtual bool CanSelectMultipleInstances ()
607 protected virtual CollectionEditor.CollectionForm CreateCollectionForm ()
609 return new ConcreteCollectionForm (this);
612 protected virtual Type CreateCollectionItemType ()
614 PropertyInfo[] properties = type.GetProperties ();
615 foreach (PropertyInfo property in properties)
616 if (property.Name == "Item")
617 return property.PropertyType;
618 return typeof (object);
621 protected virtual object CreateInstance (Type itemType)
623 object instance = null;
624 if (typeof (IComponent).IsAssignableFrom (itemType)) {
625 IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost;
627 instance = host.CreateComponent (itemType);
630 if (instance == null) {
631 instance = TypeDescriptor.CreateInstance (provider, itemType, null, null);
636 protected virtual Type[] CreateNewItemTypes ()
638 return new Type[] { collectionItemType };
641 protected virtual void DestroyInstance (object instance)
643 IComponent component = instance as IComponent;
644 if (component != null) {
645 IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost;
647 host.DestroyComponent (component);
651 public override object EditValue (ITypeDescriptorContext context, IServiceProvider provider, object value)
653 this.context = context;
654 this.provider = provider;
656 if (context != null && provider != null)
658 editorService = (IWindowsFormsEditorService)provider.GetService (typeof (IWindowsFormsEditorService));
659 if (editorService != null)
661 CollectionForm editorForm = CreateCollectionForm ();
662 editorForm.EditValue = value;
663 editorForm.ShowEditorDialog (editorService);
664 return editorForm.EditValue;
667 return base.EditValue (context, provider, value);
670 protected virtual string GetDisplayText (object value)
675 PropertyInfo nameProperty = value.GetType ().GetProperty ("Name");
676 if (nameProperty != null)
678 string data = (nameProperty.GetValue (value, null)) as string;
680 if (data.Length != 0)
684 if (Type.GetTypeCode (value.GetType ()) == TypeCode.Object)
685 return value.GetType ().Name;
687 return value.ToString ();
690 public override UITypeEditorEditStyle GetEditStyle (ITypeDescriptorContext context)
692 return UITypeEditorEditStyle.Modal;
695 protected virtual object[] GetItems (object editValue)
697 if (editValue == null)
698 return new object[0];
699 ICollection collection = editValue as ICollection;
700 if (collection == null)
701 return new object[0];
703 object[] result = new object[collection.Count];
704 collection.CopyTo (result, 0);
708 protected virtual IList GetObjectsFromInstance (object instance)
710 ArrayList list = new ArrayList ();
715 protected object GetService (Type serviceType)
717 return context.GetService (serviceType);
720 protected virtual object SetItems (object editValue, object[] value)
722 IList list = (IList) editValue;
727 foreach (object o in value)
733 protected virtual void ShowHelp ()
735 //TODO: Fixme Add parent and URL
736 Help.ShowHelp (null, "", HelpTopic);