本文的样例来自于Windows-universal-samples的XamlListView中的MasterDetailSelection。
Master/details pattern是一个很经典的应用程序结构布局,所以在UWP的设计指南里面也有重点描述的。
此示例由两个Page构成,分别是MasterDetailSelection和DetailPage。两者应用在不同的屏幕宽度,屏幕宽的时候用前者,屏幕窄的时候用后者。
为了依据屏幕宽度改变显示页面,MasterDetailSelection中定义了一个VisualStateGroup,名为PageSizeStatesGroup。并在其中创建了两个VisualState,分别为WideState和NarrowState。在WideState的情况下显示MasterDetailSelection,在NarrowState的情况下显示DetailPage。WideState要求屏幕宽度至少为720。
窄屏的情况下,内容显示受限。精简的MasterDetailSelection作为导航,而DetailPage作为内容页面。DetailPage使用了下面的Transitions,以便能够返回上一级的MasterDetailSelection:
<Page.Transitions>
<TransitionCollection>
<NavigationThemeTransition />
</TransitionCollection>
</Page.Transitions>
MasterDetailSelection还定义了一个AppBar,上有若干按键。每个按键要根据目前用户操作的状态来决定隐藏还是显示,于是MasterDetailSelection还定义了另一个VisualStateGroup,名为MasterDetailsStatesGroup,用于控制这些按键的显示。MasterDetailsStatesGroup共有四种状态:MasterState、MasterDetailsState、ExtendedSelectionState和MultipleSelectionState。
上面这四种不同的状态同时也控制着ListView的显示模式。比如在MultipleSelectionState的时候,ListView的状态为:
<Setter Target="MasterListView.SelectionMode" Value="Multiple" />
<Setter Target="MasterListView.IsItemClickEnabled" Value="False" />
ListView的左边会出现复选框,可以让用户勾选多个选项。
在MasterDetailSelection页面中,页面的内容是通过ContentPresenter来显示的:
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Column="1"
Grid.RowSpan="2"
BorderThickness="1,0,0,0"
Padding="24,0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate x:DataType="data:Contact">
<StackPanel>
<TextBlock Style="{ThemeResource TitleTextBlockStyle}"
Margin="0,8"
Text="{x:Bind Name}"/>
<TextBlock Style="{ThemeResource BodyTextBlockStyle}"
Margin="0,9"
Text="{x:Bind Position}" />
<TextBlock Style="{ThemeResource BodyTextBlockStyle}"
Margin="0,9"
Text="{x:Bind PhoneNumber}" />
<TextBlock Style="{ThemeResource BodyTextBlockStyle}"
Margin="0,9"
TextWrapping="WrapWholeWords"
Text="{x:Bind Biography}" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.ContentTransitions>
<TransitionCollection/>
</ContentPresenter.ContentTransitions>
</ContentPresenter>
可以看到此ContentPresenter的ContentTemplate设置为一个DataTemplate 。这个DataTemplate的内容和DetailPage其实是一致的,稍微显得有点重复,或许可以独立出来,变成一个UserControl,以便共享代码。
上面的DataTemplate 有点函数式编程的味道,如果把它看成一个函数,它的输出是模板的内容。那么将其赋值给ContentPresenter的ContentTemplate,就相当于返回一个函数,当需要的时候调用这个函数,然后输出相应的内容。
小结:此示例中使用两个页面来适应宽窄屏,导致两个页面之间需要一些交互代码。比如两个页面都必须有逻辑去侦测窗口大小,当窗口大小改变至条件合适时,切换到另一个页面。并且切换过程中可能需要携带一些参数。比如当DetailPage删除了当前页面时,就必须切换到MasterDetailSelection,并且告知当前页面被删除这个状态。
(完)