在WPF中,如何从包含的ListBox的DataTemplate内部绑定到Window DataContext?

11

我有一个WPF窗口,其中设置了一个视图模型作为其DataContext,并且有一个ListBox,其中包含DataTemplate并且ItemsSource绑定到视图模型,如下例所示:

视图模型:

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>

当然,团队类(Team class)的TeamName属性并未显示在ListBox项中,因为每个ListBox项都是List.ItemTemplate的DataContext,它会覆盖Window的DataContext。

问题是:我如何从ListBox的DataTemplate中对视图模型(Window.DataContext)的TeamName属性进行数据绑定?

4个回答

18

你也可以使用相对源绑定,它并不复杂:

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

16

我会将 l:Team 的声明提取到 Window.Resources 部分,并在 DataContext 和 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>

+1。当然可以!我没有考虑过将视图模型共享为资源。我一直试图使用RelativeSources和FindAncestor来复杂化绑定,但没有成功。 - Rafa Castaneda

1
我会为团队成员创建一个视图模型,其中包含一个名为TeamName的属性,类似于以下内容:
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<>
}

那么你的XAML将会像你的示例一样干净。使用MVVM,您不应该在视图中需要任何奇特的绑定技巧。您所需的所有内容都应该通过视图模型提供。


1
我该如何进行数据绑定? - Rafa Castaneda

0
为什么不将你的DataContext绑定到团队,然后让你的ItemSource绑定到team.members呢?
通常我会在我的CodeBehind中分配DataContext,但对于您来说应该没有任何区别。
itemsouce="{Binding Path="team.Members"}
我猜这确实与Aviad建议的是一样的。只是我在我的CodeBehind中分配了DataContext。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接