Creating a custom type converter part 3: Types to string
How to use a TypeConverter to provide editing of immutable structs that are properties of containing objects via a PropertyGrid.
I have discussed creating type converters a few times on this
blog, and recently came across another use for them. I have a
.NET Standard library that uses a variety of
that are sometimes properties of concrete classes. I also have a
.NET Framework 4.7.2 demonstration application to work with the
library that uses the
PropertyGrid control to provide easy
Note: While in this article I'm talking about using type converters purely in relation for use with the
PropertyGridcontrol, this isn't their only purpose (just as well otherwise they wouldn't be a part of .NET Standard I assume). You can learn more about the
TypeConverterclass on MSDN.
In a previous article, I noted you could use the ExpandableObjectConverter type to provide a reasonably decent editing experience for basic objects out the box. However, this doesn't work for structs, usually for one of two reasons
structis generally immutable
structis a value type, meaning that the
PropertyGridis actually editing a copy of the value, not the original
There is also a related issue - the
PropertyGrid will default
ToString on the source type. If you haven't
ToString, then this will default to the name of the
type but even if you have it is possible that you followed the
conventions of similar types and return a list of names and
values, great for debugging purposes but less so from the end
user in a consumer application.
In short, there isn't an "easy fix" to allow easy editing of value types. But the solution isn't onerous or convoluted.
For the purposes of this article, I'm going to be working with a
read-only struct named
UserTuple, using the following
This in turn is used by the fictitious
I have also created a
UserTupleConverter class that inherits
TypeConverter and is bound to the
UserTuple type via
The full types are in the available demonstration download.
At the most basic level, we have a class that inherits from
TypeConverter and we override either the
ConvertFrom method pair, or the
pair, or both.
ConvertFrom method is used to create an instance of our
type from another value - often (as in this example), a
ConvertTo method is used to take an instance of our type
and convert it
to another value. Again, this example is going to use a
string but you can do
many things - including design time code generation.
Although I won't cover it here, there are some more advanced features you can do, for example making your type "expandable" so that you can edit individual properties, or providing pre-defined standard values.
To provide our
UserTuple functionality, we first
CanConvertFrom and make sure we return
true if the
For performing the actual conversion, we override
see if the passed
value is a string and if so call our
function to perform the conversion.
Note that I am calling
TryParse. This is so if the user enters an invalid string, they'll get a (hopefully!) tailored exception message - otherwise the exception will be worded similar to "UserTupleConverter cannot convert from System.String.", which is clearly incorrect.
That provides the first part of the functionality we need - I can now type in a separated list of values and have a brand new value created and assigned, thus neatly bypassing both of the problems outlined in the list at the start of the article.
However, as you can see in the animation it is hardly a neat
process due to the inclusion of all the additional data from
To clean up the raw text displayed in the
PropertyGrid, we can
make use of the
CanConvertTo methods. I
touched on these in a previous post regarding generating design
time code but glossed over the string aspects.
It seems you don't need to override
stringtype as the base
TypeConvertorclass has a default
ToStringimplementation, but I choose to include it below for completeness.
First I test to see if the
value is a
UserTuple and if it is
I return a manually constructed comma separated list of the four
I'm not actually sure what best practice is in this situation. Certainly, I wouldn't want the parsing functionality part of this class as it has definite use outside of it, hence having
TryParsemethods on the core type. But for this "simpler" string representation I have no idea and have chosen to keep it a private part of the converter class.
And that's it - with this simple converter in place, my UI behaves exactly as I wanted.
A demonstration application is available from the link below.
Note that as always, this is demonstration code and therefore
may elide extended details or contain less than stellar
functionality (for example the parsing of the
is neither efficient or locale aware).
Like what you're reading? Perhaps you like to buy us a coffee?