Using .NET 4, C#
Let's say I have class Info
that extends CustomTypeDescriptor
. An instance of class Info
has a dictionary of <string, object>
pairs, loaded at runtime.
I'd like to be able to expose the dictionary keys as properties (so that each instance of Info
has different properties). The values of the properties should be the corresponding values in the dictionary.
I got around to exposing properties:
public override PropertyDescriptorCollection GetProperties()
{
var orig = base.GetProperties();
var newProps = dictionary.Select( kvp =>
TypeDescriptor.CreateProperty(
this.GetType(),
kvp.key,
typeof(string)));
return new PropertyDescriptorCollection(
orig.Cast<PropertyDescriptor>()
.Concat(newProps)
.ToArray());
}
The problem is, how do I get their values?
var info = new Info(new Dictionary<string, object>{{"some_property", 5}};
var prop = TypeDescriptor.GetProperties(i_info)["some_property"];
var val = prop.GetValue(i_info); //should return 5
The only way I found to get control when prop.GetValue()
is called was to override GetPropertyOwner(PropertyDescriptor pd)
, but the way I understand it, it expects me to return the instance of another type that has a matching real (compiled) property.
I'd like to be able to write the actual implementation of the property myself (for this example, return the value in the dictionary whose key matches the property name).
Is this possible?
You need to make your own implementation of the PropertyDescriptor
class overriding GetValue
method. So instead of TypeDescriptor.CreateProperty
you'll use new MyCoolPropertyDescriptor(dictionary, kvp.Key)
or like.
Here is a sample of how it can be implemented:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace ConsoleApplication1
{
internal sealed class MyCoolPropertyDescriptor : PropertyDescriptor
{
private Func<object, object> propertyGetter;
private Action<object, object> propertySetter;
public MyCoolPropertyDescriptor(
string name,
Func<object, object> propertyGetter,
Action<object, object> propertySetter)
: base(name, new Attribute[] {})
{
this.propertyGetter = propertyGetter;
this.propertySetter = propertySetter;
}
public override bool CanResetValue(object component)
{
return true;
}
public override System.Type ComponentType
{
get { return typeof(object); }
}
public override object GetValue(object component)
{
return this.propertyGetter(component);
}
public override bool IsReadOnly
{
get { return false; }
}
public override System.Type PropertyType
{
get { return typeof(object); }
}
public override void ResetValue(object component)
{
this.propertySetter(component, null);
}
public override void SetValue(object component, object value)
{
this.propertySetter(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
public sealed class Info : CustomTypeDescriptor
{
IDictionary<string, object> properties;
public Info(IDictionary<string, object> properties)
{
this.properties = properties;
}
public override PropertyDescriptorCollection GetProperties()
{
var orig = base.GetProperties();
var newProps = this.properties
.Select(kvp => new MyCoolPropertyDescriptor(
kvp.Key,
o => ((Info)o).properties[kvp.Key],
(o, v) => ((Info)o).properties[kvp.Key] = v));
return new PropertyDescriptorCollection(orig
.Cast<PropertyDescriptor>()
.Concat(newProps)
.ToArray());
}
}
internal class Program
{
private static void Main(string[] args)
{
var info = new Info(new Dictionary<string, object>{{"some_property", 5}});
var prop = TypeDescriptor.GetProperties(info)["some_property"];
var val = prop.GetValue(info); //should return 5
Console.WriteLine(val);
}
}
}