页面的DataContext属性没有从父Frame继承?

20

我有一个在Frame frame中的Pagepage,并设置了frame.DataContext = "foo"

  • (page.Parent as Frame).DataContext"foo"没问题
  • page.DataContext 的 BindingExpression 为 null(也通过ClearValue强制为null)。 没问题
  • page.DataContextnull但我期望是"foo"!

为什么DataContext没有继承呢?据我所知,Frame会对内容进行隔离(sandboxing)。但我无法找到任何关于此行为的文档 - 请问是否可以指出任何提到这一点的地方?


绑定表达式并未传递给子项,但数据上下文已传递。您是否检查了数据上下文属性本身? - Carlo
是的,DataContext 也为空。即使调用 ClearValue,它仍然保持为空。 - Marcel Jackwerth
感谢您的提问:0)请注意,在所有内容加载完成后(已加载事件之后),从承载框架的用户控件更改数据上下文会导致页面继承新的数据上下文(必须将其清空并设置回原始状态,或者在加载事件完成后再进行设置)。 - paulecoyote
3个回答

33
你并没有特别询问如何让这个功能起作用,只是想知道为什么默认情况下不能。然而,如果你想让页面继承框架的DataContext,可以按照以下方式操作:
在XAML中:
<Frame Name="frame"
       LoadCompleted="frame_LoadCompleted"
       DataContextChanged="frame_DataContextChanged"/>
在代码后台:
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;
}

正常工作 :) 谢谢。 - torpederos

20

回答关于此行为的文档问题:这不是微软的文档,但我有几本提到了这点的WPF书籍。

"Essential Windows Presentation Foundation" 说道(第160-161页):

有两种有趣的可导航内容宿主模型:隔离宿主和集成宿主。

隔离宿主指的是内容是不可信的,并在完全隔离的(沙盒)环境中运行。当作为XAML浏览器应用程序在系统Web浏览器中运行时,这就是WPF内容的托管方式。对于导航到另一个应用程序或HTML内容,可以使用Frame对象支持此隔离宿主模型。

集成宿主,我们希望内容表现为应用程序的一部分,在系统中根本不受支持。当Frame导航到应用程序内的内容时,我们会得到奇怪的隔离和集成行为混合体。 Frame将其内容与其样式(以及其父级样式)隔离,但不隔离应用程序的样式。事件不会从Frame中的内容冒泡;但是,可以通过Content属性访问对象(这意味着它们在安全意义上并没有被隔离)。

出于所有这些原因,当我们使用外部内容时,Frame最有用,但可以小心地用于应用程序内容。

书中只提到了这些,没有关于属性继承的内容。

"Windows Presentation Foundation Unleashed" 说道(第95页):

Frame 控件可以保存任意内容,就像其他所有内容控件一样,但它将内容与其余 UI 隔离开来。例如,通常会向下继承沿元素树的属性在到达 Frame 时停止。


2
为了补充@Joe-White的答案,对于那些想知道如何让Frame级联DataContext的方法,我将提到这也可以仅在XAML中执行。
    <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>

对于那些刚接触WPF的人,您可以将此XAML放入App.xaml文件中,以便覆盖应用程序中所有选取默认样式的Frame控件。这意味着您不必每次使用新的Frame时编写特定的代码。我使用VisualStudio 2015可视化设计器(见下图)创建了上面大部分的XAML,然后添加了DataContext="{TemplateBinding DataContext}"来执行级联。

VS2015 designer


对我没有用。我收到了一个异常:“无法将类型为'MS.Internal.NamedObject'的对象转换为类型'System.Windows.FrameworkTemplate'”。 - denver

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