MultiBindings в Silverlight: присвоение множества привязок одному свойству - Получение DataContext

ОГЛАВЛЕНИЕ

Получение DataContext

Обычно, при определении привязки мы опускаем свойство Source, то есть {Binding Path=Forename}. Когда Binding, которое данное свойство представляет, ассоциируется с элементом, источником привязки будет (наверняка унаследован) DataContext целевого элемента. Потому для того, чтобы позволить осуществлять привязку по классу MultiBinding, он должен быть FrameworkElement, что дает нам свойство DataContext и метод SetBinding().

Тем не менее, есть одна проблема - DataContext каждого элемента наследуется от родителя в пределах визуального дерева. Наше MultiBinding не находится в пределах визуального дерева и мы не хотим, чтобы оно там было, поэтому оно не будет участвовать в наследовании DataContext. Нам необходимо обеспечить то, что когда DataContext будет изменяться по элементу, который имеет MultiBinding , связанный с ним, то мы ‘передаем’ данный DataContext к MultiBinding. С WPF это выполнить довольно легко, FrameWorkElement раскрывает событие DataContextChanged, (для зависимостей, которые не раскрывают события, всегда существует DependencyPropertyDescriptor). Тем не менее, с Silverlight нам недоступна ни одна из этих опций.

Наше решение заключается в том, чтобы создать новое привязанное свойство и привязать его к целевому (в данном случае это наш TextBlock), что потянет за собой DataContext. Следующий код скопирован из класса BindingUtil - когда класс MultiBinding ассоциируется с целевым элементом в качестве привязанного свойства, мы также привяжем его привязанное свойство DataContextPiggyBack. Мы определяем статический ( static ) метод, которые вызывается тогда, когда DatatContext целевого объекта изменяется, и мы продвигаем новый DataContext к классу MultiBinding.

/// <summary>
/// Вызывается тогда, когда свойство MultiBinding устанавливается в элементе структуры
/// </summary>
private static void OnMultiBindingChanged(DependencyObject depObj,
  DependencyPropertyChangedEventArgs e)
{
  FrameworkElement targetElement = depObj as FrameworkElement;
 
  // Привязка целевых  элементов DataContext, к нашему свойству DataContextPiggyBack
  // Это повзоляет нам получить события изменения свойства тогда, когда изменяется targetElement DataContext
  targetElement.SetBinding(BindingUtil.DataContextPiggyBackProperty, new Binding());
}
 
public static readonly DependencyProperty DataContextPiggyBackProperty =
    DependencyProperty.RegisterAttached("DataContextPiggyBack",
        typeof(object), typeof(BindingUtil), new PropertyMetadata(null,
              new PropertyChangedCallback(OnDataContextPiggyBackChanged)));
 
public static object GetDataContextPiggyBack(DependencyObject d)
{
  return (object)d.GetValue(DataContextPiggyBackProperty);
}
 
public static void SetDataContextPiggyBack(DependencyObject d, object value)
{
  d.SetValue(DataContextPiggyBackProperty, value);
}
 
/// <summary>
/// Обрабатывает изменения свойства DataContextPiggyBack.
/// </summary>
private static void OnDataContextPiggyBackChanged(DependencyObject d,
                                                  DependencyPropertyChangedEventArgs e)
{
  FrameworkElement targetElement = d as FrameworkElement;
 
  // когда изменяется targeElement DataContext мы копируем значение обновленного свойства в наш MultiBinding.
  MultiBinding relay = GetMultiBinding(targetElement);
  relay.DataContext = targetElement.DataContext;
}