I apologize for double-posting, but despite the view count on my previous post, I have a feeling no one is actually reading it because it is too long. So I am trying again, shorter this time. Basically, I want to have a property on my form that is serialized to InitializeComponent like this:
myForm.MyProperty = new MyClass(typeof(MyOtherClass), MyOtherClass.FldMyField);
where FldMyField is a constant string. MyClass has a TypeConverter that is doing the design-time conversion between string and a MyClass object so it can be shown in the property browser, and that is all working correctly. The same TypeConverter is also converting MyClass to an InstanceDescriptor in ConvertTo, like this:
ConstructorInfo c = typeof(MyClass).GetConstructor(new Type[] { typeof(Type), typeof(string) });
if(null == c)
throw new ApplicationException("MyClass constructor not found");
InstanceDescriptor propInstance = new InstanceDescriptor(typeof(MyOtherClass).GetField("FldMyField", BindingFlags.Static | BindingFlags.Public), null);
InstanceDescriptor classInstance = new InstanceDescriptor(c, new object[] {typeof(MyOtherClass), propInstance});
return classInstance;
But this code does not end up serializing anything to InitializeComponent. I am pretty sure something is going wrong during serialization, and the designer is just dumping that property to maintain the integrity of the serialization stream. If I pass null instead of propInstance as an argument to classInstance, I get this:
myForm.MyProperty = new MyClass(typeof(MyOtherClass), null);
which is exactly what I would expect. If i return propInstance instead of classInstance, I get this:
myForm.MyProperty = (MyClass)MyOtherClass.FldMyField;
The designer is trying to explicitly cast the string field to the type of the property, but it is interpreting the InstanceDescriptor correctly. So the question is, if both InstanceDescriptors are correct, why do they not work when I put them together The MSDN says you should be able to do that: "The [arguments] collection [of the InstanceDescriptor constructor] can also consist of other instances of InstanceDescriptor." But I have not been able to find any examples of this anywhere. Surely someone somewhere has tried this before. Does it just not work
Any help would be greatly appreciated.
David

Nested InstanceDescriptors (short)
Brian Kitt
Hello David,
this is an interesting question. I don't know the answer (like, i suppose, all who have read and not answered).
Have you tried to reflect the designer classes This helped me to solve some (other) problems. The documentation is sometimes ... , you know. The only absolute truth is the program text. It's not always easy to read texts without meaningful ids, but this is a reliable way.
Regards,
D.Raiko
DanielXIX
Well...sort of.
What I would REALLY like is to be able to do is to end up with a statement like this in InitializeComponent:
MyControl.MyProperty = MyClass.FldMyField
where MyProperty is just a string property. But as far as I can tell there is no way to do this without writing a custom Serializer. And since the documentation on serializers is practically non-existent, and I was unable to find any reliable samples on the internet, I gave up on that idea. So I created MyOtherClass for the sole purpose of having something I could apply a custom TypeConverter to. Having to create ANOTHER class whose only point is to make serialization work right...I think its going too far. Can you explain why the method I am trying (as described in my first post) isnt working
I'm really surprised that I seem to be the only person who wants to do something like this. It doesnt seem like its that complicated or outrageous. The InstanceDescriptor documentation says it should work; I know documentation is never perfect, but the MSDN is usually pretty good, especially in .NET. I even started looking at the IL code, as suggested in a previous post. I found the section dealing with converting InstanceDescriptors, and in fact nested InstanceDescriptors seem to be supported. I could find nothing in the IL that even gave a clue as to what is wrong. Is there no way to find out why a method that is explicitly supported in the documentation doesnt work in practice Do I need to log a bug report
Also, I didnt mark the previous post as an answer, and in fact it does not answer my question. Can it be unmarked, so that thread is not marked as answered I think that will give me a better chance of finding an answer to my question.
MrBillNJ
Helgeo
Actually, the above example does nest InstanceDescriptors indirectly. There isn't any way I know of to nest InstanceDescriptors directly since each value of the argument array is intended to be a value to be serialized -- not a representation of that value.
For your second question (serializing the value as a property) -- this can be done in a similar way, but you still have to use a custom Type. You could change the serializer for string, but you'd probably would break other components in the designer if you did this.
You can change the serializer for the whole control if you want and then serialize the returned statements from the original serializer. I wouldn't recommend this since you are taking a dependency on implementation -- which is sure to break later.
I show this in the example below. Sorry I cannot provide more help as this is about as close I can get to your requests. About marking posts as answers -- it does not mean your post will not get further comments or posts if a post is marked as an answer. It just allows us to mark samples or snippets so they can be found by others.
Ideally, what you want is to be able to mark a single property with a specific DesignerSerializer. We will look at this in the future, but I cannot make any promises.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel.Design;
using System.Windows.Forms.Design;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
using System.CodeDom;
namespace WindowsApplication122
{
[DesignerSerializer(typeof(MyControlSerializer), typeof(CodeDomSerializer))]
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
MyClass myProperty = new MyClass(typeof(MyOtherClass), MyOtherClass.FldMyField);
public MyClass MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
MyStringClass myProperty2 = MyOtherClass.FldMyField;
public MyStringClass MyProperty2 {
get { return myProperty2; }
set { myProperty2 = value; }
}
}
public class MyControlSerializer : CodeDomSerializer {
public override object Serialize(IDesignerSerializationManager manager, object value) {
CodeDomSerializer serializer = GetSerializer(manager, this.GetType().BaseType);
object result = serializer.Serialize(manager, value);
//result is a codestatementcollection.
//you could parse this and change it, but I wouldn't recommend it.
return result;
}
}
[TypeConverter(typeof(MyClassConverter))]
public class MyClass {
public MyClass(Type t, MyStringClass param2)
{
}
}
public class MyStringClassSerializer : CodeDomSerializer {
public override object Serialize(IDesignerSerializationManager manager, object value) {
if (value is MyStringClass && ((MyStringClass)value) == MyOtherClass.FldMyField) {
return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(MyOtherClass)), "FldMyField");
}
return base.Serialize(manager, value);
}
}
public class MyClassConverter : 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, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(InstanceDescriptor))
{
return base.ConvertTo(context, culture, value, destinationType);
}
//instancedescriptor...
ConstructorInfo c = typeof(MyClass).GetConstructor(new Type[] { typeof(Type), typeof(MyStringClass) });
if (null == c)
{
throw new ApplicationException("MyClass constructor not found");
}
InstanceDescriptor classInstance = new InstanceDescriptor(c,
new object[] { typeof(MyOtherClass), MyOtherClass.FldMyField });
return classInstance;
}
}
public class MyOtherClass
{
public static MyStringClass FldMyField = new MyStringClass("FldMyFieldVal");
public static MyStringClass Default = new MyStringClass("DefaultVal");
}
[TypeConverter(typeof(MyStringConverter))]
[DesignerSerializer(typeof(MyStringClassSerializer), typeof(CodeDomSerializer))]
public class MyStringClass
{
string _val;
public string Val
{
get { return _val; }
set { _val = value; }
}
public MyStringClass(string value)
{
_val = value;
}
}
class MyStringConverter : 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, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(InstanceDescriptor))
{
return base.ConvertTo(context, culture, value, destinationType);
}
MyStringClass strVal = value as MyStringClass;
if (strVal == null) {
return null;
}
if (strVal.Val == "FldMyFieldVal")
{
return new InstanceDescriptor(typeof(MyOtherClass).GetField("FldMyField", BindingFlags.Static | BindingFlags.Public),
null);
}
else
{
return new InstanceDescriptor(typeof(MyOtherClass).GetField("Default", BindingFlags.Static | BindingFlags.Public),
null);
}
}
}
}
Thomas Jespersen
Actually, I was wrong. An instanceDescriptor can reference a static field.
I was able to get fairly close to what you need. The below does produce code such as:
this.userControl11.MyProperty = new WindowsApplication122.MyClass(typeof(WindowsApplication122.MyOtherClass), WindowsApplication122.MyOtherClass.FldMyField);Is this acceptable to you
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel.Design;
using System.Windows.Forms.Design;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
using System.CodeDom;
namespace WindowsApplication122
{
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
MyClass myProperty = new MyClass(typeof(MyOtherClass), MyOtherClass.FldMyField);
public MyClass MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
[TypeConverter(typeof(MyClassConverter))]
public class MyClass {
public MyClass(Type t, MyStringClass param2)
{
}
}
public class MyClassConverter : 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, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(InstanceDescriptor))
{
return base.ConvertTo(context, culture, value, destinationType);
}
//instancedescriptor...
ConstructorInfo c = typeof(MyClass).GetConstructor(new Type[] { typeof(Type), typeof(MyStringClass) });
if (null == c)
{
throw new ApplicationException("MyClass constructor not found");
}
InstanceDescriptor classInstance = new InstanceDescriptor(c,
new object[] { typeof(MyOtherClass), MyOtherClass.FldMyField });
return classInstance;
}
}
public class MyOtherClass
{
public static MyStringClass FldMyField = new MyStringClass("FldMyFieldVal");
public static MyStringClass Default = new MyStringClass("DefaultVal");
}
[TypeConverter(typeof(MyStringConverter))]
public class MyStringClass
{
string _val;
public string Val
{
get { return _val; }
set { _val = value; }
}
public MyStringClass(string value)
{
_val = value;
}
}
class MyStringConverter : 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, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(InstanceDescriptor))
{
return base.ConvertTo(context, culture, value, destinationType);
}
MyStringClass strVal = value as MyStringClass;
if (strVal == null) {
return null;
}
if (strVal.Val == "FldMyFieldVal")
{
return new InstanceDescriptor(typeof(MyOtherClass).GetField("FldMyField", BindingFlags.Static | BindingFlags.Public),
null);
}
else
{
return new InstanceDescriptor(typeof(MyOtherClass).GetField("Default", BindingFlags.Static | BindingFlags.Public),
null);
}
}
}
}
OZ83
I'm not sure a nested instancedescriptor is what you want here. Its more like a CodeDOM field reference embedded into the instancedescriptor. I am taking a look at this.