Области привязки данных - нуждаются в уточнении - PullRequest
1 голос
/ 29 сентября 2010

Я выполняю некоторую работу в WPF, и у меня по большей части работают мои привязки, но здесь мне нужно уточнить область видимости. Я столкнулся с некоторыми, казалось бы, простыми операциями, которые требуют обходных путей, и я считаю, что многое из этого связано с областью действия.

Пример # 1 - вне визуального дерева, привязка к родителю.

<ComboBox x:Name="Combo1" ItemsSource="{Binding SomeListOfStrings}">
  <ComboBox.ContextMenu>
    <ContextMenu>
      <MenuItem Header="{Binding ElementName=Combo1, Path=SelectedItem}" />
      <MenuItem Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}, Path=SelectedItem}" />
    </ContextMenu>
  </ComboBox.ContextMenu>
</ComboBox>

В этом примере я пытаюсь привязать свойство дочернего элемента к свойству родителя. Так как этот элемент не находится в визуальном дереве под элементом, а как просто свойство, я не могу найти родителя с помощью FindAncestor. По моему опыту мне не повезло связывать с ElementName и в этом случае (пробовал и Name="" и x:Name="").

Какой здесь объем? Как MenuItem относится к ComboBox? Поскольку я знаю, что он наследует DataContext своего родителя, почему он недоступен с FindAncestor / ElementName?

Пример # 2 - Ресурсы + StaticResource / DynamicResource

<UserControl x:Name="MainControl" ... />
  <UserControl.Resources>
     <Style TargetType="ComboBox">
       <Setter Property="ContextMenu">
         <Setter.Value>
           <ContextMenu ItemsSource="{Binding ViewModelsMenuItems}" />
         </Setter.Value>
       </Setter>
     </Style>
     <Style TargetType="ComboBox" x:Key="Example2_Style2">
       <Setter Property="ContextMenu">
         <Setter.Value>
           <ContextMenu ItemsSource="{Binding ElementName=MainControl, Path=DataContext.ViewModelMenuItems}" />
         </Setter.Value>
       </Setter>
     </Style>
  </UserControl.Resources>
  <StackPanel>
    <ComboBox />
    <ComboBox />
    <ComboBox Style="{StaticResource Example2_Style2" />
  </StackPanel>
</UserControl>

В этом примере я пытаюсь установить контекстное меню для всех ComboBox в моем пользовательском элементе управления (или определенных, если я использовал именованный стиль). Поскольку ContextMenu определяется вне области и «устанавливается» в область, у меня раньше были проблемы с наследованием DataContext, или с возможностью использования ElementName (поскольку элемент находится вне области действия?).

Бонусный вопрос

Так как мне повезло в целом с ElementName, может кто-нибудь подскажет, пожалуйста, какой использовать, потому что я вижу оба ВСЕГО через Интернет / книги. Name="Whatever" или x:Name="Whatever"

Обновление (по запросу)

Тип ошибок привязки, которые я получаю:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ComboBox', AncestorLevel='1''. BindingExpression:Path=SelectedItem; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Header' (type 'object')

1 Ответ

2 голосов
/ 14 июня 2011

Существует нечто, называемое контекст наследования , в котором не так много информации, кроме этого сообщения в блоге ( архив ).

В этой статье в качестве примера приводится ContextMenu, в котором не происходит связывание.

Я бы, например, решил бы эту проблему, используя свойство PlacementTarget:

<ContextMenu>
  <MenuItem Header="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" />
</ContextMenu>

Оригинальная статья исчезла;Вот основные моменты из архива:

Ник в Silverlight и WPF

Что такое контекст наследования?

Ник Крамер [MSFT] 17 августа 2006 г.

Но прежде чем я расскажу вам о контекстах наследования, я должен объяснить проблему, которую он решает.Когда-то давно наследование свойств смотрело только на логическое дерево и визуальное дерево - поэтому, если у элемента не было логического или визуального родителя, его свойства не наследовали значения от родительского элемента, которого у него не было.Что имеет смысл, если вы думаете о мире с точки зрения кода.Но если вы посмотрите на мир через очки xaml, во многих местах один элемент выглядит так, как будто у него есть родитель, а на самом деле его нет.Рассмотрим следующее:

<Button>
  <Button.Background>
    <SolidColorBrush>green</SolidColorBrush>
  </Button.Background>
</Button>

Быстро, что является родителем SolidColorBrush?Если бы вы сказали Button, вы бы ошиблись - SolidColorBrush не является частью визуального дерева (это не Visual).SolidColorBrush также не является частью логического дерева, потому что если вы вызываете Button.Content, ответ не будет SolidColorBrush.Таким образом, SolidColorBrush в этом примере не имеет родителя, поэтому он не наследует значения свойств от кого-либо.

Сначала это может показаться академическим - кого волнует, наследует ли SolidColorBrush?На самом деле, есть пара причин, по которым это важно: свойство DataContext и событие Loaded.DataContext - это унаследованное свойство, которое использует источник данных по умолчанию для ваших операторов {Binding}.Когда вы пишете:

    <SolidColorBrush Color="{Binding}"/>

Поскольку вы не указали источник данных (и кто это делает), он использует свойство DataContext.И если это наследует, как вы ожидаете, все будет счастливым.Но это легко написать:

<Button DataContext="whatever">
  <Button.Background>
    <SolidColorBrush Color="{Binding}"/>
  </Button.Background>
</Button>

И запутайтесь, что ваш SolidColorBrush не наследовал DataContext.Аналогично, событие Loaded изначально было привязано к логическому дереву, поэтому, если вы поместите ваш MediaElement в VisualBrush, в визуальном дереве будет пробел, и ваши медиа никогда не получат событие Loaded и никогда не начнут воспроизводить видео.

И именно поэтому мы изобрели контекст наследования.Вы можете думать об этом как о логическом дереве 2.0 - контекст наследования является дополнительным указателем, который механизм свойств использует, когда нет логического родителя или визуального родителя для получения значений.Контекст наследования не решает все проблемы в этом пространстве, но они решают многие из них, и в будущем мы добавим больше указателей контекста наследования и решим больше проблем.

Есть ряд мест, которые мы устанавливаемУказатели контекста наследования, я не буду пытаться перечислить их все, но вот некоторые из наиболее интересных:

Возможность замораживания внутри FrameworkElement - наш образец SolidColorBrush / Button над FrameworkElement внутри триггеров и сеттеров VisualBrush

Ресурсные словари представляют еще один интересный случай.Предположим, вы используете DynamicResource внутри словаря ресурсов:

Оценивается ли этот динамический ресурс там, где был определен SolidColorBrush?Или где кисть привыкает?Если последнее, что произойдет, если вы используете SolidColorBrush в двух разных местах, где DynamicResource даст два разных ответа?Это может показаться надуманным:

<Window.Resources>

  `<Color x:Key="color">red</Color>`

  `<SolidColorBrush x:Key="brush" Color="{DynamicResource color}" />  `

</Window.Resources>

<Button>
  <Button.Background>
    <StaticResource ResourceKey="brush"/>
  </Button.Background>
</Button>


<Button>
  <Button.Resources>
    <Color x:Key="color">blue</Color>
  </Button.Resources>
  <Button.Background>
    <StaticResource ResourceKey="brush"/>
  </Button.Background>
</Button>

Но на самом деле это происходит в реальном коде.Мы выбрали первое решение, контекст наследования для SolidColorBrush указывает на ресурссловарь, а не его смысл.

Контекст наследования был удивительно полезен, но мы не контекстные ссылки наследования везде, где это теоретически возможно, в основном из-за времени суток (добавление ссылок на контекст наследования очень сложно реализовать качественно и не вызывая нежелательные изменения поведения). Вероятно, самый простой пример того, где у нас нет контекстных ссылок наследования через случайное свойство элементы:

<Button>
  <Button.ContextMenu>
    <ContextMenu/>
  </Button.ContextMenu>
</Button>

ContextMenu не является ни визуальным, ни логическим потомком Button, ни один из случаев контекста наследования, перечисленных выше (ContextMenu не Freezable). Но, имея в своем распоряжении концепцию контекста наследования, мы надеемся решить эту проблему в будущих версиях WPF.

...