19

我正在试验ICustomTypeDescriptor接口和PropertyDescriptor类,以便在对象上创建动态属性。我在简单对象方面取得了很大成功,但我无法让嵌套对象创建它们的动态属性?

例如,在下面的数据绑定对话框中,我将我的 Person 类添加为StaticResource,然后尝试将数据绑定Person.Child.Name到测试框:

对于Person.Child我希望看到我动态创建的属性(NameAge),但正如您所见,它没有按预期工作?就好像数据绑定对话框没有ICustomTypeDescriptor询问Person.Child

有关如何使这些嵌套属性“可见”的任何指导?

外层

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);
    }
}

内部类

[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);
    }
}

属性描述符

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));
        }
    }
}
4

1 回答 1

1

我认为您错误地设置了 ComponentType 属性。

  1. 属性:Person.Child 应该将 ComponentType 设置为 typeof(Person) 而不是 typeof(Child)。就像属性:Person.Name。
  2. 属性:Child.Name 应该将 ComponentType 设置为 typeof(Child)。

ComponentType 用于定义属性所有者类型。

于 2014-05-11T15:33:51.533 回答