views:

597

answers:

4

I have a WPF Window with a view model set as its DataContext, and have a ListBox with a DataTemplate and its ItemsSource bound to the view model, like in the following example:

View model:

using System.Collections.Generic;

namespace Example
{
    class Member
 {
  public string Name { get; set; }
  public int Age { get; set; }
 }

    class Team
 {
  private List<Member> members = new List<Member>();

  public string TeamName { get; set; }
  public List<Member> Members { get { return members; } }
    }
}

MainWindow.xaml:

<Window x:Class="Example.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:l="clr-namespace:Example" 
    Title="Example" Height="300" Width="300" Name="Main">

 <Window.DataContext>
  <l:Team TeamName="The best team">
   <l:Team.Members>
    <l:Member Name="John Doe" Age="23"/>
    <l:Member Name="Jane Smith" Age="20"/>
    <l:Member Name="Max Steel" Age="24"/>
   </l:Team.Members>
  </l:Team>
 </Window.DataContext>

 <ListBox ItemsSource="{Binding Path=Members}">
  <ListBox.ItemTemplate>
   <DataTemplate>
    <StackPanel Orientation="Horizontal">
     <TextBlock Text="{Binding Path=TeamName}" Margin="4"/>
     <TextBlock Text="{Binding Path=Name}" Margin="4"/>
    </StackPanel>
   </DataTemplate>
  </ListBox.ItemTemplate>
 </ListBox>
</Window>

Of course, the TeamName property of the Team class is not displayed in the ListBox items because each item of the LisBox is the DataContext of the List.ItemTemplate, and it overrides the DataContext of the Window.

The question is: How do I databind to the TeamName property of the view model (Window.DataContext) from within the ListBox's DataTemplate?

+4  A: 

I would extract the l:Team declaration to the Window.Resources section, and reference it from within the DataContext and the DataTemplate:

<Window x:Class="Example.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:l="clr-namespace:Example" 
    Title="Example" Height="300" Width="300" Name="Main">

 <Window.Resources>
  <l:Team x:Key="data" TeamName="The best team">
   <l:Team.Members>
    <l:Member Name="John Doe" Age="23"/>
    <l:Member Name="Jane Smith" Age="20"/>
    <l:Member Name="Max Steel" Age="24"/>
   </l:Team.Members>
  </l:Team>
 <Window.Resources>

 <Window.DataContext>
     <StaticResource ResourceKey="data"/>
 </Window.DataContext>

 <ListBox ItemsSource="{Binding Path=Members}">
  <ListBox.ItemTemplate>
   <DataTemplate>
    <StackPanel Orientation="Horizontal">
     <TextBlock Text="{Binding Source={StaticResource data}, Path=TeamName}" Margin="4"/>
     <TextBlock Text="{Binding Path=Name}" Margin="4"/>
    </StackPanel>
   </DataTemplate>
  </ListBox.ItemTemplate>
 </ListBox>
</Window>
Aviad P.
+1. Of course! I hadn't thought about sharing the view model as a resource. I was trying complicating the dindings with RelativeSources and FindAncestor without success.
Rafa Castaneda
Oops, I'd vote you up if I had enough reputation.
Rafa Castaneda
A: 

I'd create a view model for a team member, with a TeamName property, something like:

class MemberViewModel
{
    ...
    private TeamViewModel _team; 
    public string TeamName{ get { return _team.Name; } } 
}

class TeamViewModel
{
    public List< MemberViewModel > Members { get{ ... } }
    // You may consider using ObservableCollection<> instead of List<>
}

Then your XAML will look as clean as in your example. With MVVM, you shouldn't need any exotic binding tricks in the view. All you need should be available via the view model.

Danko Durbić
And how would I do the databindings?
Rafa Castaneda
+1  A: 

You can also use RelativeSource binding, it's not that complicated:

<TextBlock Text="{Binding Path=DataContext.TeamName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Margin="4"/>
cz_dl
A: 

Why dont you bind your DataContext to team, then make your itemsource bound to team.members?

usually I am assigning my datacontext in my codebehind but its still shouldnt be anydifferent for you.

itemsouce="{Binding Path="team.Members"}

I guess really its the same thing as what Aviad suggested. Just I assign datacontext in my codebehind.

ecathell