与Bindings混淆& ContentTemplate中的DataContext

时间:2013-12-29 22:21:09

标签: c# wpf

考虑以下风格:

<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属性,但我从未在DataContext元素上明确设置<Button>。对我来说,这意味着Button的DataContext为NULL,因此无法找到要显示的值。

有人可以解释当我没有设置Button的DataContext时如何引用它?

3 个答案:

答案 0 :(得分:7)

DataContext的{​​{1}}非常特殊,因为它被设置为<ContentTemplate>正在应用的对象的Content属性。

在这种情况下,<ContentTemplate>正在应用于ContentTemplate,因此Button内的DataContext设置为ContentTemplate属性ContentButton设置为Button.Content

如果您将此"0"应用于具有不同ContentTemplate属性的其他Button,则会改为使用Content属性。

这是一个简单的例子,我希望能更好地证明这一点。

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>

我们这里有一个按钮。 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。此按钮的Button属性设置为不同的值。

单击任一按钮将输出单击按钮的ContentContent。请记住,内部按钮嵌套在OuterButton中,因此当您单击它时,将为两个按钮处理Click方法。

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

InnerButton.DataContext: OuterButton.Content
InnerButton.Content: InnerButton.Content

OuterButton.DataContext: OuterButton.DataContext
OuterButton.Content: OuterButton.Content

正如您所看到的,DataContext的特殊之处在于它将ContentTemplate设置为应用它的任何对象的DataContext属性。

答案 1 :(得分:1)

您的猜测是正确的default source property for ContentControl is Content。因此,通过执行此{Binding},您明确告知绑定引擎将Label的内容属性与按钮的Content属性绑定。

但是假设您要绑定到按钮的DataContext中的某个属性 - DummyDataContext的名称属性,则必须这样做(假设DummyDataContext在根窗口上设置为DataContext < / em>的):

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

和DataTemplate

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

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

希望现在有道理.. !!

答案 2 :(得分:0)

对于大多数控件,如果未设置DataContext,则使用父DataContext,通过继承的依赖项属性完成。 ContentControls和派生的控件,就像Button一样,工作方式略有不同。如果您设置这些Content的{​​{1}},则还会将Controls设置为内容。因此,在您的情况下,您的DataContext也是字符串“0”。根本没有DataContext设置会破坏继承链,因此该按钮内的Content将为空。