我有一个使用 AdaptiveTrigger
来适应不同设备外形的用户界面.特别是,我有一个 SplitView
- 当最小窗口宽度分别为 1000 epx 和 600 epx 时,具有两个状态触发器的基于 shell 菜单。 600 epx 状态触发器可以在某些横向模式下的移动设备上触发,具体取决于它们的外形和比例因子,这是预期的行为。
但是,我注意到在桌面上,只要我更改主题设置(例如在暗模式和亮模式之间切换),600 epx 状态触发器就会在后台触发(将 ShellSplitView.DisplayMode
从 Overlay
更改为 CompactOverlay
) ,更改强调色,或切换高对比度模式;在移动模拟器和设备上,当我转到“设置”应用程序、更改主题设置并返回我的应用程序时,它就会触发。最特别的是,这只发生在默认状态下,并且只有在我通过调整桌面上的窗口大小或旋转移动模拟器或设备来触发任何状态触发器之后。返回应用程序后再次调整大小/旋转会使问题消失。我以前在任何其他应用程序中都没有看到这一点,包括 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 并运行该项目来自行重现该问题。
考虑到我所做的只是更改主题设置,我不明白为什么任何状态触发器都会触发。事实上,根据 this guide在 MSDN 上,默认值反射(reflect)在
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 isVertical
. When the window width is >= 720 effective pixels, theVisualState
change is triggered, and theStackPanel
orientation is changed toHorizontal
.
......这共同强化了我的观点 - 当我更改主题设置时窗口宽度不会改变,所以我不明白为什么状态触发器会在不应该启动时启动,为什么只有在我已经通过调整窗口大小或旋转设备触发状态更改。
那么为什么会发生这种情况,我该如何解决呢?
请您参考如下方法:
此问题似乎已在所有设备系列的 Windows 10 周年更新中得到修复。状态触发器在不应该激活时将不再激活,并且现在可以正确重新应用在状态触发器之外指定的默认值。
不幸的是,如果不向
VisualStateGroup
添加默认状态,我就无法解决这个问题。无论如何,使用最小维度为 0 epx 的状态触发器(您不需要任何 setter - 您只需要一个状态触发器):
<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
正在尝试在系统主题更新时进入一个状态,但由于没有为初始(或“默认”或“正常”)状态定义,它必须选择下一个最佳选择。我仍然不明白,为什么
VisualStateManager
首先更新系统主题时必须更改视觉状态...不要
ThemeResource
自己刷新?为什么他们会依赖
VisualStateManager
?
有趣的是,大多数关于 UWP 中自适应触发器的第三方教程已经这样做了。尚不清楚作者是否意识到了这个问题,或者他们是否只是因为他们在 Silverlight/WPF 时代习惯于这样做而添加了默认状态。但是,根据上面的文档和其他正确的 UWP 行为,这可能是一个极端情况。