19

ICustomTypeDescriptorオブジェクトに動的プロパティを作成するために、インターフェイスとPropertyDescriptorクラスを試しています。単純なオブジェクトで多くの成功を収めていますが、ネストされたオブジェクトを取得して動的プロパティを作成することはできませんか?

たとえば、以下のデータ バインディング ダイアログでは、 Person クラスを として追加し、 をテストボックス StaticResourceにデータ バインドしようとしています。Person.Child.Name

Person.Child動的に作成されたプロパティ (Nameおよび) が表示されることを期待していますが、ご覧Ageのとおり、期待どおりに機能していませんか? あたかもデータバインディング ダイアログICustomTypeDescriptorPerson.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