Подтвердить что ты не робот

Page.DataContext не унаследован от родительского фрейма?

У меня есть страница page в кадре frame, с frame.DataContext = "foo".

  • (page.Parent as Frame).DataContext - "foo". ОК
  • BindingExpression для page.DataContext - null (также принудительно с ClearValue). ОК
  • page.DataContext - null. , но я ожидал "foo"!

Почему DataContext не унаследован? Насколько я понимаю, кадр изолирует содержимое. Но я не мог найти документацию об этом поведении - может ли кто-нибудь указать мне место, где это упоминается?

4b9b3361

Ответ 1

Чтобы ответить на вопрос о документации этого поведения: это не документация Microsoft, но у меня есть несколько книг WPF, в которых упоминаются об этом.

" Essential Windows Presentation Foundation" говорит: (стр. 160-161)

Есть две интересные модели для размещения навигационного контента: изолированный хостинг и интегрированный хостинг.

С изолированным хостингом содержимое не доверяется и запускается в полностью изолированной (изолированной) среде. Так размещается контент WPF при работе в веб-браузере системы как приложение браузера XAML. Для навигации к другому приложению или содержимому HTML эта изолированная модель хостинга поддерживается объектом Frame.

Интегрированный хостинг, в котором мы хотим, чтобы контент вел себя как часть нашего приложения, вообще не поддерживается в системе. Когда Frame переходит к контенту в приложении, мы получаем нечетный гибрид изолированного и интегрированного поведения. Frame изолирует его содержимое от его стиля (и его родительского стиля), но не от стиля приложения. События не выходят из содержимого в Frame; однако объекты доступны из свойства Content (что означает, что они не изолированы в смысле безопасности).

По всем этим причинам Frame наиболее полезно, когда мы работаем с внешним контентом, но его можно тщательно использовать для содержимого приложения.

Что все это должно сказать - ничего о наследовании свойств.

" Windows Presentation Foundation Unleashed говорит (стр. 95):

Элемент управления Frame содержит произвольный контент, как и все другие элементы управления содержимым, но он изолирует контент от остальной части пользовательского интерфейса. Например, свойства, которые обычно наследуются вниз по дереву элементов, останавливаются, когда они достигают Frame.

Ответ 2

Вы специально не задавались вопросом, как вы могли бы сделать эту работу, только почему это не по умолчанию. Однако, если вы хотите, чтобы ваши страницы наследовали Frame DataContext, вы можете сделать это:

В XAML:

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

В 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;
}

Ответ 3

Чтобы построить ответ на @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 Visual Designer (см. рис. ниже), чтобы создать основную часть XAML выше, а затем добавил DataContext="{TemplateBinding DataContext}" для выполнения каскада.

VS2015 designer