Click here to Skip to main content
15,900,816 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm experimenting with the ICustomTypeDescriptor interface and the PropertyDescriptor class in-order to create dynamic properties on objects. I am having a lot of success with simple objects, but I cannot get nested objects to create their dynamic properties?

For example in the data-binding dialog below, I'm adding my Person class as a StaticResource, then trying to data-bind the Person.Child.Name to a testbox:


For the Person.Child I am expecting to see my dynamically created properties (Name and Age), but as you can see it's not working as expected? It's almost as if the databinding dialog is not interrogating the ICustomTypeDescriptor interface on the Person.Child?

Any guidance on how to make these nested properties 'visible'?


Outer class




public class Person : ICustomTypeDescriptor, INotifyPropertyChanged
{
    private readonly List<CustomPropertyDescriptor> propertyDescriptors = new List<CustomPropertyDescriptor>();
    private readonly Dictionary<string, object> properties = new Dictionary<string, object>();

    public Person()
    {
        // 'Dynamic' Property
        string name = "Name";
        object value = "Person's Name";
        this.properties.Add(name, value);
        var propertyDescriptor = new CustomPropertyDescriptor(
            typeof(Person), 
            name, 
            value, 
            value.GetType().GetCustomAttributes(true).Cast<Attribute>().ToArray());
        this.propertyDescriptors.Add(propertyDescriptor);

        // 'Dynamic' Property
        name = "Child";
        value = new Child();
        this.properties.Add(name, value);
        propertyDescriptor = new CustomPropertyDescriptor(
            typeof(Child),
            name,
            value,
            value.GetType().GetCustomAttributes(true).Cast<Attribute>().ToArray());
        this.propertyDescriptors.Add(propertyDescriptor);

        propertyDescriptor.PropertyChanged += this.PropertyDescriptorPropertyChanged;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // Test Property (shouldn't be visible)
    public string NotDynamic { get; set; }

    public override string ToString()
    {
        return string.Format("{0} ({1})", this.properties["Name"], this.properties["Age"]);
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this, true);
    }

    public string GetClassName()
    {
        return TypeDescriptor.GetClassName(this, true);
    }

    public string GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }

    public EventDescriptor GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        try
        {
            return this.propertyDescriptors.First();
        }
        catch (InvalidOperationException)
        {
            return null;
        }
    }

    public object GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return new PropertyDescriptorCollection(this.propertyDescriptors.ToArray());
    }

    public PropertyDescriptorCollection GetProperties()
    {
        return this.GetProperties(null);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }

    protected void OnPropertyChanged(string name)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }

    private void PropertyDescriptorPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.OnPropertyChanged(e.PropertyName);
    }
}






Inner class



[TypeConverter(typeof(ExpandableObjectConverter))]
public class Child : ICustomTypeDescriptor, INotifyPropertyChanged 
{
    private readonly List<CustomPropertyDescriptor> propertyDescriptors = new List<CustomPropertyDescriptor>();
    private readonly Dictionary<string, object> properties = new Dictionary<string, object>();

    public Child()
    {
        // 'Dynamic' Property
        string name = "Name";
        object value = "Person's Child";
        this.properties.Add(name, value);
        var propertyDescriptor = new CustomPropertyDescriptor(
            typeof(Person),
            name,
            value,
            value.GetType().GetCustomAttributes(true).Cast<Attribute>().ToArray());
        propertyDescriptor.PropertyChanged += this.PropertyDescriptorPropertyChanged;
        this.propertyDescriptors.Add(propertyDescriptor);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // Test Property (shouldn't be visible)
    public string NotDynamic { get; set; }

    public override string ToString()
    {
        return string.Format("{0} ({1})", this.properties["Name"], this.properties["Age"]);
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this, true);
    }

    public string GetClassName()
    {
        return TypeDescriptor.GetClassName(this, true);
    }

    public string GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }

    public EventDescriptor GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        try
        {
            return this.propertyDescriptors.First();
        }
        catch (InvalidOperationException)
        {
            return null;
        }
    }

    public object GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return new PropertyDescriptorCollection(this.propertyDescriptors.ToArray());      
    }

    public PropertyDescriptorCollection GetProperties()
    {
        return this.GetProperties(null);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }

    protected void OnPropertyChanged(string name)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }

    private void PropertyDescriptorPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.OnPropertyChanged(e.PropertyName);
    }
}



Quote:
Property Descriptor



Quote:
public class CustomPropertyDescriptor : PropertyDescriptor, INotifyPropertyChanged
{
private readonly Type componentType;
private string name;
private object value;

public CustomPropertyDescriptor(Type componentType, string name, object value, Attribute[] attributes)
: base(name, attributes)
{
this.componentType = componentType;
this.name = name;
this.value = value;
}

public event PropertyChangedEventHandler PropertyChanged;

public override bool IsBrowsable
{
get
{
return true;
}
}

public override Type ComponentType
{
get { return this.componentType; }
}

public override bool IsReadOnly
{
get { return false; }
}

public override Type PropertyType
{
get { return this.value.GetType(); }
}

public override object GetValue(object component)
{
return this.value;
}

public override bool CanResetValue(object component)
{
return false;
}

public override void ResetValue(object component)
{
}

public override void SetValue(object component, object value)
{
this.value = value;
this.OnPropertyChanged(this.Name);
}

public override bool ShouldSerializeValue(object component)
{
return false;
}

private void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900