Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

page.DataContext not inherited from parent Frame?

I have a Page page in a Frame frame, with frame.DataContext = "foo".

  • (page.Parent as Frame).DataContext is "foo". ok
  • BindingExpression for page.DataContext is null (also forced with ClearValue). ok
  • page.DataContext is null. but I expected "foo"!

Why isn't the DataContext inherited? As far as I understand the Frame sandboxes the content. But I couldn't find any documentation of this behavior - can anyone point me to a place where this is mentioned?

like image 568
Marcel Jackwerth Avatar asked Sep 01 '10 19:09

Marcel Jackwerth


2 Answers

You didn't specifically ask how you could make this work, only why it doesn't by default. However, if you do want your Pages to inherit the Frame's DataContext, you can do this:

In XAML:

<Frame Name="frame"
       LoadCompleted="frame_LoadCompleted"
       DataContextChanged="frame_DataContextChanged"/>

In codebehind:

private void frame_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    UpdateFrameDataContext(sender, e);
}
private void frame_LoadCompleted(object sender, NavigationEventArgs e)
{
    UpdateFrameDataContext(sender, e);
}
private void UpdateFrameDataContext(object sender, NavigationEventArgs e)
{
    var content = frame.Content as FrameworkElement;
    if (content == null)
        return;
    content.DataContext = frame.DataContext;
}
like image 157
Joe White Avatar answered Oct 05 '22 19:10

Joe White


To build upon @Joe-White's answer for those who want to know of ways to make the Frame cascade the DataContext, I'll mention that this can also be performed in XAML only.

    <Style TargetType="{x:Type Frame}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Frame}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
                        <ContentPresenter x:Name="PART_FrameCP" DataContext="{TemplateBinding DataContext}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="NavigationUIVisibility" Value="Visible">
                <Setter Property="Template" Value="{StaticResource FrameNavChromeTemplateKey}"/>
            </Trigger>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="JournalOwnership" Value="OwnsJournal"/>
                    <Condition Property="NavigationUIVisibility" Value="Automatic"/>
                </MultiTrigger.Conditions>
                <Setter Property="Template" Value="{StaticResource FrameNavChromeTemplateKey}"/>
            </MultiTrigger>
        </Style.Triggers>
    </Style>

For those that are new to WPF, you can put this XAML in the App.xaml file so that it will override all Frame controls in your application that pick up the default style. This means you don't have to write specific code-behind each time you use a new Frame.

I used the VisualStudio 2015 Visual Designer (see pic below) to create the bulk of the XAML above and then added the DataContext="{TemplateBinding DataContext}" to perform the cascade.

VS2015 designer

like image 27
Rhys Avatar answered Oct 05 '22 18:10

Rhys