我正在实现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”时所期望的。真正按名称绑定到每个属性。我不能做后者,因为我直到运行时才知道属性名称。
因此,我假设我需要在代码后面动态地进行绑定,但我无法弄清楚如何。