MultiBindings в Silverlight: присвоение множества привязок одному свойству - Создание целевых объектов для привязок (Binding)

ОГЛАВЛЕНИЕ

Создание целевых объектов для привязок (Binding)

Класс MultiBinding должен иметь свойство, которое является коллекцией типа Binding:

[ContentProperty("Bindings")]
public class MultiBinding : Panel, INotifyPropertyChanged
{
  ...
 
  /// <summary>
  /// Привязки, результаты которых будут предоставлены преобразователю.
  /// </summary>
  public ObservableCollection<Binding> Bindings { get; set; }
 
  ...
}

(Стоить отметить использование атрибута ContentProperty, означающего, что нам нет явной необходимости описывать коллекцию Binding в XAML используя синтаксис свойства элемента). Проблемой является то, что для оценки данных привязок, они должны быть привязаны целевому свойству. Мы можем добавить число свойств-пустышек к MultiBinding, PropertyOne, PropertyTwo и т.д. и привязать к ним, тем не менее данный подход слишком трудоемок и ограничен.

Наше решение заключается в том, что MultiBinding станет Panel, а это позволит ему иметь дочерние элементы, каждый из которых унаследует его DataContext. Когда класс MultiBinding будет инициализирован в момент своей привязки, будет вызван метод Initialise. Данный метод создает экземпляр BindingSlave - простой подкласс FrameworkElement с одним единым свойством Value , которое вызывает событие PropertyChanged для каждой привязки тогда, когда данное свойство будет изменено :

/// <summary>
/// Создает BindingSlave для каждого Binding и соответственно привязывает Value.
/// </summary>
internal void Initialise()
{
  foreach (Binding binding in Bindings)
  {
    BindingSlave slave = new BindingSlave();
    slave.SetBinding(BindingSlave.ValueProperty, binding);
    slave.PropertyChanged += new PropertyChangedEventHandler(Slave_PropertyChanged);
    Children.Add(slave);
  }            
}

Когда будет изменено подчиненное свойство, обработчик события MultiBinding получает текущие значения привязки и использует их для повторной оценки преобразователя:

/// <summary>
/// Вызывается тогда, когда изменяется какое-либо из свойств Value, принадлежащих BindingSlave.
/// </summary>
private void Slave_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  List<object> values = new List<object>();
  foreach (BindingSlave slave in Children)
  {
    values.Add(slave.Value);
  }
  ConvertedValue = Converter.Convert(values.ToArray(), typeof(object), ConverterParameter,
    CultureInfo.CurrentCulture);
}

Свойство ConvertedValue привязано к целевому свойству целевого объекта, и будет обновлено для отражения изменений.

Данный метод очень прост по сравнению с другими. Экземпляры MultiBinding и BindingSlave могут быть представлены в виде виртуальных ветвей нашего визуального дерева:


Загрузка исходного кода

Вы можете загрузить исходный код по данной ссылке.

Несколько слов о MVVM

Шаблон MVVM очень популярен в разработке приложений Silverlight и WPF. При наличии данного шаблона DataContext вашего представления привязан к вашей модели представления. С данным шаблоном отпадает необходимость в множественной привязки. В нашем примере класс PersonViewModel просто раскрывает свойство Title , а оно выполняет ту же функциональность, что и TitleConverter. Делает ли этот шаблон нашу статью бесполезной?

Нам так не кажется. Хотя MVVM является хорошим шаблоном, существуют случаи, когда добавление другого слоя в ваше приложение может быть нежелательным, особенно в случае, если вашей исходной целью является простота. Более того, не всем нравится, когда их заставляют использовать определенные шаблоны просто потому, что структуре не хватает функциональности. Шаблон MVVM хорош для привязки настраиваемых приложений и позволяет проводить тестирование единиц графического интерфейса, тем не менее, если вам это не нужно, то вам не стоит использовать MVVM.

Автор: Colin Eberhardt

Загрузить исходный код проекта