Reputation: 5407
I have a following type of class assigned to PropertyGrid:
public class Message{
[TypeConverter(typeof(UserConverter))]
public int SenderId { get; set; }
}
And a Converter
public class UserConverter: TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(_users);
}
}
And of course the User
public class User
{
public int Id { get; set; }
public string FullName { get; set; }
public override string ToString()
{
return User.fullName;
}
}
All goes fine so far (note: the Sender
property in the image below is same as SenderId
in the Message
class above. My example here is highly simplified for easier readability):
Until I pick an item in the list, I get an exception in mscorlib:
System.ArgumentException occurred
_HResult=-2147024809
_message=Object of type 'System.String' cannot be converted to type 'System.Int32'.
HResult=-2147024809
IsTransient=false
Message=Object of type 'System.String' cannot be converted to type 'System.Int32'.
Source=mscorlib
StackTrace:
at System.ComponentModel.ReflectPropertyDescriptor.SetValue(Object component, Object value)
InnerException:
It doesn't even seem to go through the TypeConverter
of mine. I realize the property type is int and I've tried overriding ConvertTo
in the UserConverter
but it doesn't seem to hit the method at all when I pick an item in the ComboBox
.
How do I control the return value of such a combo box? In this specific scenario, I would like to return the Message.SenderId
instead of it's object.ToString()
override.
Upvotes: 2
Views: 3960
Reputation: 1016
I imagine you've found the solution by now, but anyway. The thing is that it's trying to find a converter for int to User... If you add this to the constructor of your converter (probably in your case in the static constructor):
Attribute[] attr = new Attribute[1];
TypeConverterAttribute vConv = new TypeConverterAttribute(typeof(UserConverter));
attr[0] = vConv;
TypeDescriptor.AddAttributes(typeof(int), attr);
It will work, but it will also show all ints on your class as a dropdownbox for users ;-) Which is simply not what you want.
It would be cool if we could simply supply a cast for the User class like this:
public static implicit operator int(User user)
{
return user.Id; // implicit conversion
}
But unfortunately this does not work as well. It will keep trying to use the Int32 and throwing exceptions.
The only way to get this working is to write a wrapper for the Id:
public class SenderId
{
public int Id { get; set; }
public string DisplayMember {get; set;}
// this is how it will display the items:
public override string ToString()
{
return $"{DisplayMember} [{Id}]";
}
}
You should also implement Equals and GetHashCode. Then use it like this:
public class Message
{
[TypeConverter(typeof(UserConverter))]
public SenderId SenderId { get; set; }
}
Converter:
public class UserConverter: TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(
ITypeDescriptorContext context)
{
return new StandardValuesCollection(_senderIds);
}
}
Cheers.
Upvotes: 0
Reputation: 4911
I've tried overriding ConvertTo in the UserConverter but it doesn't seem to hit the method at all
In addition to ConvertTo
you should also override CanConvertFrom
and ConvertFrom
methods, to specify that your converter actually can convert between desired types.
There is no need to override CanConvertTo
, because according to MSDN:
It is not necessary to override this method for conversion to a string type.Source
The following, is a simple implementation of these methods based on the models you provided:
public override bool CanConvertFrom(
ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(
ITypeDescriptorContext context, CultureInfo culture, object value)
{
return users.OfType<User>().First(u => u.ToString() == value.ToString()).Id;
}
public override object ConvertTo(
ITypeDescriptorContext context,
CultureInfo culture,
object value,
Type destinationType)
{
if (destinationType == typeof(string))
{
if (value is int)
{
return users.OfType<User>().First(u => u.Id == (int)value).ToString();
}
return value.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
Note: this is a really primitive implementation without any checks for invalid values, exception handling etc. So I discourage using it in production.
Upvotes: 1