为什么我的 AdaptiveTrigger 在我更改主题设置时会触发?

Why does my AdaptiveTrigger fire when I change the theme settings?

我有一个用户界面,可以使用 AdaptiveTrigger 适应不同的设备形状因素。特别是,我有一个基于 SplitView 的 shell 菜单,当最小 window 宽度分别为 1000 epx 和 600 epx 时,有两个状态触发器。 600 epx 状态触发器可以在横向模式的某些移动设备上触发,具体取决于它们的外形和比例因子,这是预期的行为。

但是,我注意到在桌面上,一旦我更改,600 epx 状态触发器就会在后台触发(将 ShellSplitView.DisplayModeOverlay 更改为 CompactOverlay)主题设置,例如在深色和浅色模式之间切换、更改强调色或切换高对比度模式;在移动模拟器和设备上,当我转到“设置”应用程序、更改主题设置并 return 我的应用程序时,它会触发。最特别的是,这只发生在默认状态下,并且只有在我通过调整桌面上 window 的大小或旋转移动模拟器或设备导致任何状态触发器触发后才会发生。 Resizing/rotating 在 return 到应用程序后再次 Resizing/rotating 使问题消失。我以前没有在任何其他应用程序中看到过这一点,包括 Microsoft 的应用程序或任何第三方 UWP 应用程序。

我已经确认这不是由其他一些干扰状态触发器的代码引起的,方法是在一个空白的 UWP 项目中成功地重现了这个问题,其中一个页面只包含一个 SplitView 和任意数字视觉状态(和一些文本内容):

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1000"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="ShellSplitView.DisplayMode" Value="Inline"/>
                        <Setter Target="ShellSplitView.IsPaneOpen" Value="True"/>
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="600"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="ShellSplitView.DisplayMode" Value="CompactOverlay"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <SplitView
            x:Name="ShellSplitView"
            CompactPaneLength="48"
            DisplayMode="Overlay"
            IsPaneOpen="False"
            OpenPaneLength="256">
            <SplitView.Content>
                <TextBlock Text="Content"/>
            </SplitView.Content>
            <SplitView.Pane>
                <TextBlock Text="Pane"/>
            </SplitView.Pane>
        </SplitView>
    </Grid>
</Page>

您可以尝试自己重现问题,方法是创建一个空白的 UWP 项目,将 MainPage.xaml 的内容替换为上面的 XAML,然后 运行 项目。

考虑到我所做的只是更改主题设置,我不明白为什么会触发任何状态触发器。事实上,根据 MSDN 上的 this guide,应该自动重新应用 SplitView 元素属性中反映的默认值:

When you use state triggers, you don't need to define an empty DefaultState. The default settings are reapplied automatically when the conditions of the state trigger are no longer met.

并且根据 Windows.UI.Xaml.AdaptiveTrigger page 本身:

By default, the StackPanel orientation is Vertical. When the window width is >= 720 effective pixels, the VisualState change is triggered, and the StackPanel orientation is changed to Horizontal.

... 这一起强化了我的观点 — window 宽度在我更改主题设置时不会改变,所以我不明白为什么状态触发器会在不应该启动的时候启动' t,以及为什么只有在我通过调整 window 大小或旋转设备来触发状态更改之后。

为什么会发生这种情况,我该如何解决?

此问题似乎已在所有设备系列的 Windows 10 周年更新中得到修复。状态触发器在不应该激活时将不再激活,并且状态触发器 外部 指定的默认值现在可以正确重新应用。

  • 如果您的应用仅 运行 在 1607 分支(构建 14393) 或更新版本。请注意,这指的是项目属性中指定的最低版本不是目标版本。

  • 如果您的应用程序 运行 在 1511 分支上(构建 10586,则您需要此解决方法 ]) 或 1507 分支(构建 10240),即使它在具有较新构建的设备上也能正常运行。


不幸的是,如果不向 VisualStateGroup 添加默认状态,我无法解决这个问题,使用最小维度为 0 epx 的状态触发器(你不需要任何设置器——你只需要一个状态触发器):

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup>
        <VisualState>
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="1000"/>
            </VisualState.StateTriggers>
            <VisualState.Setters>
                <Setter Target="ShellSplitView.DisplayMode" Value="Inline"/>
                <Setter Target="ShellSplitView.IsPaneOpen" Value="True"/>
            </VisualState.Setters>
        </VisualState>
        <VisualState>
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="600"/>
            </VisualState.StateTriggers>
            <VisualState.Setters>
                <Setter Target="ShellSplitView.DisplayMode" Value="CompactOverlay"/>
            </VisualState.Setters>
        </VisualState>
        <VisualState>
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="0"/>
            </VisualState.StateTriggers>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

至于为什么会出现这种情况,我的猜测是 VisualStateManager 试图在系统主题更新时进入状态,但是由于 none 为初始(或"Default" 或 "Normal") 状态,它必须选择下一个最佳选择。我 仍然 不明白的是,为什么 VisualStateManager 必须在系统主题首先更新时更改视觉状态...不要 ThemeResource 自己刷新?他们为什么要依赖 VisualStateManager?

有趣的是,大多数关于 UWP 中的自适应触发器的第三方教程已经这样做了。目前尚不清楚作者是否意识到这个问题,或者他们添加默认状态仅仅是因为他们在 Silverlight/WPF 时代已经习惯了这样做。但是,根据上面的文档和其他正确的 UWP 行为,这可能是一种边缘情况。