FrameworkElement`s DataContext Property does NOT inherit down the element tree
Asked Answered
H

4

10

Hello WPF Pros at least I hope some of you read this!

DataContext is a property on FrameworkElement (base class for all WPF Controls) and is implemented as a DependencyProperty. That means all the descendant elements in the logical tree share the same DataContext.

So the ContentControl should do it with its descendant elements right?

I have a scenario where that is NOT the case and I would like to know WHAT is the cause of that misbehaviour ?!

That you understand a bit more about it please read this thread ( dont NOT want to copy everything here) where the trouble starts...:

WPF: Can not find the Trigger target 'cc'. The target must appear before any Setters, Triggers

and to say it in short words: My DataTemplates within the ContentControl do have a dead DataContext that means there is NOTHING to bind to it, what is actually not possible...

Every Element down the ContentControl has NOTHING set in the DataContext Property ???

Howsoever answered 18/12, 2010 at 21:56 Comment(0)
M
24

DataContext is a property on FrameworkElement (base class for all WPF Controls) and is implemented as a DependencyProperty. That means all the descendant elements in the logical tree share the same DataContext.

The fact that it's a dependency property doesn't imply inheritance... It's true for DataContext, but only because the dependency property has the FrameworkPropertyMetadataOptions.Inherits flag in its metadata.

So the ContentControl should do it with its descendant elements right?

ContentControl is a bit special: the DataContext of its descendants (the visual tree built from the DataTemplate) is actually be the Content of the ContentControl. So if your ContentControl has no content, the DataContext inside it is null.

Marco answered 18/12, 2010 at 22:25 Comment(12)
My ContentControl HAS a DataContext like PersonListViewModel, but the problem seems to be one Node down the ContentPresenter and its Content Property which is not set and so the DataContext is NOT either. Ok I understand your answer although it still sounds a bit odd to me. So the solution that Meleak offered me was actually no workaround rather the only thing he could/must do. Any other solution/opinion on this matter ?Howsoever
Your ContentControl has a DataContext, but does it have a Content ?Marco
There is no "Content" Property in the ContentControl using Snoop. In XAML there is... ?? One Node down there is the ContentPresenter which has a Content Property and that is empty.Howsoever
By default Snoop doesn't show the properties that are set to their default value, you can display them with the "D" button. Anyway, that probably means the Content is null, which explains why you have no DataContext in the descendantsMarco
Yes the "D" up right in the corner of snoop... Your were right the Content is empty so no DataContext. The question just is why is the Content Property empty ?Howsoever
Well, perhaps because you didn't set it... I can only make assumptions since you didn't post the codeMarco
ah sorry I thought you know that link from the other thread with Meleak: sendspace.com/file/a427u1 is it ok for you to download this VS2010 project? posting all code here will be a lot prolly?Howsoever
Regarding this accepted answer, why is this the case? It seems like a nasty gotcha for which I can see no reason, but I'm sure there is a reason.Gastric
@ardave, I don't understand your question. Why is what the case?Marco
Why is it the case that ContentControls need their Content to be set for their DataContexts to have a value, whereas so many other WPF entities support direct DataContext inheritance?Gastric
@ardave, my guess is that if it were the case, it would cause nesting problems with implicit data templates. For instance, assume that you have a DataTemplate for type X (declared in resources with type X as the key, so it's picked up automatically in ContentControls), and that DataTemplate contains a ContentControl. If you didn't set the Content on this ContentControl, and the DataContext is inherited as the content, then this ContentControl would use the same DataTemplate to display its content, causing infinite nesting...Marco
Anyway, that's probably a conscious design decision, that might have nothing to do with the explanation above... you'd have to ask Microsoft for the real reasons.Marco
E
15

This worked for me:

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

Without the Content="{Binding}", the DataContext was NULL

Ethiop answered 9/10, 2012 at 19:24 Comment(1)
This can be simplified slightly to <ContentControl ContentTemplate="{StaticResource NotesTemplate}" Content="{Binding HeightField}"/>Cherey
L
0

The last answer (from VinceF) worked for me too.

I wanted to show a usercontrol depending on the value of a property in my viewmodel. So I made a ContentControl with some Style Triggers. Depending on the value of a bind property the trigger sets a specific ContentTemplate containing the specific usercontrol.

The usercontrol was shown right, but its DataContext was always null. So I had to set the Context of the ContentControl to: Content="{Binding}" After that, the UserControls worked fine and had the same DataContext as their parent.

So my XAML looks like that:

In the Resources part I defined two DataTemplates; each one for each UserControl I want to show.

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

The part where I show the UserControl depending on a property is the following:

<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>
Loyola answered 4/12, 2013 at 18:17 Comment(0)
S
0

after reading this question and previous answers, I prefer using ContentControl with data triggered Content like this:

Controls which will be set as Content of ContentControl:

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

ContentControl which switch own content by DataTrigger in ContentControl style:

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

I hope this helps to someone like previous answers to me.

Soprano answered 4/9, 2014 at 13:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.