WPF数据模板和绑定

6

我通过MSDN的代码来深入了解MVVC,但是我有一个问题。

.xaml文件中有一份命令列表显示在屏幕上。

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

从这里我可以理解到DataContext已经被设置(此处未显示),并且它将显示命令集合。但是我不理解下面可以看到的CommandsTemplate:

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

绑定是如何创建的?这段代码是如何告诉程序从集合中检查属性Command和DisplayName的?它是从ItemsSource中获取的吗?如果是,为什么只有在{Binding}处出现?有人可以解释一下ContentTemplate中的DataTemplate绑定是如何工作的吗?

3个回答

9

正如您所说,DataContext设置为ViewModel类,因此在XAML中提到的控件将能够访问该ViewModel的公共属性。

例如:

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; }
}

那个虚拟机有一个叫做Commands的属性,可能是ObservableCollection类型。这个属性可以从XAML中访问。

你可以想象HeaderedContentControl是一个容器。该HeaderedContentControl的内容是一个名为"CommandsTemplate"的DataTemplate,它具有一个ItemsControl,并将其绑定到VM的Commands属性。

Content="{Binding Path=Commands}"

然后,您可以再次使用Commands来绑定ItemControl,但是该ItemControl位于绑定到Commands的内容中。因此,您不需要再次指定路径。您只需使用

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

在ItemControl中有两个文本块,因此它们与Commands ObservableCollection的Commander类处于同一级别。这就是为什么您可以直接访问Text =“{Binding Path = DisplayName}”的原因。

希望对你有所帮助。


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; }
}

1

ItemsSource绑定到{Binding}直接绑定到ItemsControl的DataContext(它会一直查找链,直到找到设置的DataContext)。在这种情况下,它已经在HeaderedContentControl中设置了。

然后,ItemsControl内的每个项目将其DataContext设置为列表中的元素。

<ItemsControl.ItemTemplate>设置列表中每个项目的模板,而不是ItemsControl本身的模板。因此,{Binding Path=Command}{Binding Path=DisplayName}将查看列表内元素上的这些属性。


如果它直接绑定到DataContext,那么它应该绑定到列表的Context而不是列表元素。 - Patrick Desjardins
这对于ItemsControl来说是正确的,但是ItemsControl中的每个**项(item)**都将有一个列表元素作为其DataContext。 - Ray
好的,那么使用 {Binding} 会在此集合中搜索 DataContext,对吗? - Patrick Desjardins
我不明白你在问什么。{Binding}本身将始终绑定到DataContext。因此,如果您在ItemTemplate内部,则会绑定到集合中的元素。 - Ray
好的,对我来说{Binding}绑定到集合内部的DataContext并不那么明显。对我来说,自然而然地进入当前数据上下文即列表看起来很自然。但是现在我明白了,谢谢。 - Patrick Desjardins

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