WPF 动态绑定到 属性
WPF dynamic binding to property
我有一个应该显示对象某些属性值的 ItemsControl。
ItemsControl 的 ItemsSource 是一个具有两个属性的对象:Instance 和 PropertyName.
我想做的是显示 Instance 对象的所有 属性 值,但我找不到设置绑定路径的方法到 PropertyName 值:
<ItemsControl ItemsSource={Binding Path=InstanceProperties}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=PropertyName, Mode=OneWay}"/>
<TextBlock Text=": "/>
<TextBlock Text="{Binding Source=??{Binding Path=Instance}??, Path=??PropertyName??, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
问号是我不知道如何创建绑定的地方。
我最初尝试使用 MultiValueConverter:
<TextBlock Grid.Column="1" Text="{Binding}">
<TextBlock.DataContext>
<MultiBinding Converter="{StaticResource getPropertyValue}">
<Binding Path="Instance" Mode="OneWay"/>
<Binding Path="PropertyName" Mode="OneWay"/>
</MultiBinding>
</TextBlock.DataContext>
</TextBlock>
MultiValueConverter 使用反射来查看 Instance 和 returns 属性.
的值
但如果属性值发生变化,则不会通知此变化,显示值保持不变。
我正在寻找一种仅使用 XAML 的方法,如果可能的话,我将不得不为 ItemsSource 集合的项目编写一个包装器 class,并且我知道怎么做,但是,因为这将是我项目中的一个重复任务,所以它会非常昂贵。
编辑:
对于那些提问的人,InstanceProperties 是 ViewModel 上的一个 属性,它公开了如下对象的集合:
public class InstanceProperty : INotifyPropertyChanged
{
//[.... INotifyPropertyChanged implementation ....]
public INotifyPropertyChanged Instance { get; set; }
public string PropertyName { get; set; }
}
显然,这两个属性通过 INotifyPropertyChanged 通知它们的值正在发生变化,为简单起见,我不包括 OnPropertyChanged 事件处理。
集合中填充了一组有限的属性,我必须向用户展示这些属性,我不能使用 PropertyGrid,因为我需要过滤我必须展示的属性,并且必须展示这些属性以图形更丰富的方式。
谢谢
好的,感谢@GazTheDestroyer 评论:
@GazTheDestroyer wrote: I cannot think of any way to dynamically iterate and bind to an arbitrary object's properties in XAML only. You need to write a VM or behaviour to do this so you can watch for change notifications, but do it in a generic way using reflection you can just reuse it throughout your project
我找到了一个解决方案:像这样编辑 ViewModel class InstanceProperty
- 添加了一个 PropertyValue 属性
- 监听 Instance 上的 PropertyChanged 事件,当触发 PropertyName 值更改时,在 PropertyValue[ 上引发 PropertyChanged =33=]
- 当 Instance 或 PropertyName 更改时,保存对将由 PropertyValue[= 使用的 Reflection 的 PropertyInfo 的引用33=] 读取值
这是新的、完整的 ViewModel class:
public class InstanceProperty : INotifyPropertyChanged
{
#region Properties and events
public event PropertyChangedEventHandler PropertyChanged;
private INotifyPropertyChanged FInstance = null;
public INotifyPropertyChanged Instance
{
get { return this.FInstance; }
set
{
if (this.FInstance != null) this.FInstance.PropertyChanged -= Instance_PropertyChanged;
this.FInstance = value;
if (this.FInstance != null) this.FInstance.PropertyChanged += Instance_PropertyChanged;
this.CheckProperty();
}
}
private string FPropertyName = null;
public string PropertyName
{
get { return this.FPropertyName; }
set
{
this.FPropertyName = value;
this.CheckProperty();
}
}
private System.Reflection.PropertyInfo Property = null;
public object PropertyValue
{
get { return this.Property?.GetValue(this.Instance, null); }
}
#endregion
#region Private methods
private void CheckProperty()
{
if (this.Instance == null || string.IsNullOrEmpty(this.PropertyName))
{
this.Property = null;
}
else
{
this.Property = this.Instance.GetType().GetProperty(this.PropertyName);
}
this.RaisePropertyChanged(nameof(PropertyValue));
}
private void Instance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == this.PropertyName)
{
this.RaisePropertyChanged(nameof(PropertyValue));
}
}
private void RaisePropertyChanged(string propertyname)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
#endregion
}
这里是 XAML:
<ItemsControl ItemsSource={Binding Path=InstanceProperties}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=PropertyName, Mode=OneWay}"/>
<TextBlock Text=": "/>
<TextBlock Text="{Binding Path=PropertyValue, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
我有一个应该显示对象某些属性值的 ItemsControl。
ItemsControl 的 ItemsSource 是一个具有两个属性的对象:Instance 和 PropertyName.
我想做的是显示 Instance 对象的所有 属性 值,但我找不到设置绑定路径的方法到 PropertyName 值:
<ItemsControl ItemsSource={Binding Path=InstanceProperties}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=PropertyName, Mode=OneWay}"/>
<TextBlock Text=": "/>
<TextBlock Text="{Binding Source=??{Binding Path=Instance}??, Path=??PropertyName??, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
问号是我不知道如何创建绑定的地方。
我最初尝试使用 MultiValueConverter:
<TextBlock Grid.Column="1" Text="{Binding}">
<TextBlock.DataContext>
<MultiBinding Converter="{StaticResource getPropertyValue}">
<Binding Path="Instance" Mode="OneWay"/>
<Binding Path="PropertyName" Mode="OneWay"/>
</MultiBinding>
</TextBlock.DataContext>
</TextBlock>
MultiValueConverter 使用反射来查看 Instance 和 returns 属性.
的值但如果属性值发生变化,则不会通知此变化,显示值保持不变。
我正在寻找一种仅使用 XAML 的方法,如果可能的话,我将不得不为 ItemsSource 集合的项目编写一个包装器 class,并且我知道怎么做,但是,因为这将是我项目中的一个重复任务,所以它会非常昂贵。
编辑:
对于那些提问的人,InstanceProperties 是 ViewModel 上的一个 属性,它公开了如下对象的集合:
public class InstanceProperty : INotifyPropertyChanged
{
//[.... INotifyPropertyChanged implementation ....]
public INotifyPropertyChanged Instance { get; set; }
public string PropertyName { get; set; }
}
显然,这两个属性通过 INotifyPropertyChanged 通知它们的值正在发生变化,为简单起见,我不包括 OnPropertyChanged 事件处理。
集合中填充了一组有限的属性,我必须向用户展示这些属性,我不能使用 PropertyGrid,因为我需要过滤我必须展示的属性,并且必须展示这些属性以图形更丰富的方式。
谢谢
好的,感谢@GazTheDestroyer 评论:
@GazTheDestroyer wrote: I cannot think of any way to dynamically iterate and bind to an arbitrary object's properties in XAML only. You need to write a VM or behaviour to do this so you can watch for change notifications, but do it in a generic way using reflection you can just reuse it throughout your project
我找到了一个解决方案:像这样编辑 ViewModel class InstanceProperty
- 添加了一个 PropertyValue 属性
- 监听 Instance 上的 PropertyChanged 事件,当触发 PropertyName 值更改时,在 PropertyValue[ 上引发 PropertyChanged =33=]
- 当 Instance 或 PropertyName 更改时,保存对将由 PropertyValue[= 使用的 Reflection 的 PropertyInfo 的引用33=] 读取值
这是新的、完整的 ViewModel class:
public class InstanceProperty : INotifyPropertyChanged
{
#region Properties and events
public event PropertyChangedEventHandler PropertyChanged;
private INotifyPropertyChanged FInstance = null;
public INotifyPropertyChanged Instance
{
get { return this.FInstance; }
set
{
if (this.FInstance != null) this.FInstance.PropertyChanged -= Instance_PropertyChanged;
this.FInstance = value;
if (this.FInstance != null) this.FInstance.PropertyChanged += Instance_PropertyChanged;
this.CheckProperty();
}
}
private string FPropertyName = null;
public string PropertyName
{
get { return this.FPropertyName; }
set
{
this.FPropertyName = value;
this.CheckProperty();
}
}
private System.Reflection.PropertyInfo Property = null;
public object PropertyValue
{
get { return this.Property?.GetValue(this.Instance, null); }
}
#endregion
#region Private methods
private void CheckProperty()
{
if (this.Instance == null || string.IsNullOrEmpty(this.PropertyName))
{
this.Property = null;
}
else
{
this.Property = this.Instance.GetType().GetProperty(this.PropertyName);
}
this.RaisePropertyChanged(nameof(PropertyValue));
}
private void Instance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == this.PropertyName)
{
this.RaisePropertyChanged(nameof(PropertyValue));
}
}
private void RaisePropertyChanged(string propertyname)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
#endregion
}
这里是 XAML:
<ItemsControl ItemsSource={Binding Path=InstanceProperties}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=PropertyName, Mode=OneWay}"/>
<TextBlock Text=": "/>
<TextBlock Text="{Binding Path=PropertyValue, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>