Rado R

I have created a TypeConverter for a custom type so I can edit properties of that type from the Property Designer. However, I don't want new objects of that type to be instantiated and just use predefined object Constants. I noticed that ColorConverter does exactly what I want -- it sets the color property to a constant ... something like: target.ColorProperty = Color.Red rather than doing Color c = new Color(); Color.Property = something; target.ColorProperty = c;

Anyway, I was wondering how I can achieve the same trick. And since I don't have access to the .NET source code, maybe some of you geniuses here might know how to do this.

The code that I have so far is:

Discriminator is a simple type that I want to edit in the Property designer. I wanted it to look just like an enum in the Property Designer where the designer will be just picking one of the available values from a dropdown list. However, the reason I didn't use an enum was that the Property Designer will show enum values with the same value as the default (i.e. firts) enum name. For example, the following enum
Code Snippet

enum something
{
one = 1,
two = 2,
three = 1
}

Will show in the property designer as:
  • one
  • two
  • one
Whereas I want it to show "three" in the last option.

So here is the class. It's a real simple Name-Value structure that I actually wanted for it to have only private constructors, but the typeconverter wasn't working with private constructors. The strings that will be displayed in the property designer are simply the names of the constants:
Code Snippet

public class Discriminator
{
int value;
string name;

#region Static DiscriminatorValue Objects
public static readonly Discriminator D1 = new Discriminator(0x1, "D1");
public static readonly Discriminator D2 = new Discriminator(0x2, "D2");
public static readonly Discriminator D3 = new Discriminator(0x4, "D3");
#endregion

#region Constructors
public Discriminator()
{
}
public Discriminator(int v, string n)
{
value = v;
name = n;
}

#endregion

#region Properties
public int Value
{
get { return value; }
set { this.value = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
#endregion

#region Instance Methods
public override string ToString()
{
return this.Name;
}
#endregion

}


Here is the Converter Code
Code Snippet

public class DiscriminatorConverter : TypeConverter
{
private static readonly StandardValuesCollection defaultRelations;

static DiscriminatorConverter()
{
if (defaultRelations == null)
{
System.Reflection.FieldInfo[] fields = typeof(Discriminator).GetFields();
string[] strings = new string[fields.Length + 1];
strings[0] = "blah";
for (int i = 0; i < fields.Length; i++)
{
strings[i + 1] = fields[i].Name;
}
defaultRelations = new StandardValuesCollection(strings);
}

}

//returns the discriminator Constant names
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return defaultRelations;
}

public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
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)
{
if (value == null || (string)value == "" || (string)value == "blah")
return (Discriminator)null;
try
{
Discriminator d = (Discriminator)typeof(Discriminator).GetField((string)value).GetValue(null);
return d;
}
catch { }
throw new ArgumentException("Can not convert '" + (string)value + "' to type DiscriminatorValue");
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
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)
{
if (destinationType == typeof(string) && value is Discriminator)
{
if (value == null) return "blah";
return ((Discriminator)value).Name;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}



So the Property designer displays the properties (Constants) fine and it assigns properties correctly, but it is doing like this:
Code Snippet

Discriminator d = new Discriminator();

d.Name = "D1";

d.Value = 1;

target.Disc = d;

But I want it to use the Constant values instead thus allowing me to make the Discriminator constructors private and prevent other discriminators to be created. So ideally, the generated code by the property designer should be:
Code Snippet
target.Disc = Discriminator.D1;


Sorry for the length of this post, but I wanted to be as thorough as possible.

Thanks for all your help!


Re: Windows Forms Designer TypeConverter to use a Constant/Property rather than creating a new object?

Rado R

I found the solution. The problem was that I wasn't converting the object to InstanceDescriptor. The designer uses an InstanceDescriptor to write the actual code and since I wasn't generating my own InstanceDescriptor, it was giving me the default. Here are the updated methods that handle the conversion to InstanceDescriptor:

Code Snippet
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string) || 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(string))
{
if (value == null)
return "(none)";
else if(value is Discriminator)
return ((Discriminator)value).Name;
}
else if (destinationType == typeof(InstanceDescriptor))
{
if (value == null)
return null;
else if(value is Discriminator)
return new InstanceDescriptor(typeof(Discriminator).GetField(((Discriminator)value).Name), null);

}

return base.ConvertTo(context, culture, value, destinationType);
}

I also made the constructors of Discriminator private so no new instances can be created. So in essence, I got the enum that I was looking for. Now even if I have Discriminators with the same value, they still display their own name in the Property Designer dropdown Big Smile

Cheers!




Re: Windows Forms Designer TypeConverter to use a Constant/Property rather than creating a new object?

Malacki

Haha I was meaning to have a crack at this one since I'll be having to do something along the same lines in the not-too-distant future. Thanks for posting the solution Smile

Marc.