代码之家  ›  专栏  ›  技术社区  ›  Jake Pearson

WPF动态绑定

  •  3
  • Jake Pearson  · 技术社区  · 15 年前

    今天,我正在研究一个WPF用户控件来显示一些变量的当前值。我想知道是否有一种方法可以在WPF中创建一个超级简单的属性网格。问题在下面XAML的星号行中。 如何将字符串绑定到具有下面设置的项模板的属性? 更清楚地说,我可以在彼此内部嵌入绑定吗 {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 回复  |  直到 15 年前
        1
  •  5
  •   Ray Burns    15 年前

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

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

      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();
      }
    

    使用此转换器,您可以这样绑定项源:

      <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");
          }
        }
      }
    

    尽管坦率地说,我会在单个属性上使用描述属性和反射,而不是对列表进行硬编码。这也将首先消除对多重结合的需要。

        2
  •  1
  •   Drew Marsh    15 年前

    嗯,应该是:

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

    不过,我想我明白你想做什么。问题是keyValuePair不是一个inotifyPropertyChange子类型(正确地说,这样就不会涉及到该子类型),因此,如果在字典中更改该值,则永远不会收到来自该子类型的通知。此外,keyValuePair实际上是一个结构。因此,更改绑定副本的值不会更新实际的数据源,因为它是数据的副本。

    如果您使用的模型实际上是keyValuePair,那么您需要创建一个更具体的视图模型类来启用这个数据绑定场景。这需要是一种包装键并引用基础源(可能是字典?)的类。当基础源的属性发生更改时,实际上调用更新其值。也就是说,您仍然不会从字典中收到通知(同样,假设这是您的源),因为它不会触发任何通知,所以您将无法提供转发更改通知。