Динамическое связывание WPF - PullRequest
3 голосов
/ 02 ноября 2009

Сегодня я работал над 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 ]

5 голосов
/ 03 ноября 2009

Суть проблемы в том, что вам нужен не только список описаний полей и имен, но и реальный объект, который имеет эти поля и имена.

Вы можете использовать конвертер, который добавляет целевые ссылки к объектам поля и предоставляет средство доступа к значениям, например:

  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 голос
/ 02 ноября 2009

Ну, это должно быть просто:

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

Однако, я думаю, я понимаю, что вы пытаетесь сделать. Проблема заключается в том, что KeyValuePair не является подтипом INotifyPropertyChange (справедливо, не будем вдаваться в это), поэтому вы никогда не получите уведомление от него, если значение будет изменено в словаре. Кроме того, KeyValuePair на самом деле является структурой. Таким образом, изменение значения в связанной копии не приведет к обновлению фактического источника данных, поскольку это копия данных.

Если модель, с которой вы работаете, в действительности является KeyValuePair, вам потребуется создать более конкретный класс View-Model, чтобы включить этот сценарий привязки данных. Это должен быть какой-то класс, который оборачивает ключ и имеет ссылку на базовый источник (возможно, Словарь?) И фактически выполняет вызовы для обновления значения базового источника при изменении его свойства. Тем не менее, вы все равно не будете получать уведомления из словаря (опять же, если предположить, что это ваш источник), потому что он не генерирует уведомления, поэтому вы не сможете предоставлять уведомления об изменениях вперед.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...