// // System.ComponentModel.Design.CollectionEditor // // Authors: // Martin Willemoes Hansen (mwh@sysrq.dk) // Andreas Nahr (ClassDevelopment@A-SoftTech.com) // Ivan N. Zlatev (contact@i-nz.net) // // (C) 2003 Martin Willemoes Hansen // (C) 2007 Andreas Nahr // (C) 2007 Ivan N. Zlatev // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Reflection; using System.Collections; using System.ComponentModel; using System.Drawing.Design; using System.Windows.Forms; using System.Windows.Forms.Design; namespace System.ComponentModel.Design { public class CollectionEditor : UITypeEditor { protected abstract class CollectionForm : Form { private CollectionEditor editor; private object editValue; public CollectionForm (CollectionEditor editor) { this.editor = editor; } protected Type CollectionItemType { get { return editor.CollectionItemType; } } protected Type CollectionType { get { return editor.CollectionType; } } protected ITypeDescriptorContext Context { get { return editor.Context; } } public object EditValue { get { return editValue; } set { editValue = value; OnEditValueChanged (); } } protected object[] Items { get { return editor.GetItems (editValue); } set { if (editValue == null) { object newEmptyCollection = null; try { if (typeof (Array).IsAssignableFrom (CollectionType)) newEmptyCollection = Array.CreateInstance (CollectionItemType, 0); else newEmptyCollection = Activator.CreateInstance (CollectionType); } catch {} object val = editor.SetItems (newEmptyCollection, value); if (val != newEmptyCollection) EditValue = val; } else { object val = editor.SetItems (editValue, value); if (val != editValue) EditValue = val; } } } protected Type[] NewItemTypes { get { return editor.NewItemTypes; } } protected bool CanRemoveInstance (object value) { return editor.CanRemoveInstance (value); } protected virtual bool CanSelectMultipleInstances () { return editor.CanSelectMultipleInstances (); } protected object CreateInstance (Type itemType) { return editor.CreateInstance (itemType); } protected void DestroyInstance (object instance) { editor.DestroyInstance (instance); } protected virtual void DisplayError (Exception e) { MessageBox.Show (e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Information); } protected override object GetService (Type serviceType) { return editor.GetService (serviceType); } protected abstract void OnEditValueChanged (); protected internal virtual DialogResult ShowEditorDialog (IWindowsFormsEditorService edSvc) { return edSvc.ShowDialog (this); } } private class ConcreteCollectionForm : CollectionForm { internal class ObjectContainerConverter : TypeConverter { private class ObjectContainerPropertyDescriptor : TypeConverter.SimplePropertyDescriptor { private AttributeCollection attributes; public ObjectContainerPropertyDescriptor (Type componentType, Type propertyType) : base (componentType, "Value", propertyType) { CategoryAttribute cat = new CategoryAttribute (propertyType.Name); attributes = new AttributeCollection (new Attribute[] { cat }); } public override object GetValue (object component) { ObjectContainer container = (ObjectContainer)component; return container.Object; } public override void SetValue (object component, object value) { ObjectContainer container = (ObjectContainer)component; container.Object = value; } public override AttributeCollection Attributes { get { return attributes; } } } public override PropertyDescriptorCollection GetProperties (ITypeDescriptorContext context, object value, Attribute[] attributes) { ObjectContainer container = (ObjectContainer)value; ObjectContainerPropertyDescriptor desc = new ObjectContainerPropertyDescriptor (value.GetType (), container.editor.CollectionItemType); PropertyDescriptor[] properties = new PropertyDescriptor[] { desc }; PropertyDescriptorCollection pc = new PropertyDescriptorCollection (properties); return pc; } public override bool GetPropertiesSupported (ITypeDescriptorContext context) { return true; } } [TypeConverter (typeof (ObjectContainerConverter))] private class ObjectContainer { internal string Name; internal object Object; internal CollectionEditor editor; public ObjectContainer (string name, object obj, CollectionEditor editor) { this.Name = name; this.Object = obj; this.editor = editor; } public override string ToString () { return Name; } } private class UpdateableListbox : ListBox { public void DoRefreshItem (int index) { base.RefreshItem (index); } } private CollectionEditor editor; private System.Windows.Forms.Label labelMember; private System.Windows.Forms.Label labelProperty; private UpdateableListbox itemsList; private System.Windows.Forms.PropertyGrid itemDisplay; private System.Windows.Forms.Button doClose; private System.Windows.Forms.Button moveUp; private System.Windows.Forms.Button moveDown; private System.Windows.Forms.Button doAdd; private System.Windows.Forms.Button doRemove; private System.Windows.Forms.Button doCancel; private System.Windows.Forms.ComboBox addType; public ConcreteCollectionForm (CollectionEditor editor) : base (editor) { this.editor = editor; this.labelMember = new System.Windows.Forms.Label (); this.labelProperty = new System.Windows.Forms.Label (); this.itemsList = new UpdateableListbox (); this.itemDisplay = new System.Windows.Forms.PropertyGrid (); this.doClose = new System.Windows.Forms.Button (); this.moveUp = new System.Windows.Forms.Button (); this.moveDown = new System.Windows.Forms.Button (); this.doAdd = new System.Windows.Forms.Button (); this.doRemove = new System.Windows.Forms.Button (); this.doCancel = new System.Windows.Forms.Button (); this.addType = new System.Windows.Forms.ComboBox (); this.SuspendLayout (); // // labelMember // this.labelMember.Location = new System.Drawing.Point (12, 9); this.labelMember.Size = new System.Drawing.Size (55, 13); this.labelMember.Text = "Members:"; // // labelProperty // this.labelProperty.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelProperty.Location = new System.Drawing.Point (172, 9); this.labelProperty.Size = new System.Drawing.Size (347, 13); this.labelProperty.Text = "Properties:"; // // itemsList // this.itemsList.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.itemsList.HorizontalScrollbar = true; this.itemsList.Location = new System.Drawing.Point (12, 25); this.itemsList.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; this.itemsList.Size = new System.Drawing.Size (120, 290); this.itemsList.TabIndex = 0; this.itemsList.SelectedIndexChanged += new System.EventHandler (this.itemsList_SelectedIndexChanged); // // itemDisplay // this.itemDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.itemDisplay.HelpVisible = false; this.itemDisplay.Location = new System.Drawing.Point (175, 25); this.itemDisplay.Size = new System.Drawing.Size (344, 314); this.itemDisplay.TabIndex = 6; this.itemDisplay.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler (this.itemDisplay_PropertyValueChanged); // // doClose // this.doClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.doClose.Location = new System.Drawing.Point (341, 345); this.doClose.Size = new System.Drawing.Size (86, 26); this.doClose.TabIndex = 7; this.doClose.Text = "OK"; this.doClose.Click += new System.EventHandler (this.doClose_Click); // // moveUp // this.moveUp.Location = new System.Drawing.Point (138, 25); this.moveUp.Size = new System.Drawing.Size (31, 28); this.moveUp.TabIndex = 4; this.moveUp.Text = "Up"; this.moveUp.Click += new System.EventHandler (this.moveUp_Click); // // moveDown // this.moveDown.Location = new System.Drawing.Point (138, 59); this.moveDown.Size = new System.Drawing.Size (31, 28); this.moveDown.TabIndex = 5; this.moveDown.Text = "Dn"; this.moveDown.Click += new System.EventHandler (this.moveDown_Click); // // doAdd // this.doAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.doAdd.Location = new System.Drawing.Point (12, 346); this.doAdd.Size = new System.Drawing.Size (59, 25); this.doAdd.TabIndex = 1; this.doAdd.Text = "Add"; this.doAdd.Click += new System.EventHandler (this.doAdd_Click); // // doRemove // this.doRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.doRemove.Location = new System.Drawing.Point (77, 346); this.doRemove.Size = new System.Drawing.Size (55, 25); this.doRemove.TabIndex = 2; this.doRemove.Text = "Remove"; this.doRemove.Click += new System.EventHandler (this.doRemove_Click); // // doCancel // this.doCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.doCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.doCancel.Location = new System.Drawing.Point (433, 345); this.doCancel.Size = new System.Drawing.Size (86, 26); this.doCancel.TabIndex = 8; this.doCancel.Text = "Cancel"; this.doCancel.Click += new System.EventHandler (this.doCancel_Click); // // addType // this.addType.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.addType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.addType.Location = new System.Drawing.Point (12, 319); this.addType.Size = new System.Drawing.Size (120, 21); this.addType.TabIndex = 3; // // DesignerForm // this.AcceptButton = this.doClose; this.CancelButton = this.doCancel; this.ClientSize = new System.Drawing.Size (531, 381); this.ControlBox = false; this.Controls.Add (this.addType); this.Controls.Add (this.doCancel); this.Controls.Add (this.doRemove); this.Controls.Add (this.doAdd); this.Controls.Add (this.moveDown); this.Controls.Add (this.moveUp); this.Controls.Add (this.doClose); this.Controls.Add (this.itemDisplay); this.Controls.Add (this.itemsList); this.Controls.Add (this.labelProperty); this.Controls.Add (this.labelMember); this.HelpButton = true; this.MaximizeBox = false; this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size (400, 300); this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.ResumeLayout (false); #if NET_2_0 if (editor.CollectionType.IsGenericType) this.Text = editor.CollectionItemType.Name + " Collection Editor"; else this.Text = editor.CollectionType.Name + " Collection Editor"; #else this.Text = editor.CollectionType.Name + " Collection Editor"; #endif foreach (Type type in editor.NewItemTypes) addType.Items.Add (type.Name); if (addType.Items.Count > 0) addType.SelectedIndex = 0; } private void AddItems () { foreach (object o in editor.GetItems (EditValue)) { this.itemsList.Items.Add (new ObjectContainer (editor.GetDisplayText (o), o, editor)); } if (itemsList.Items.Count > 0) itemsList.SelectedIndex = 0; } private void doClose_Click (object sender, EventArgs e) { object[] items = new object[itemsList.Items.Count]; for (int i = 0; i < itemsList.Items.Count; i++) items[i] = ((ObjectContainer)itemsList.Items[i]).Object; EditValue = editor.SetItems (EditValue, items); this.Close (); } private void doCancel_Click (object sender, EventArgs e) { editor.CancelChanges (); this.Close (); } private void itemsList_SelectedIndexChanged (object sender, EventArgs e) { if (itemsList.SelectedIndex <= 0 || itemsList.SelectedItems.Count > 1) moveUp.Enabled = false; else moveUp.Enabled = true; if (itemsList.SelectedIndex > itemsList.Items.Count - 2 || itemsList.SelectedItems.Count > 1) moveDown.Enabled = false; else moveDown.Enabled = true; if (itemsList.SelectedIndex == -1) return; if (itemsList.SelectedItems.Count == 1) { ObjectContainer o = (ObjectContainer)itemsList.SelectedItem; if (Type.GetTypeCode (o.Object.GetType ()) != TypeCode.Object) itemDisplay.SelectedObject = o; else itemDisplay.SelectedObject = o.Object; } else { object[] items = new object[itemsList.SelectedItems.Count]; for (int i = 0; i < itemsList.SelectedItems.Count; i++) { ObjectContainer o = (ObjectContainer)itemsList.SelectedItem; if (Type.GetTypeCode (o.Object.GetType ()) != TypeCode.Object) items[i] = ((ObjectContainer)itemsList.SelectedItems[i]); else items[i] = ((ObjectContainer)itemsList.SelectedItems[i]).Object; } itemDisplay.SelectedObjects = items; } labelProperty.Text = ((ObjectContainer)itemsList.SelectedItem).Name + " properties:"; } private void itemDisplay_PropertyValueChanged (object sender, EventArgs e) { int[] indices = new int[itemsList.SelectedIndices.Count]; itemsList.SelectedIndices.CopyTo (indices, 0); for (int i = 0; i < indices.Length; i++) { ObjectContainer o = (ObjectContainer)itemsList.Items [indices[i]]; o.Name = editor.GetDisplayText (o.Object); itemsList.DoRefreshItem (indices[i]); } for (int i = 0; i < indices.Length; i++) itemsList.SetSelected (indices[i], true); } private void moveUp_Click (object sender, EventArgs e) { if (itemsList.SelectedIndex <= 0) return; object selected = itemsList.SelectedItem; int index = itemsList.SelectedIndex; itemsList.Items.RemoveAt (index); itemsList.Items.Insert (index - 1, selected); itemsList.SelectedIndex = index - 1; } private void moveDown_Click (object sender, EventArgs e) { if (itemsList.SelectedIndex > itemsList.Items.Count - 2) return; object selected = itemsList.SelectedItem; int index = itemsList.SelectedIndex; itemsList.Items.RemoveAt (index); itemsList.Items.Insert (index + 1, selected); itemsList.SelectedIndex = index + 1; } private void doAdd_Click (object sender, EventArgs e) { object o; try { o = editor.CreateInstance (editor.NewItemTypes[addType.SelectedIndex]); } catch (Exception ex) { DisplayError (ex); return; } itemsList.Items.Add (new ObjectContainer (editor.GetDisplayText (o), o, editor)); } private void doRemove_Click (object sender, EventArgs e) { if (itemsList.SelectedIndex != -1) itemsList.Items.RemoveAt (itemsList.SelectedIndex); } protected override void OnEditValueChanged () { AddItems (); } } private Type type; private Type collectionItemType; private Type[] newItemTypes; private ITypeDescriptorContext context; private IServiceProvider provider; private IWindowsFormsEditorService editorService; public CollectionEditor (Type type) { this.type = type; this.collectionItemType = CreateCollectionItemType (); this.newItemTypes = CreateNewItemTypes (); } protected Type CollectionItemType { get { return collectionItemType; } } protected Type CollectionType { get { return type; } } protected ITypeDescriptorContext Context { get { return context; } } protected virtual string HelpTopic { get { return "CollectionEditor"; } } protected Type[] NewItemTypes { get { return newItemTypes; } } protected virtual void CancelChanges () { } protected virtual bool CanRemoveInstance (object value) { return true; } protected virtual bool CanSelectMultipleInstances () { return true; } protected virtual CollectionEditor.CollectionForm CreateCollectionForm () { return new ConcreteCollectionForm (this); } protected virtual Type CreateCollectionItemType () { PropertyInfo[] properties = type.GetProperties (); foreach (PropertyInfo property in properties) if (property.Name == "Item") return property.PropertyType; return typeof (object); } protected virtual object CreateInstance (Type itemType) { object instance = null; if (typeof (IComponent).IsAssignableFrom (itemType)) { IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost; if (host != null) instance = host.CreateComponent (itemType); } if (instance == null) { #if NET_2_0 instance = TypeDescriptor.CreateInstance (provider, itemType, null, null); #else instance = Activator.CreateInstance (itemType); #endif } return instance; } protected virtual Type[] CreateNewItemTypes () { return new Type[] { collectionItemType }; } protected virtual void DestroyInstance (object instance) { IComponent component = instance as IComponent; if (component != null) { IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost; if (host != null) host.DestroyComponent (component); } } public override object EditValue (ITypeDescriptorContext context, IServiceProvider provider, object value) { this.context = context; this.provider = provider; if (context != null && provider != null) { editorService = (IWindowsFormsEditorService)provider.GetService (typeof (IWindowsFormsEditorService)); if (editorService != null) { CollectionForm editorForm = CreateCollectionForm (); editorForm.EditValue = value; editorForm.ShowEditorDialog (editorService); return editorForm.EditValue; } } return base.EditValue (context, provider, value); } protected virtual string GetDisplayText (object value) { if (value == null) return string.Empty; PropertyInfo nameProperty = value.GetType ().GetProperty ("Name"); if (nameProperty != null) { string data = (nameProperty.GetValue (value, null)) as string; if (data != null) if (data.Length != 0) return data; } if (Type.GetTypeCode (value.GetType ()) == TypeCode.Object) return value.GetType ().Name; else return value.ToString (); } public override UITypeEditorEditStyle GetEditStyle (ITypeDescriptorContext context) { return UITypeEditorEditStyle.Modal; } protected virtual object[] GetItems (object editValue) { if (editValue == null) return new object[0]; ICollection collection = editValue as ICollection; if (collection == null) return new object[0]; object[] result = new object[collection.Count]; collection.CopyTo (result, 0); return result; } protected virtual IList GetObjectsFromInstance (object instance) { ArrayList list = new ArrayList (); list.Add (instance); return list; } protected object GetService (Type serviceType) { return context.GetService (serviceType); } protected virtual object SetItems (object editValue, object[] value) { IList list = (IList) editValue; if (list == null) return null; list.Clear (); foreach (object o in value) list.Add (o); return list; } protected virtual void ShowHelp () { //TODO: Fixme Add parent and URL Help.ShowHelp (null, "", HelpTopic); } } }