WPF 动态绑定到 属性

WPF dynamic binding to property

我有一个应该显示对象某些属性值的 ItemsControl。

ItemsControl 的 ItemsSource 是一个具有两个属性的对象:InstancePropertyName.

我想做的是显示 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

  1. 添加了一个 PropertyValue 属性
  2. 监听 Instance 上的 PropertyChanged 事件,当触发 PropertyName 值更改时,在 PropertyValue[ 上引发 PropertyChanged =33=]
  3. InstancePropertyName 更改时,保存对将由 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>