绑定和ContentTemplate中的DataContext引起的混淆

4

考虑以下样式:

<Window.Resources>
    <Style x:Key="NumberButton" TargetType="Button">
        <Setter Property="Width" Value="Auto"/>
        <Setter Property="Height" Value="Auto"/>
        <Setter Property="Margin" Value="2"/>
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Label Content="{Binding}" FontSize="20" FontFamily="Consolas"/>
                </DataTemplate>
            </Setter.Value>
        </Setter>
        <EventSetter Event="Click" Handler="OnClicked"/>
    </Style>
</Window.Resources>

我的目标是创建多个按钮,每个按钮代表一个数字0-9。例如,这里是数字0的按钮:

<Button Grid.Row="3" Grid.Column="1" Style="{StaticResource NumberButton}" Content="0"/>

我的理解是,如果您在XAML中没有明确设置DataContext,则它会被视为NULL,并指示绑定使用父级的DataContext。这是传递性的,因此它将继续向上移动每个父级,直到找到明确设置的DataContext为止。
然而,我对如何将绑定映射到Content属性感到困惑。我知道ContentControl的默认属性是Content属性,但我从未在
3个回答

9
<ContentTemplate>DataContext很特殊,因为它被设置为应用<ContentTemplate>的对象的Content属性。

在这个例子中,ContentTemplate被应用到一个Button上,所以ContentTemplate内部的DataContext被设置为该ButtonContent属性,而Button.Content被设置为"0"

如果将此ContentTemplate应用于具有不同Content属性的其他Button,则将使用该Content属性。

以下是一个简单的示例,希望能更好地说明这一点。

<Button x:Name="OuterButton" Click="Button_Click">
    <!-- // Set DataContext to a string equal to "OuterButton.DataContext" -->
    <Button.DataContext>
        OuterButton.DataContext
    </Button.DataContext>

    <!-- // Set Content to a string equal to "OuterButton.DataContext" -->
    <Button.Content>
        OuterButton.Content
    </Button.Content>

    <Button.ContentTemplate>
        <DataTemplate>
            <StackPanel>
                <Button x:Name="InnerButton" Content="InnerButton.Content" Click="Button_Click" />
                <TextBlock FontWeight="Bold" Text="{Binding }"/>
            </StackPanel>
        </DataTemplate>
    </Button.ContentTemplate>
</Button>

private void Button_Click(object sender, RoutedEventArgs e)
{
    Button btn = sender as Button;
    Debug.WriteLine(string.Format("{0}.DataContext: {1}", btn.Name, btn.DataContext));
    Debug.WriteLine(string.Format("{0}.Content: {1}", btn.Name, btn.Content));
}

我们这里有一个按钮。该按钮的DataContext和Content属性被设置为不同的值。该按钮还有一个定义好的ContentTemplate,其中包含另一个按钮。该按钮的Content属性被设置为不同的值。
单击任一按钮将输出所单击按钮的Content和DataContext。请注意,内部按钮嵌套在OuterButton中,因此当您单击它时,Click方法将同时处理两个按钮。
点击内部按钮后的最终结果:
InnerButton.DataContext: OuterButton.Content InnerButton.Content: InnerButton.Content
OuterButton.DataContext: OuterButton.DataContext OuterButton.Content: OuterButton.Content
正如您可以看到的那样,ContentTemplate是特殊的,因为它将DataContext设置为应用于其的任何对象的Content属性。

Rachel,你总是让学习WPF变得更加容易!!我把你的答案标记为新答案。非常感谢,这让我清晰明了。这也帮助我弄清楚如何在我的样式内外正确映射我的命令,因为两者之间的数据上下文发生了变化! - void.pointer
这是一种很好的方法,可以修改按钮的内容(例如添加图标或其他文本),而不会影响按钮的外观(如悬停、禁用状态等)。 - apc

2

你的猜测是正确的,ContentControl 的默认源属性是 Content。所以通过这样做 {Binding},你明确告诉绑定引擎将标签的内容属性与按钮的内容属性绑定。

但是,假设你想要绑定到按钮的 DataContext 中的某个属性 - 绑定 DummyDataContext 的 Name 属性,你需要这样做(假设 DummyDataContext 已设置为根窗口的 DataContext):

<Button Style="{StaticResource CalcButton}" Content="{Binding}"/>

在数据模板中。
<Label Content="{Binding Name}" FontSize="20" FontFamily="Consolas"/>

按钮的内容指向 DummyDataContext 类的实例。因此,当你将 Content="{Binding Name}" 绑定到标签上时,你告诉它绑定到与按钮内容绑定的对象的 Name 属性。

希望现在讲得更清楚了..!!


那么{Binding}不会绑定到DataContext的值,就像我在网上读到的那样?它会绑定到类的默认属性吗?我听到了很多相互矛盾的信息,这让我非常困惑:(另外,你所说的直接与Rachel在她的答案中陈述的相矛盾。 - void.pointer
你在这里把事情搞混了。我提到了与 Rachel 在她的答案中提到的相同的事情——“<ContentTemplate> 的 DataContext 在特殊之处是设置为应用 <ContentTemplate> 对象的 Content 属性。”在 ContentTemplate 中的 {Binding} 与您阅读过的其他内容不同。 - Rohit Vats

0
对于大多数控件,如果未设置 DataContext ,则会使用父级 DataContext ,通过继承的依赖属性完成。 ContentControls 和从中派生的控件(如 Button )的工作方式略有不同。如果设置这些 Controls Content ,还会将 DataContext 设置为Content。因此,在您的情况下,您的 DataContext 也是字符串"0"。没有设置 Content 将会打破继承链,因此该按钮内部的 DataContext 将为null。

这种行为的改变取决于特定的层次结构,我假设是这样的吗?在我的特定情况下,任何从ContentControl驱动的内容都会自动设置数据上下文,就像你所解释的那样?我找不到任何官方文件对这种行为的说明。 - void.pointer
3
我不同意你的答案。如果你设置ContentControl或继承自ContentControl(如(Button))的控件的Content属性,它并不会同时设置该控件的DataContext。然而,它会将该Content属性用作应用于该控件的任何ContentTemplateDataContext,这可能是你所考虑的情况。请参见我的答案获取示例 :) - Rachel
是的,你说得对,Rachel。离开代码几天后,就开始忘记东西了。 - dowhilefor

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