WPF DataTemplate and Binding

30,341

Solution 1

As you said, the DataContext is set to the ViewModel class so the control that you mentioned in XAML will be able to access the public properties of that ViewModel.

For example:

private ObservableCollection<Commander> commands = new ObservableCollection<Commander>();

    public ObservableCollection<Commander> Commands {
        get { return commands; }
        set { commands = value; }
    }

The structure of Commander class.

public class Commander {
    public ICommand Command { get; set; }
    public string DisplayName { get; set; }
}

That VM has the property called Commands which might be ObservableCollection. This property can be accessible from XAML.

You can imagine that HeaderedContentControl is a container. The content of that HeaderedContentControl is a DataTemplate "CommandsTemplate" that has a ItemsControl and it bind to the Commands property of VM.

Content="{Binding Path=Commands}"

And then, you can to bind ItemControl with Commands again but that ItemControl is inside the content that bind to Commands. So you don't need to specify the path again. You can just use

 ItemsSource="{Binding}" instead of ItemsSource="{Binding Commands}".

Two textblocks are inside ItemControl so they are at the same level as Commander class of Commands ObservableCollection. That's why you can access Text="{Binding Path=DisplayName}" directly.

Hope it helps.

Solution 2

Example:

XAML

<Window x:Class="WpfApplication2.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <DataTemplate x:Key="CommandsTemplate">
            <ItemsControl IsTabStop="False" ItemsSource="{Binding}" Margin="6,2">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Margin="2,6">pou
                            <Hyperlink Command="{Binding Path=Command}">
                                <TextBlock Text="{Binding Path=DisplayName}" />
                            </Hyperlink>
                        </TextBlock>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Border Width="170">
            <HeaderedContentControl
                Content="{Binding Path=Commands}"
                ContentTemplate="{StaticResource CommandsTemplate}"
                Header="Control Panel"/>
        </Border>
    </Grid>
</Window>

C#

/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window {
    public Window1() {
        InitializeComponent();

        Commands.Add(new Commander() { DisplayName = "DN1" });
        Commands.Add(new Commander() { DisplayName = "DN2" });
        Commands.Add(new Commander() { DisplayName = "DN3" });

        this.DataContext = this;
    }

    private void Window_Loaded(object sender, RoutedEventArgs e) {

    }

    private ObservableCollection<Commander> commands = new ObservableCollection<Commander>();

    public ObservableCollection<Commander> Commands {
        get { return commands; }
        set { commands = value; }
    }
}

public class Commander {
    public ICommand Command { get; set; }
    public string DisplayName { get; set; }
}

Solution 3

The ItemsSource binding to {Binding} binds directly to the DataContext of the ItemsControl (which will look up the chain until it finds a set DataContext). In this case it has been set in the HeaderedContentControl

Each item inside the ItemsControl will then have its DataContext set to an element in the list.

The <ItemsControl.ItemTemplate> is setting the template for each Item inside the list, not for the ItemsControl itself. So that {Binding Path=Command} and {Binding Path=DisplayName} will look at those properties on the elements inside the list.

Share:
30,341
Patrick Desjardins
Author by

Patrick Desjardins

Senior Software Developer at Netflix [California, Los Gatos] Senior Software Developer at Microsoft [Washington, Redmond] Working for Microsoft Cloud and Enterprise, mostly on Team Services Dashboards, Kanban and Scaled Agile project Microsoft Teams (first release) Microsoft MVP 2013 and 2014 [Quebec, Montreal]

Updated on January 25, 2020

Comments

  • Patrick Desjardins
    Patrick Desjardins over 4 years

    I continue my understanding of MVVC with the code of MSDN and I have a question.

    In the .xaml they have a list of commands displayed to the screen.

       <Border 
        Grid.Column="0" 
        Style="{StaticResource MainBorderStyle}"
        Width="170"
        >
        <HeaderedContentControl
          Content="{Binding Path=Commands}"
          ContentTemplate="{StaticResource CommandsTemplate}"
          Header="Control Panel"
          Style="{StaticResource MainHCCStyle}"
          />
      </Border>
    

    From here, I understand that the DataContext is set (not shown here) and it will display the collection of Commands. What I do not understand is the CommandsTemplate that you can see below:

    <DataTemplate x:Key="CommandsTemplate">
    <ItemsControl IsTabStop="False" ItemsSource="{Binding}" Margin="6,2">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <TextBlock Margin="2,6">pou
            <Hyperlink Command="{Binding Path=Command}">
              <TextBlock Text="{Binding Path=DisplayName}" />
            </Hyperlink>
          </TextBlock>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
    </DataTemplate>
    

    How does the binding is created? How this code tell to check the property Command and DisplayName from the object inside the collection? Is it from the ItemsSource? If yes, I do not understand why it's only at {Binding}. Anyone can explain me please how the DataTemplate binding work from a ContentTemplate?