C# 如何在设置DataContext后向ICustomTypeDescriptor添加其他PropertyDescriptor
Qeustion: 在设置了C# 如何在设置DataContext后向ICustomTypeDescriptor添加其他PropertyDescriptor,c#,wpf,xaml,data-binding,C#,Wpf,Xaml,Data Binding,Qeustion: 在设置了DataConext之后,是否可以在ICustomTypeDescriptor的PropertyDescriptorCollection中添加额外的PropertyDescriptor 详细信息: 我正在创建一个新类,该类实现了ICustomTypeDescriptor,目标是在运行时向该类动态添加新属性,并将它们绑定到WPF应用程序 例如,用户可以在应用程序运行时通过脚本语言(如Lua)定义新属性,我希望XAML能够绑定到该属性 我遇到的问题是,如果我在设置Data
DataConext
之后,是否可以在ICustomTypeDescriptor
的PropertyDescriptorCollection
中添加额外的PropertyDescriptor
详细信息:
我正在创建一个新类,该类实现了ICustomTypeDescriptor
,目标是在运行时向该类动态添加新属性,并将它们绑定到WPF应用程序
例如,用户可以在应用程序运行时通过脚本语言(如Lua)定义新属性,我希望XAML能够绑定到该属性
我遇到的问题是,如果我在设置DataContext之后添加属性,那么到XAML的绑定将无法工作;它不会尝试调用关联的PropertyDescriptor.GetValue
方法
我认为这个问题是,在将DataContext设置为我的ICustomTypeDescriptor
后不久,它的ICustomTypeDescriptor.GetProperties
的实现被调用,此时我为所有已知属性创建了PropertyDescriptor
。随后我添加了新属性,但无法为它们添加新的PropertyDescriptor
代码:
我在这里对代码进行了一些精简,以使其更易于阅读,删除了许多锅炉板的内容
本例中的想法是,第一个文本字段绑定到一个动态创建的属性,该属性是在构造期间定义的,第二个文本字段绑定到一个在单击按钮之前不存在的属性
单击按钮时创建的属性只有在我将DataContext设置为新内容时才能正确绑定数据
public class MyCustomType : ICustomTypeDescriptor, INotifyPropertyChanged
{
Dictionary<string, object> Properties = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
public MyCustomType()
{
Properties.Add("CustomProp", "What up, world?");
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public void CreateOrSetProperty(string name, object val)
{
Properties[name] = val;
NotifyPropertyChanged(name);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
List<PropertyDescriptor> props = new List<PropertyDescriptor>();
foreach (KeyValuePair<string, object> entry in Properties)
{
props.Add(new MyCustomTypePropertyDescriptor(entry.Key, attributes));
}
return new PropertyDescriptorCollection(props.ToArray());
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
class MyCustomTypePropertyDescriptor : PropertyDescriptor
{
public MyCustomTypePropertyDescriptor(string name, Attribute[] attrs)
: base(name, attrs)
{
}
public override object GetValue(object component)
{
return ((MyCustomType)component).Properties[Name];
}
public override bool IsReadOnly
{
get { return false; }
}
public override void SetValue(object component, object value)
{
((MyCustomType)component).Properties[Name] = value;
}
}
}
public partial class Page5
{
public Page5()
{
this.InitializeComponent();
}
private void BtnAddProperty_Click(object sender, System.Windows.RoutedEventArgs e)
{
// A static resource defined in XAML used as the DataContext for both text blocks.
MyCustomType c = Resources["MyCustomTypeData"] as MyCustomType;
// Create a new property which is already being referenced in a DataBind in XAML but
// prior to this function being called, did not exist.
c.CreateOrSetProperty("AnotherProp", "Another Property!");
// If I clear the DataContext and set it back to the previous value it triggers
// ICustomTypeDescriptor.GetProperties to get called again, and the property gets
// Bound. If I don't do this, it fails.
//MyTextBlock2.DataContext = null;
//MyTextBlock2.DataContext = c;
}
}
公共类MyCustomType:ICustomTypeDescriptor,INotifyPropertyChanged
{
字典属性=新字典();
公共事件属性更改事件处理程序属性更改;
公共MyCustomType()
{
添加(“CustomProp”、“What up,world?”);
}
私有void NotifyPropertyChanged(字符串信息)
{
if(PropertyChanged!=null)
{
PropertyChanged(此,新PropertyChangedEventArgs(信息));
}
}
public void CreateOrSetProperty(字符串名称,对象值)
{
属性[名称]=val;
NotifyPropertyChanged(名称);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(属性[]属性)
{
列表道具=新列表();
foreach(属性中的KeyValuePair条目)
{
添加(新的MyCustomTypePropertyDescriptor(entry.Key,attributes));
}
返回新的PropertyDescriptorCollection(props.ToArray());
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
返回((ICustomTypeDescriptor)this.GetProperties(null);
}
类MyCustomTypePropertyDescriptor:PropertyDescriptor
{
公共MyCustomTypePropertyDescriptor(字符串名称,属性[]属性)
:base(名称、属性)
{
}
公共覆盖对象GetValue(对象组件)
{
返回((MyCustomType)组件)。属性[名称];
}
公共覆盖布尔为只读
{
获取{return false;}
}
公共覆盖无效设置值(对象组件、对象值)
{
((MyCustomType)组件)。属性[名称]=值;
}
}
}
公共部分类第5页
{
公共网页5(
{
this.InitializeComponent();
}
私有无效BtnAddProperty_单击(对象发送者,System.Windows.RoutedEventArgs e)
{
//XAML中定义的静态资源,用作两个文本块的DataContext。
MyCustomType c=资源[“MyCustomTypeData”]作为MyCustomType;
//创建已在XAML中的数据绑定中引用的新属性,但
//在调用此函数之前,不存在。
c、 CreateOrSetProperty(“另一个属性”,“另一个属性!”);
//如果我清除DataContext并将其设置回它触发的前一个值
//ICustomTypeDescriptor.GetProperties再次调用,并且该属性
//当然。如果我不这样做,它就失败了。
//MyTextBlock2.DataContext=null;
//MyTextBlock2.DataContext=c;
}
}
我不完全理解你的问题。然而,我想在绑定表达式中使用字典可能是您的问题的一部分。在我看来,将在xaml中绑定的集合应该是从ObservableCollection派生的类型,以便拥有在集合更改时通知视图的能力。我正在通过ICustomTypeDescriptor的实例进行绑定,并编写自定义PropertyDescriptor。我认为这本词典应该是无关紧要的。这正是我选择存储属性数据的方式。
<Page.Resources>
<WpfApplication1:MyCustomType x:Key="MyCustomTypeData"/>
</Page.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBlock DataContext="{StaticResource MyCustomTypeData}" x:Name="MyTextBlock" HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding CustomProp}" VerticalAlignment="Top" FontSize="32" Background="Black" Foreground="White"/>
<TextBlock DataContext="{StaticResource MyCustomTypeData}" x:Name="MyTextBlock2" HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding AnotherProp}" VerticalAlignment="Top" FontSize="32" Background="Black" Foreground="White"/>
<Button x:Name="BtnAddProperty" Content="Add Additional Property" Click="BtnAddProperty_Click"/>
</StackPanel>
</Grid>