【问题标题】:Confusion with Bindings & DataContext in ContentTemplate与 ContentTemplate 中的绑定和 DataContext 混淆
【发布时间】:2013-12-29 22:21:08
【问题描述】:

考虑以下样式:

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

我对 DataContext 工作原理的理解是,如果您没有在 XAML 中显式设置它,它应该是 NULL,它指示绑定改为使用父级的 DataContext。这是传递性的,因此它会继续向上移动每个父级,直到找到要使用的明确设置的 DataContext。

然而,我对我的绑定如何映射到Content 属性感到困惑。我知道ContentControl 的默认属性是Content 属性,但我从未在&lt;Button&gt; 元素上明确设置DataContext。对我来说,这意味着 Button 的 DataContext 为 NULL,因此它无法找到要显示的值。

谁能解释一下我没有设置 Button 的 DataContext 是如何被引用的?

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    &lt;ContentTemplate&gt;DataContext 的特殊之处在于它被设置为 &lt;ContentTemplate&gt; 正在应用到的对象的 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));
    }
    

    我们在这里有一个按钮。 Button 的DataContextContent 属性设置为不同的值。该按钮还定义了一个ContentTemplate,其中包含另一个Button。此按钮的Content 属性设置为不同的值。

    单击任一按钮将输出被单击按钮的ContentDataContext。请记住,内部按钮嵌套在 OuterButton 内,因此当您单击它时,两个按钮的 Click 方法都会得到处理。

    点击内部按钮的最终结果:

    InnerButton.DataContext:OuterButton.Content 内部按钮内容:内部按钮内容 OuterButton.DataContext:OuterButton.DataContext 外按钮内容:外按钮内容

    如您所见,ContentTemplate 的特殊之处在于它将DataContext 设置为所应用对象的Content 属性。

    【讨论】:

    • Rachel,你总是让学习 WPF 变得如此简单!我将您的标记为新答案。非常感谢,这对我来说很清楚。这也帮助我弄清楚了如何在 ContentTemplate 外部和内部以我的风格正确映射命令,因为两者之间的数据上下文会发生变化!
    • 这是修改按钮内容(例如添加图标或附加文本)而不影响按钮外观(例如悬停、禁用状态等)的好方法
    【解决方案2】:

    您的猜测是正确的default source property for ContentControl is Content。因此,通过这样做{Binding},您明确告诉绑定引擎将标签的内容属性与按钮的内容属性绑定。

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

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

    在数据模板中

    <Label Content="{Binding Name}" FontSize="20" FontFamily="Consolas"/>
    

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

    希望现在有意义..!!

    【讨论】:

    • 所以{Binding} 没有绑定到DataContext 的值,就像我一直在线阅读的那样?它绑定到类的默认属性?我听到了很多相互矛盾的信息,这让我很困惑:(而且你所说的与雷切尔在她的回答中所说的直接矛盾。
    • 你在这里搞混了。我提到了雷切尔在她的回答中提到的相同内容 - ContentTemplate 中的The DataContext of the &lt;ContentTemplate&gt; is special in that it gets set to the Content property of the object the &lt;ContentTemplate&gt; is getting applied to. {Binding} 与您阅读的其他内容相比表现不同。
    【解决方案3】:

    对于大多数控件,如果没有设置DataContext,它将使用父DataContext,通过继承的依赖属性完成。 ContentControls 和从中派生的控件,如 Button,工作方式略有不同。如果您设置了这些Controls 中的Content,您也将DataContext 设置为内容。因此,在您的情况下,您的 DataContext 也是字符串“0”。完全不设置Content 会破坏继承的链,因此该按钮内的DataContext 将为空。

    【讨论】:

    • 这种行为变化取决于我假设的特定层次结构?在我的具体情况下,从 ContentControl 驱动的任何内容都会按照您的解释自动设置数据上下文?我找不到有关此行为的任何官方文档。
    • 我不同意你的回答。如果您设置ContentControlContent 属性,或从ContentControl 派生的控件,例如Button,它也不会设置该控件的DataContext。但是,它确实使用该Content 属性作为应用于该控件的任何ContentTemplateDataContext,这可能是您所想的。有关示例,请参见 my answer :)
    • 是的,你是对的,瑞秋。离代码还有几天,已经开始忘记东西了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-25
    • 1970-01-01
    相关资源
    最近更新 更多