我有一个 ItemsControl,它应该显示对象的某些属性的值。
ItemsControl 的 ItemsSource 是一个具有两个属性的对象:Instance 和 PropertyName。
我想要做的是显示Instance对象的所有属性值,但我找不到将绑定(bind)的路径设置为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>
问号是我不知道如何创建绑定(bind)的地方。
我最初尝试使用 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 使用反射来查看实例并返回属性的值。
但如果属性值发生变化,则不会通知此变化,并且显示的值保持不变。
我正在寻找一种仅使用 XAML 来完成此操作的方法,如果可能的话,如果没有,我将不得不为 ItemsSource 集合的项目编写一个包装类,并且我知道如何做到这一点,但是,因为它将是我的项目中的一项重复任务,这将是相当昂贵的。
编辑:
对于那些询问的人,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 类InstanceProperty
- 添加了 PropertyValue 属性
- 监听Instance上的PropertyChanged事件,并在PropertyName值发生更改时触发,引发PropertyValue上的PropertyChanged
- 当Instance或PropertyName更改时,保存对Reflection的PropertyInfo的引用,PropertyValue将使用该引用来读取值
这是新的、完整的 ViewModel 类:
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>
关于WPF 动态绑定(bind)到属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38182945/