To push the property grid to show a dropdown for a property, usually the best way is creating a TypeConverter
for the prioperty. It basically Provides a unified way of converting types of values to other types, as well as for accessing standard values and subproperties.
In this post I’ll show how you can create generic TypeConverter which shows a dropdown in property grid and allows you to pick all the types which are derived from the type that you specified in the generic parameter. So for example if you want to have a Form
picker which allows you to pick from all the forms which are deriving from BaseForm
, it will show something like this:
To do so, as I explained above, the first step is creating a new type converter by deriving from TypeConverter
.
Then the next step is overriding GetStandardValues
method of the TypeConverter
and return the desired values.
Also you need to override GetStandardValuesSupported
method and return true
to determine this type converter can show dropdown.
It’s also a good idea to limit the editor just to select from the dropdown, by overrideing GetStandardValuesExclusive
.
Then you need to get the desired types; There are two services that can help you at design-time to discover and resolve all types in the solution:
ITypeDiscoveryService
: Discovers available types at design time.-
ITypeResolutionService
: Provides an interface to retrieve an assembly or type by name.
Now by knowing about above options, you can create a custom type converter to discover all form types in the project and list in the dropdown; and the usage will be something like this, easy and clean:
[TypeConverter(typeof(TypeTypeConverter<BaseForm>))]
public Type FormType { get; set; }
And here is the code for TypeTypeConverter<T>
:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Globalization;
using System.Linq;
public class TypeTypeConverter<T> : TypeConverter
{
public override bool GetStandardValuesExclusive
(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, CultureInfo culture, object value)
{
if (value is string)
return GetTypeFromName(context, (string)value);
return base.ConvertFrom(context, culture, value);
}
public override bool GetStandardValuesSupported
(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues
(ITypeDescriptorContext context)
{
return new StandardValuesCollection(GetProjectTypes(context)
.OrderBy(x => x.FullName).ToList());
}
private List<Type> GetProjectTypes(IServiceProvider serviceProvider)
{
var typeDiscoverySvc = (ITypeDiscoveryService)serviceProvider
.GetService(typeof(ITypeDiscoveryService));
var types = typeDiscoverySvc.GetTypes(typeof(object), true)
.Cast<Type>().Where(item =>
item.IsPublic &&
typeof(T).IsAssignableFrom(item) &&
!item.IsAbstract &&
!item.FullName.StartsWith("System")
).ToList();
return types;
}
private Type GetTypeFromName(IServiceProvider
serviceProvider, string typeName)
{
ITypeResolutionService typeResolutionSvc =
(ITypeResolutionService)serviceProvider
.GetService(typeof(ITypeResolutionService));
return typeResolutionSvc.GetType(typeName);
}
}