WPF动态绑定

时间:2009-11-02 18:24:15

标签: c# wpf data-binding

今天,我正在研究一个WPF UserControl来显示一些变量的当前值。我想知道是否有办法在WPF中创建一个超级简单的属性网格。问题出在下面的XAML的主角上。 如何将字符串绑定到具有ItemTemplate的属性,就像我在下面设置的那样?为了更清楚,我可以将绑定嵌入到彼此{Binding Path={Binding Value}}中。

这是班级:

public class Food
{
    public string Apple { get; set; }
    public string Orange { get; set; }
    public IEnumerable<KeyValuePair<string, string>> Fields
    {
        get
        {
            yield return new KeyValuePair<string, string>("Apple Label", "Apple");
            yield return new KeyValuePair<string, string>("Orange Label", "Orange");
        }
    }
}

这是XAML:

<UserControl x:Class="MAAD.Plugins.FRACTIL.Simulation.SimulationStateView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="331" Width="553">
 <ListView ItemSource="{Binding Fields}">
  <ListView.ItemTemplate>
   <DataTemplate>
    <StackPanel Orientation="Horizontal">
     <TextBlock Text="{Binding Key}" />
     **<TextBlock Text="{Binding Path={Binding Value}}" />**
    </StackPanel>
   </DataTemplate>
  </ListView.ItemTemplate>
 </ListView>
</UserControl>

2 个答案:

答案 0 :(得分:5)

问题的实质是你不仅需要字段描述和名称列表,还需要具有这些字段和名称的实际对象。

你可以使用像这样的转换器添加对象字段对象的目标引用,并提供一个值访问器,如下所示:

  public class PropertyValueAccessConverter : IMultiValueConverter
  {
    object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
      var target = values[0];
      var fieldList = values[1] as IEnumerable<KeyValuePair<string,string>>;
      return
        from pair in fieldList
        select new PropertyAccessor
        {
          Name = pair.Name,
          Target = target,
          Value = target.GetType().GetProperty(target.Value),
        };
    }

    object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
      throw new InvalidOperationException();
    }

    public class PropertyAccessor
    {
      public string Name
      public object Target;
      public PropertyInfo Property;

      public object Value
      {
        get { return Property.GetValue(Target, null); }
        set { Property.SetValue(Target, value, null); }
      }
    }

    public static PropertyValueAccessConverter Instance = new PropertyValueAccessConverter();
  }

使用此转换器,您可以像这样绑定ItemsSource:

  <ListView>
    <ListView.ItemsSource>
      <MultiBinding Converter="{x:Static local:PropertyValueAccessConverter.Instance}">
        <Binding />
        <Binding Path="Fields" />
      </MultiBinding>
    </ListView.ItemsSource>
  </ListView>

顺便说一下,实现Fields属性的一种更有效的方法是:

  public IEnumerable<KeyValuePair<string, string>> Fields
  {
    get
    {
      return new KeyValuePair<string, string>[]
      {
        new KeyValuePair<string, string>("Apple Label", "Apple");
        new KeyValuePair<string, string>("Orange Label", "Orange");
      }
    }
  }

虽然坦率地说我会在各个属性上使用描述属性以及反射而不是对列表进行硬编码。这也将首先消除对MultiBinding的需求。

答案 1 :(得分:1)

嗯,它应该是:

<TextBlock Text="{Binding Path=Value}" />

但是,我想我明白你要做什么。问题是KeyValuePair不是一个INotifyPropertyChange子类型(理所当然,不会进入那个)所以如果在字典中更改了值,你将永远不会收到通知。此外,KeyValuePair实际上是一个结构。因此,更改绑定副本上的值不会更新实际数据源,因为它是数据的副本。

如果您实际使用的模型是KeyValuePair,则需要创建更具体的View-Model类来启用此数据绑定方案。这需要是一种包装Key的类,并且对底层源(可能是Dictionary?)有一个引用,并实际调用在其属性发生更改时更新底层源的值。也就是说,您仍然不会从字典中获取通知(再次,假设这是您的来源),因为它不会触发,因此您将无法提供转发通知。