FrameworkElement的DataContext属性不会向下继承到元素树。

10

你好,WPF专家们,至少我希望你们中的一些人能够阅读这篇文章!

DataContext是FrameworkElement上的一个属性(所有WPF控件的基类),并且被实现为DependencyProperty。这意味着逻辑树中的所有后代元素共享相同的DataContext。

那么ContentControl应该和其后代元素一起使用,对吗?

我有一个场景,情况就不是这样的,我想知道这种不当行为的原因是什么?!

为了更好地理解它,请阅读这个线程(不想在这里复制所有内容),其中麻烦开始...:

WPF:找不到触发器目标“cc”。目标必须出现在任何Setters、Triggers之前

简单来说:我的ContentControl中的DataTemplates具有死亡的DataContext,这意味着没有东西可以绑定它,但事实上这是不可能的...

每个ContentControl下面的元素DataContext属性都没有设置吗?

4个回答

24

DataContextFrameworkElement(WPF控件的基类)上的一个属性,它被实现为一个依赖属性。这意味着逻辑树中所有后代元素都共享相同的DataContext

事实上,它是一个依赖属性并不意味着继承...对于DataContext来说是正确的,但这仅因为该依赖属性在其元数据中具有FrameworkPropertyMetadataOptions.Inherits标志。

那么ContentControl应该与其后代元素一起做到这一点,对吗?

ContentControl有点特殊:它的后代元素(从DataTemplate构建的可视树)的DataContext实际上就是ContentControlContent。因此,如果您的ContentControl没有内容,则其中的DataContext为空。


关于这个被接受的答案,为什么会是这样呢?我觉得这是一个非常棘手的问题,我无法找到任何原因,但我相信一定有原因。 - user74754
@ardave,我不明白你的问题。为什么会是这种情况? - Thomas Levesque
为什么ContentControls需要设置它们的Content属性才能使它们的DataContext有值,而许多其他WPF实体支持直接的DataContext继承呢? - user74754
1
@ardave,我的猜测是,如果这样的话,它会导致隐式数据模板的嵌套问题。例如,假设您有一个类型为X的DataTemplate(在资源中声明,类型X作为键,因此在ContentControls中自动选择),并且该DataTemplate包含一个ContentControl。如果您没有设置此ContentControl上的Content,并且DataContext被继承为内容,则此ContentControl将使用相同的DataTemplate来显示其内容,从而导致无限嵌套... - Thomas Levesque
无论如何,那可能是一个有意识的设计决策,与上述解释可能没有任何关系...你需要向微软询问真正的原因。 - Thomas Levesque
显示剩余7条评论

15

这对我有用:

<ContentControl ContentTemplate="{StaticResource NotesTemplate}"
                Content="{Binding}"
                DataContext="{Binding HeightField}"/>

没有Content="{Binding}",DataContext就是NULL


这可以稍微简化为<ContentControl ContentTemplate="{StaticResource NotesTemplate}" Content="{Binding HeightField}"/> - Peter Taylor

0

阅读了这个问题和之前的答案后,我更喜欢使用带有数据触发内容的ContentControl,像这样:

将被设置为ContentControl内容的控件:

<TextBox x:Key="ViewA">
   ...
</TextBox>
<ComboBox x:Key="ViewB">
   ...
</ComboBox>

ContentControl 可以通过 DataTrigger 在 ContentControl 样式中切换自己的内容:

<ContentControl>
  <ContentControl.Style>
    <Style>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=Property}" Value="0">
                <Setter Property="Content" Value="{StaticResource ViewA}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=Property}" Value="1">
                <Setter Property="Content" Value="{StaticResource ViewB}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
  </ContentControl.Style>
</ContentControl>

我希望这对像我之前得到答案的人有所帮助。


0

最后一个答案(来自VinceF)对我也起作用。

我想根据视图模型中属性的值显示用户控件。所以我制作了一个ContentControl,使用一些Style Triggers。根据绑定属性的值,触发器设置特定的ContentTemplate,其中包含特定的用户控件。

用户控件被正确显示,但它的DataContext总是为空。因此,我必须设置ContentControl的上下文为:Content="{Binding}" 然后,UserControls正常工作,并具有与其父级相同的DataContext。

因此,我的XAML如下:

在资源部分,我定义了两个DataTemplates;每个DataTemplate都用于显示每个想要显示的UserControl。

<DataTemplate x:Key="ViewA">
    <namespace:UserControlA/>
</DataTemplate>
<DataTemplate x:Key="ViewB">
    <namespace:UserControlB/>
</DataTemplate>

根据属性显示用户控件的部分如下:

<ContentControl Content="{Binding}">
    <ContentControl.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=Property}" Value="0">
                    <Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewA}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=Property}" Value="1">
                    <Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewB}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

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