views:

2302

answers:

3

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?

+1  A: 

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.

Ray
If it binds directly to the DataContext it should be binded to the Context of the List and not the element of the list?
Daok
That's true for the ItemsControl, but each **item** in the ItemsControl will have an element of the list for its DataContext.
Ray
Alright, so using {Binding} will search a DataContext inside this collection right?
Daok
I don't get what you're asking there. {Binding} by itself will always bind to the DataContext. So if you're inside the ItemTemplate then it will bind to an element in the collection.
Ray
Alright, it doesn't sound that much evident for me that {Binding} bind itself to the DataContext inside the collection. For me, it looks naturaly to go on the current datacontext that is the List. But I understand now thanks.
Daok
+2  A: 

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.

Michael Sync
A: 

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#

namespace WpfApplication2 { /// /// Interaction logic for Window1.xaml /// 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; }
}

}

Michael Sync