.NET Forum / Windows Forms / WinForm General / September 2004
Adding to custom collection property in designer
|
|
Thread rating:  |
Bob Dankert - 21 Sep 2004 18:31 GMT I have a custom control (inheriting from ListBox) which has a collection as a property. This collection implements IList (and ICollection, IEnumerator) and has the DesignerSerializationVisibility set to Content. The collection consists of a custom class I created, which I also created a typeconverter so I can use the Visual Studio designer to add items to my collection. When I try to add items to my collection, I get an error that the constructor can not be found.
I tried to use this code without the typeconverter and I had the problem when I added items to my collection, the code would not be created by designer in VS and therefore they would never be added.
I would appreciate any suggestions
My code for the collection property of the control is here:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public MyControls.MyListBoxColumnCollection Columns { get { return this.columns; } set { this.columns = value; } }
The code for the Column item is here (I am omiting the code for the columncollection as this is very basic):
using System; using System.Globalization; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; namespace MyControls { /// <summary> /// Summary description for MyListBoxColumn. /// </summary> [TypeConverter(typeof(MyListBoxColumnConverter))] public class MyListBoxColumn { private int width; public MyListBoxColumn(int width) { this.width = width; } public int Width { get { return this.width; } set { this.width = value; } } } internal class MyListBoxColumnConverter : TypeConverter { public override bool CanConvertTo(ITypeDescriptorContext context,Type destinationType) { if (destinationType == typeof(InstanceDescriptor)) { return true; } return base.CanConvertTo(context, destinationType); } public override object ConvertTo(ITypeDescriptorContext context,CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor)) { MyListBoxColumn col = (MyListBoxColumn)value; //DockPosition dp = (DockPosition)value; ConstructorInfo ctor = typeof(MyListBoxColumn).GetConstructor(new Type[] {typeof(int)}); //ConstructorInfo ctor = typeof(DockPosition).GetConstructor(new Type[] {typeof(int), typeof(int)}); return new InstanceDescriptor(ctor, new object[] {col.Width}); //return new InstanceDescriptor(ctor, new object[] {dp.Left,dp.Top}); } return ConvertTo(context, culture, value, destinationType); } } }
Thanks
Bob Dankert
Claes Bergefall - 22 Sep 2004 08:05 GMT The collection editor calls the public empty constructor when you press the Add button. Since you don't have such a constructor you'll get an error. That gives you two options:
1. Provide a public empty constructor 2. Inherit CollectionEditor and override the CreateInstance method. Add the Editor attribute to your collection property (i.e. Columns) to use your inherited editor
I noticed that the collection property (i.e. Columns) is read-write. Are you sure you want that (usually such properties are read-only)?
/claes
> I have a custom control (inheriting from ListBox) which has a collection as > a property. This collection implements IList (and ICollection, IEnumerator) [quoted text clipped - 91 lines] > > Bob Dankert Bob Dankert - 22 Sep 2004 15:26 GMT I made the suggested change to provide an empty parameterless constructor, and now when I try to add a column I get an unhandles System.StackOverflowException and Visual Studio immediately terminates.
Any other ideas?
Bob
> The collection editor calls the public empty constructor > when you press the Add button. Since you don't have [quoted text clipped - 115 lines] >> >> Bob Dankert Claes Bergefall - 23 Sep 2004 07:23 GMT That should work. What does your new constructor look like? Could you post the rest of the (relevant) code?
/claes
> I made the suggested change to provide an empty parameterless constructor, > and now when I try to add a column I get an unhandles [quoted text clipped - 42 lines] > >> > >> My code for the collection property of the control is here: [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
> >> public MyControls.MyListBoxColumnCollection Columns > >> { [quoted text clipped - 75 lines] > >> > >> Bob Dankert "Jeffrey Tan[MSFT]" - 23 Sep 2004 09:29 GMT Hi Bob,
Based on my understanding, you want to add custom design-time editor for Collection property.
I have writen a sample project, which works well: public class UserControl1 : System.Windows.Forms.UserControl { [TypeConverter(typeof(MyListBoxColumnConverter))] public class MyListBoxColumn { public MyListBoxColumn() { }
private int width=0; public MyListBoxColumn(int width) { this.width = width; }
public int Width { get { return this.width; } set { this.width = value; } } }
public class MyListBoxColumnConverter : TypeConverter { public override bool CanConvertTo(ITypeDescriptorContext context,Type destinationType) { if (destinationType == typeof(InstanceDescriptor)) { return true; } if(destinationType==typeof(string)) { return true; } return base.CanConvertTo(context, destinationType); }
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { MyListBoxColumn col = (MyListBoxColumn)value; if (destinationType == typeof(InstanceDescriptor)) { //DockPosition dp = (DockPosition)value; ConstructorInfo ctor = typeof(MyListBoxColumn).GetConstructor(new Type[] {}); //ConstructorInfo ctor = typeof(DockPosition).GetConstructor(new Type[] {typeof(int), typeof(int)}); return new InstanceDescriptor(ctor, null); //return new InstanceDescriptor(ctor, new object[] {dp.Left,dp.Top}); } if(destinationType==typeof(string)) { return "MyListBoxColumn"+col.Width.ToString(); } return base.ConvertTo(context, culture, value, destinationType); } }
MyListBoxColumnCollection columns=null;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public MyListBoxColumnCollection Columns { get { if(columns==null) { columns=new MyListBoxColumnCollection(); } return this.columns; } set { this.columns = value; } }
[TypeConverter(typeof(MyListBoxColumnCollectionConverter))] public class MyListBoxColumnCollection: CollectionBase { public MyListBoxColumn this[int index] { get { return List[index] as MyListBoxColumn; } }
public void Add(MyListBoxColumn item) { List.Add(item); }
public void AddRange(MyListBoxColumn[] items) { foreach(MyListBoxColumn item in items) { List.Add(item); } } }
internal class MyListBoxColumnCollectionConverter : TypeConverter { public override bool CanConvertTo(ITypeDescriptorContext context,Type destinationType) { if (destinationType == typeof(string)) { return true; } return base.CanConvertTo(context, destinationType); }
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if(sourceType==typeof(string)) { return true; } return base.CanConvertFrom (context, sourceType); }
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { if(value is string) { string str=(string)value; string[] str_arr=str.Split(new char[]{','});
MyListBoxColumnCollection collection=new MyListBoxColumnCollection(); foreach(string item in str_arr) { MyListBoxColumn mlbc=new MyListBoxColumn(int.Parse(item)); collection.Add(mlbc); } return collection; } return base.ConvertFrom (context, culture, value); }
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string)) { MyListBoxColumnCollection collection = (MyListBoxColumnCollection)value; string str=string.Empty; foreach(MyListBoxColumn mlbc in collection) { str=str+mlbc.Width.ToString()+","; } str=str.Substring(0, str.Length-1); return str; } return base.ConvertTo(context, culture, value, destinationType); } } } ============================================================== Thank you for your patience and cooperation. If you have any questions or concerns, please feel free to post it in the group. I am standing by to be of assistance.
Best regards, Jeffrey Tan Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "as is" with no warranties and confers no rights.
Bob Dankert - 24 Sep 2004 14:27 GMT Jeffrey,
After doing more testing with this code you provided (the UserControl1 class), I found that while it does add the columns, I have a couple difficulties:
1. It does not save the widths I assign to the columns. They all go back to 0 eventually.
2. Instead of showing (Collection) ... in the properties of the designer, it shows the values of the widths. This works fine, but would be nice to have it work like the rest of the collections.
3. Is there a way to have the columns automatically name themselves, such as myListBoxColumn1, myListBoxColumn2, myListBoxColumn3, etc...?
Thanks a ton for all your help,
Bob Dankert
> Hi Bob, > [quoted text clipped - 183 lines] > Get Secure! - www.microsoft.com/security > This posting is provided "as is" with no warranties and confers no rights. "Jeffrey Tan[MSFT]" - 24 Sep 2004 08:02 GMT Hi Bob,
Also, we may explicitly specify the CollectionEditor to use certain constructor through inherit the default CollectionEditor, then override CollectionEditor.CreateInstance method and explicitly call the constructor. Like this: public class MyCollectionEditor: CollectionEditor { private Type[] types; public MyCollectionEditor(Type type): base(type) { types=new Type[]{typeof(MyListBoxColumn)}; } protected override Type[] CreateNewItemTypes() { return types; }
protected override object CreateInstance(Type itemType) { if(itemType==typeof(MyListBoxColumn)) { MyListBoxColumn mlbc=new MyListBoxColumn(1); return mlbc; } return base.CreateInstance(itemType); } }
Thank you for your patience and cooperation. If you have any questions or concerns, please feel free to post it in the group. I am standing by to be of assistance.
Best regards, Jeffrey Tan Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "as is" with no warranties and confers no rights.
Bob Dankert - 24 Sep 2004 14:15 GMT Thanks a lot for all your help. I played around with your stuff and switched around some of my code with yours and was able to determine that my collection is the problem. If I use all of your code except substitute my collection in for yours, then the code will no longer work -- it does not save columns when I add them with the column editor.
I will paste my code below as I am curious what might be wrong with this?
public class MyListBoxColumnCollection : IList { private ArrayList myList; public MyListBoxColumnCollection() { myList = new ArrayList(); } public virtual void AddRange(MyListBoxColumn[] values) { foreach (MyListBoxColumn ch in values) this.myList.Add(ch); } #region IList Explicait Members [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public MyListBoxColumn this[int index] { get { if (index > this.myList.Count-1) { throw new ArgumentOutOfRangeException("MyListBoxColumnCollection.Items index",index,"Index out of range, please specify a valid value"); } else return (MyListBoxColumn)this.myList[index]; } set { if (index > this.myList.Count-1) { throw new ArgumentOutOfRangeException("MyListBoxColumnCollection.Items index",index,"Index out of range, please specify a valid value"); } else this.myList[index] = value; } } public void Insert(int index, MyListBoxColumn value) { if (index > this.myList.Count-1) { throw new ArgumentOutOfRangeException("MyListBoxColumnCollection.Items index",index,"Index out of range, please specify a valid value"); } this.myList.Insert(index, value); } public void Remove(MyListBoxColumn value) { if (this.myList.Contains(value)) this.myList.Remove(value); } public bool Contains(MyListBoxColumn value) { return this.myList.Contains(value); } public int IndexOf(MyListBoxColumn value) { return this.myList.IndexOf(value); } public int Add(MyListBoxColumn value) { return this.myList.Add(value); } #endregion #region ICollection Members public bool IsSynchronized { get { return this.myList.IsSynchronized; } } public int Count { get { return this.myList.Count; } } public void CopyTo(Array array, int index) { this.myList.CopyTo(array, index); } public object SyncRoot { get { return this.myList.SyncRoot; } } #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { return this.myList.GetEnumerator(); } #endregion #region IList Members public bool IsReadOnly { get { return false; } } object System.Collections.IList.this[int index] { get { return null; } set { } } void System.Collections.IList.Insert(int index, object value) { } void System.Collections.IList.Remove(object value) { } bool System.Collections.IList.Contains(object value) { return false; } int System.Collections.IList.IndexOf(object value) { return 0; } int System.Collections.IList.Add(object value) { return 0; } public void Clear() { this.myList.Clear(); } public void RemoveAt(int index) { if (index > this.myList.Count-1) { throw new ArgumentOutOfRangeException("MyListBoxColumnCollection.Items index",index,"Index out of range, please specify a valid value"); } this.myList.RemoveAt(index); } public bool IsFixedSize { get { return false; } }
#endregion }
Thanks a ton,
Bob Dankert
> Hi Bob, > [quoted text clipped - 36 lines] > Get Secure! - www.microsoft.com/security > This posting is provided "as is" with no warranties and confers no rights. Bob Dankert - 24 Sep 2004 15:04 GMT Ok, I'm sorry for all of the erradic replies, but here is another problem I ran into. When I try to work with the CollectionEditor, I keep getting an error that it doesnt belong in the System.ComponentModem.Design namespace.
C:\Programming\Projects\lbcoltest\lbcoltest\lbedit.cs(9): The type or namespace name 'CollectionEditor' does not exist in the class or namespace 'System.ComponentModel.Design' (are you missing an assembly reference?)
I'm not really sure what is causing this?
Bob Dankert
> Hi Bob, > [quoted text clipped - 36 lines] > Get Secure! - www.microsoft.com/security > This posting is provided "as is" with no warranties and confers no rights. "Jeffrey Tan[MSFT]" - 27 Sep 2004 10:18 GMT Hi Bob,
This is because System.ComponentModel.Design.CollectionEditor is not included in default assembly in VS.NET IDE, we should add System.Design.dll into application as reference. Then we should have no problem using this class.
Best regards, Jeffrey Tan Microsoft Online Partner Support
 Signature Get Secure! - www.microsoft.com/security This posting is provided "as is" with no warranties and confers no rights.
Bob Dankert - 27 Sep 2004 14:38 GMT Jeffrey,
I noticed this over the weekend, unfortunately I was getting rather frusterated with other stuff and not thinking very logically.
Bob Dankert
> Hi Bob, > [quoted text clipped - 9 lines] > Get Secure! - www.microsoft.com/security > This posting is provided "as is" with no warranties and confers no rights.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|