将ICustomTypeDescriptor与ItemsControl一起使用

时间:2014-07-28 19:55:47

标签: wpf binding itemscontrol icustomtypedescriptor

我正在实现ICustomTypeDescriptor,以便我可以在运行时创建具有动态属性的类型,但是,而不是将ICustomTypeDescriptor与大多数人似乎使用它的DataGrid一起使用,我想将它与ItemsControl一起使用

以下是该应用程序的内容。 (对不起,这是很多代码)

public class BindableTypeDescriptor : INotifyPropertyChanged, ICustomTypeDescriptor
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged(string _propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(_propertyName));
        }
    }

    class BindablePropertyDescriptor : PropertyDescriptor
    {
        public override bool IsReadOnly { get { return false; } }
        public override Type PropertyType { get { return m_type; } }
        public override Type ComponentType { get { return null; } }

        public BindablePropertyDescriptor(BindableTypeDescriptor _owner, Type _type, string _name)
            : base(_name, null)
        {
            m_owner = _owner;
            m_type = _type;
            m_name = _name;
        }

        public override void SetValue(object component, object _value)
        {
            m_owner[m_name] = _value;
        }

        public override object GetValue(object component)
        {
            return m_owner[m_name];
        }

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

        public override void ResetValue(object component)
        {

        }

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

        BindableTypeDescriptor m_owner;
        Type m_type;
        string m_name;
    }

    public IReadOnlyDictionary<string, object> Properties { get { return m_properties; } }

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

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

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

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

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

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

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

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

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

    public PropertyDescriptor GetDefaultProperty()
    {
        return null;
    }

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

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return new PropertyDescriptorCollection(
            m_properties.Select(e => new BindablePropertyDescriptor(this, e.Value != null ? e.Value.GetType() : typeof(object), e.Key))
            .ToArray());
    }

    public object this[string _name]
    {
        get { return m_properties[_name]; }
        set 
        {
            m_properties[_name] = value;
            NotifyPropertyChanged(_name);
        }
    }

    Dictionary<string, object> m_properties = new Dictionary<string, object>();
}

    public class Element 
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged(string _propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(_propertyName));
        }
    }
}

public class StringElement : Element
{
    string m_value;
    public string Value
    {
        get { return m_value; }
        set
        {
            m_value = value;
            NotifyPropertyChanged("Value");
        }
    }
}

public class NumberElement : Element
{
    int m_value;
    public int Value
    {
        get { return m_value; }
        set
        {
            m_value = value;
            NotifyPropertyChanged("Value");
        }
    }
}

public partial class MainWindow : Window
{
    public BindableTypeDescriptor CustomType { get; private set; }

    public MainWindow()
    {
        CustomType = new BindableTypeDescriptor();
        CustomType["Name"] = new StringElement() { Value = "Dave" };
        CustomType["Age"] = new NumberElement() { Value = 5 };

        DataContext = this;
        InitializeComponent();
    }

    protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
    {
        base.OnMouseDoubleClick(e);
        CustomType["Name"] = new StringElement() { Value = "Fred" };
        CustomType["Age"] = new NumberElement() { Value = 6 };
    }
}

XAML:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:WpfApplication1">
<ItemsControl ItemsSource="{Binding CustomType.Properties}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <Label Content="{Binding Key}"/>
                <ContentPresenter Content="{Binding Value}" Grid.Column="1">
                    <ContentPresenter.Resources>
                        <DataTemplate DataType="{x:Type l:StringElement}">
                            <TextBox Text="{Binding Value}"/>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type l:NumberElement}">
                            <Slider Value="{Binding Value}" Minimum="0" Maximum="50"/>
                        </DataTemplate>
                    </ContentPresenter.Resources>
                </ContentPresenter>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

问题是,通过上面的实现,当我更改属性值时,ItemsControl不会更新(请参阅OnMouseDoubleClick),这是我应该在我应该绑定到初始“CustomType.Properties”时所期望的。真正按名称绑定到每个属性。我不能做后者,因为我直到运行时才知道属性名称。

因此,我假设我需要在代码后面动态地进行绑定,但我无法弄清楚如何。

0 个答案:

没有答案