4
votes

I'm rebuilding Josh Smith's WPF CommandSink example and there are a few things that I don't understand about his databinding, especially about how datacontext is inherited when a view is contained in another view which is contained in a window which has a datacontext.

  • all databinding is declared in the XAML files, there is absolutely no code behind the Window or either of the Views (nice)
  • the top Window defines its DataContext as CommunityViewModel and simply displays the CommunityView
  • Question: so now in the CommunityViewModel, what does the jas:CommandSinkBinding.CommandSink="{Binding}" do actually? "CommandSink" is an attached property so is this "attaching" the inherited Binding that comes from DemoWindow as the value of the attached property called "CommandSink" on the CommandSinkBinding object?

  • Question: also, PersonView doesn't seem to have a DataContext yet it has lines such as <TextBlock Text="{Binding Name}" Width="60" /> which assume that a binding is set. So does PersonView automatically get its binding from the line in CommunityView ItemsSource="{Binding People}"?

Thanks for any clarification here.

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>
4

4 Answers

4
votes

If you set the ItemsSource of an ItemsControl, the DataContexts of the items in that control are directly mapped to the items of the ItemsSource.

3
votes

A control inherits it's parents DataContext unless one is specified explicitly for the control. So the answer to your second question is yes.

1
votes

PersonView is generated by the DataTemplate for the ItemsControl in CommunityView (CommunityView.xaml:16).

DataTemplates automatically assign the DataContext of their visuals to the data that the template is displaying. This is how WPF works.

CommunityView gets its DataContext set by inheritence from the Window.

The attached command sink property sets the instance CommandSink property on all of the CommandSinkBinding objects contained within the CommandBindings property of the object with the attached property assignment. So in CommunityView.xaml, the CommandSink of all of the CommandSinkBindings in CommunityView (KillAll) are set to the DataContext of the view through binding.

The CommandSink property is used to call the Execute and CanExecute functions on the proper target (the ViewModel in this case).

0
votes

DataContext is a property on FrameworkElement (base class for all WPF Controls) and is implemented as a DependencyProperty. That means all the descendant elements in the logical tree share the same DataContext.