Как DataContext наследуется в представлениях, содержащих представления?

Я перестраиваю пример WPF CommandSink Джоша Смита, и есть несколько моментов, которые Я не понимаю о его привязке данных, особенно о том, как наследуется контекст данных, когда представление содержится в другом представлении, которое содержится в окне, имеющем контекст данных.

  • все привязки данных объявлены в файлах XAML, за окном или любым из представлений нет абсолютно никакого кода (приятно)
  • верхнее окно определяет свой DataContext как CommunityViewModel и просто отображает CommunityView
  • Вопрос: что теперь в модели CommunityViewModel делает jas:CommandSinkBinding.CommandSink="{Binding}"? «CommandSink» — это прикрепленное свойство, поэтому является ли это «присоединением» унаследованного Binding, полученного от DemoWindow, в качестве значения присоединенного свойства, называемого «CommandSink», к объекту CommandSinkBinding?

  • Вопрос: кроме того, у PersonView нет DataContext, но есть такие строки, как <TextBlock Text="{Binding Name}" Width="60" />, которые предполагают, что привязка установлена. Таким образом, PersonView автоматически получает свою привязку к строке в CommunityView ItemsSource="{Binding People}"?

Спасибо за любые разъяснения здесь.

DemoWindow.xaml:

<Window 
  x:Class="VMCommanding.DemoWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:view="clr-namespace:VMCommanding.View"
  xmlns:vm="clr-namespace:VMCommanding.ViewModel"
  FontSize="13"
  ResizeMode="NoResize"
  SizeToContent="WidthAndHeight" 
  Title="ViewModel Commanding Demo"   
  WindowStartupLocation="CenterScreen"
  >
  <Window.DataContext>
    <vm:CommunityViewModel />
  </Window.DataContext>

  <Window.Content>
    <view:CommunityView />
  </Window.Content>
</Window>

CommunityView.xaml:

<UserControl 
  x:Class="VMCommanding.View.CommunityView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:jas="clr-namespace:VMCommanding"
  xmlns:view="clr-namespace:VMCommanding.View"
  xmlns:vm="clr-namespace:VMCommanding.ViewModel"  
  jas:CommandSinkBinding.CommandSink="{Binding}"
  >
    <UserControl.CommandBindings>
        <jas:CommandSinkBinding Command="vm:CommunityViewModel.KillAllMembersCommand" />
    </UserControl.CommandBindings>

    <DockPanel Margin="4">
        <ItemsControl
      DockPanel.Dock="Bottom" ItemsSource="{Binding People}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <view:PersonView />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button 
      Command="vm:CommunityViewModel.KillAllMembersCommand"
      Content="Kill All"
      Margin="0,0,0,8"
      />
    </DockPanel>
</UserControl>

PersonView.xml:

<UserControl 
  x:Class="VMCommanding.View.PersonView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:jas="clr-namespace:VMCommanding"
  xmlns:vm="clr-namespace:VMCommanding.ViewModel"
  jas:CommandSinkBinding.CommandSink="{Binding}"
  >  
  <UserControl.CommandBindings>
    <jas:CommandSinkBinding Command="vm:PersonViewModel.DieCommand" />
    <jas:CommandSinkBinding Command="vm:PersonViewModel.SpeakCommand" />
  </UserControl.CommandBindings>

  <UserControl.Resources>
    <Style TargetType="{x:Type TextBlock}">
      <Setter Property="Margin" Value="0,0,6,0" />
      <Style.Triggers>
        <DataTrigger Binding="{Binding CanDie}" Value="False">
          <Setter Property="Foreground" Value="#88000000" />
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </UserControl.Resources>

  <StackPanel Margin="2" Orientation="Horizontal">
    <TextBlock Text="Name:" FontWeight="Bold" />
    <TextBlock Text="{Binding Name}" Width="60" />
    <TextBlock Text="Age:" FontWeight="Bold" />
    <TextBlock Text="{Binding Age}" Width="40" />
    <Button 
      Command="vm:PersonViewModel.SpeakCommand"
      CommandParameter="Howdy partner!"
      Content="Speak"
      Margin="0,0,6,0"
      Width="60"
      />
    <Button
      Command="vm:PersonViewModel.DieCommand"
      Content="Die"
      Width="60"
      />
  </StackPanel>
</UserControl>

person Edward Tanguay    schedule 22.04.2009    source источник


Ответы (4)


Если вы установите ItemsSource для ItemsControl, DataContexts элементов в этом элементе управления напрямую сопоставляются с элементами ItemsSource.

person Botz3000    schedule 22.04.2009
comment
Это решило проблему, с которой я столкнулся. Я явно устанавливал DataContext дочернего элемента в ItemsControl.ItemTemplate, и он будет установлен как нуль. Спасибо! - person hitec; 29.04.2009

Элемент управления наследует родительский DataContext, если он не указан явно для элемента управления. Так что ответ на ваш второй вопрос - да.

person Farawin    schedule 22.04.2009

PersonView создается DataTemplate для ItemsControl в CommunityView (CommunityView.xaml:16).

DataTemplates автоматически присваивают DataContext своих визуальных элементов данным, отображаемым в шаблоне. Вот как работает WPF.

CommunityView получает свой набор DataContext путем наследования от Window.

Свойство присоединенного приемника команд задает свойство экземпляра CommandSink для всех объектов CommandSinkBinding, содержащихся в свойстве CommandBindings объекта с присоединенным назначением свойства. Таким образом, в CommunityView.xaml CommandSink всех привязок CommandSinkBindings в CommunityView (KillAll) устанавливается в DataContext представления через привязку.

Свойство CommandSink используется для вызова функций Execute и CanExecute для соответствующей цели (в данном случае ViewModel).

person Josh G    schedule 22.04.2009

DataContext — это свойство FrameworkElement (базовый класс для всех элементов управления WPF), реализованное как DependencyProperty. Это означает, что все элементы-потомки в логическом дереве используют один и тот же DataContext.

person Vasu Balakrishnan    schedule 09.06.2009