WPF 动态绑定到属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38182945/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF dynamic binding to property
提问by Formentz
I have an ItemsControl that should display the values of some properties of an object.
我有一个 ItemsControl 应该显示对象的某些属性的值。
The ItemsSource of the ItemsControl is an object with two properties: Instanceand PropertyName.
ItemsControl 的 ItemsSource 是一个具有两个属性的对象:Instance和PropertyName。
What I am trying to do is displaying all the property values of the Instanceobject, but I do not find a way to set the Path of the binding to the PropertyNamevalue:
我想要做的是显示Instance对象的所有属性值,但我没有找到将绑定的 Path 设置为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>
the question marks are the points where I don't know how to create the binding.
问号是我不知道如何创建绑定的点。
I initially tried with a MultiValueConverter:
我最初尝试使用 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>
The MultiValueConverter uses Reflection to look through the Instanceand returns the value of the property.
MultiValueConverter 使用反射来查看实例并返回属性的值。
But if the property value changes, this change is not notified and the displayed value remains unchanged.
但如果属性值发生变化,则不会通知此更改,并且显示值保持不变。
I am looking for a way to do it with XAML only, if possible, if not I will have to write a wrapper class to for the items of the ItemsSource collection, and I know how to do it, but, since it will be a recurring task in my project, it will be quite expensive.
我正在寻找一种仅使用 XAML 的方法,如果可能的话,如果不是,我将不得不为 ItemsSource 集合的项目编写一个包装类,并且我知道该怎么做,但是,因为它将是一个在我的项目中重复执行任务,这将非常昂贵。
Edit:
编辑:
For those who asked, InstancePropertiesis a property on the ViewModel which exposes a collection of objects like this:
对于那些问过的人,InstanceProperties是 ViewModel 上的一个属性,它公开了这样的对象集合:
public class InstanceProperty : INotifyPropertyChanged
{
//[.... INotifyPropertyChanged implementation ....]
public INotifyPropertyChanged Instance { get; set; }
public string PropertyName { get; set; }
}
Obviously the two properties notify theirs value is changing through INotifyPropertyChanged, I don't include the OnPropertyChanged event handling for simplicity.
显然这两个属性通过 INotifyPropertyChanged 通知它们的值正在改变,为了简单起见,我不包括 OnPropertyChanged 事件处理。
The collection is populated with a limited set of properties which I must present to the user, and I can't use a PropertyGrid because I need to filter the properties that I have to show, and these properties must be presented in a graphically richer way.
该集合填充了一组有限的属性,我必须将这些属性呈现给用户,我不能使用 PropertyGrid,因为我需要过滤必须显示的属性,并且这些属性必须以更丰富的图形方式呈现.
Thanks
谢谢
采纳答案by Formentz
Ok, thanks to @GazTheDestroyer comment:
好的,感谢@GazTheDestroyer 评论:
@GazTheDestroyerwrote: 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
@GazTheDestroyer写道:我想不出任何方法仅在 XAML 中动态迭代和绑定到任意对象的属性。您需要编写一个 VM 或行为来执行此操作,以便您可以监视更改通知,但是使用反射以通用方式执行此操作,您可以在整个项目中重复使用它
I found a solution: editing the ViewModel class InstancePropertylike this
我找到了一个解决方案:像这样编辑 ViewModel 类InstanceProperty
- added a PropertyValueproperty
- listen to PropertyChanged event on Instanceand when the PropertyNamevalue changed is fired, raise PropertyChanged on PropertyValue
- When Instanceor PropertyNamechanges, save a reference to Reflection's PropertyInfo that will be used by PropertyValueto read the value
- 添加了一个PropertyValue属性
- 侦听Instance上的 PropertyChanged 事件,当PropertyName值更改时触发,在PropertyValue上引发PropertyChanged
- 当实例或属性名,将被用来修改,保存到反思的的PropertyInfo参考的PropertyValue读取值
here is the new, complete, ViewModel class:
这是新的、完整的 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
}
and here is the XAML:
这是 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>

