Подтвердить что ты не робот

"Binding Builder" не запрашивает вложенный ICustomTypeDescriptor (путь пуст)?

Я экспериментирую с интерфейсом ICustomTypeDescriptor и классом PropertyDescriptor для создания динамических свойств объектов. У меня большой успех с простыми объектами, но я не могу заставить вложенные объекты создавать свои динамические свойства?

Например, в диалоговом окне привязки данных ниже я добавляю свой класс Person как StaticResource, а затем пытаюсь привязать данные Person.Child.Name к тестовому поле:

dZGL3.png

Для Person.Child я ожидаю увидеть мои динамически созданные свойства (Name и Age), но как вы видите, он работает не так, как ожидалось? Это почти так, как если диалог привязки данных не допрашивает интерфейс 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 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 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));
        }
    }
}
4b9b3361

Ответ 1

Я думаю, что вы неправильно установили свойство ComponentType.

  • Свойство: Person.Child должен иметь параметр ComponentType для typeof (Person), а не typeof (Child). Также как свойство: Person.Name.
  • Свойство: Child.Name должен иметь тип ComponentType для typeof (Child).

ComponentType используется для определения типа владельца свойства.