SemanticZoom

2019/03/13

150

什么是SemanticZoom

SemanticZoom(语义式缩放控件),语义式缩放使用户能够在相同内容的两个不同视图之间切换,以便他们可以在大型分组数据集中快速导航。

Windows10 开始菜单,Windows Phone 右侧菜单均使用了SemanticZoom

sample

使用方法

由于微软官方文档XAML Control Gallery的示例代码不够详细,需要查看XAML Control Gallery的源代码才能看到详细的细节,所以记录下来。

SemanticZoom的核心

SemanticZoom的核心其实是带标题的 ListView (Listview with Grouped Headers),Listview with Grouped Headers 的使用方法:

<ContentDialog.Resources>
    <CollectionViewSource x:Name="SampleCVS" Source="{x:Bind Items}" IsSourceGrouped="True"/>
</ContentDialog.Resources>

<ListView ItemsSource="{x:Bind SampleCVS.View, Mode=OneWay}" Height="400">
    <ListView.GroupStyle>
        <GroupStyle>
            <GroupStyle.HeaderTemplate>
                <DataTemplate x:DataType="local:SampleItemGroup">
                    <TextBlock Text="{x:Bind Key}" Style="{ThemeResource TitleTextBlockStyle}"/>
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
        </GroupStyle>
    </ListView.GroupStyle>
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <ItemsStackPanel AreStickyGroupHeadersEnabled="False"/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:SampleItem">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{x:Bind FirstName}"/>
                <TextBlock Text="{x:Bind LastName}"/>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
public sealed partial class FileShareDialg : ContentDialog
{
    public FileShareDialg()
    {
        InitializeComponent();
        var list = new List<SampleItem>
        {
            new SampleItem { FirstName = "郭", LastName = "靖" },
            new SampleItem { FirstName = "郭", LastName = "芙" },
            new SampleItem { FirstName = "黄", LastName = "蓉" },
            new SampleItem { FirstName = "黄", LastName = "药师" }
        };
        Items = list.GroupBy(s => s.FirstName)
            .Select(s => new SampleItemGroup(s)
            {
                Key = s.Key
            })
            .ToList();
    }

    List<SampleItemGroup> Items { get; }

    private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
    {
        if (e.IsSourceZoomedInView == false)
        {
            e.DestinationItem.Item = e.SourceItem.Item;
        }
    }
}

class SampleItem
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class SampleItemGroup: List<object>
{
    public SampleItemGroup(IEnumerable<object> items) : base(items) { }

    public string Key { get; set; }
}

如果 Listview with Grouped Headers 能够正常工作,那么 SemanticZoom 功能将水到渠成。以下代码为 SemanticZoom 使用代码,与 Listview with Grouped Headers 使用相同的 CodeBehind

<SemanticZoom Height="500" ViewChangeStarted="SemanticZoom_ViewChangeStarted">
    <SemanticZoom.ZoomedInView>
        <GridView ItemsSource="{x:Bind SampleCVS.View, Mode=OneWay}" 
                  ScrollViewer.IsHorizontalScrollChainingEnabled="False" 
                  SelectionMode="None">
            <GridView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate x:DataType="local:SampleItemGroup">
                            <TextBlock Text="{x:Bind Key}" Style="{ThemeResource TitleTextBlockStyle}"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </GridView.GroupStyle>
            <GridView.ItemTemplate>
                <DataTemplate x:DataType="local:SampleItem">
                    <StackPanel Orientation="Horizontal" Margin="12,6,0,6">
                        <TextBlock Text="{x:Bind FirstName}"/>
                        <TextBlock Text="{x:Bind LastName}"/>
                    </StackPanel>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </SemanticZoom.ZoomedInView>

    <SemanticZoom.ZoomedOutView>
        <ListView ItemsSource="{x:Bind SampleCVS.View.CollectionGroups}"
                  ScrollViewer.IsHorizontalScrollChainingEnabled="False"
                  SelectionMode="None">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="wuxdata:ICollectionViewGroup">
                    <TextBlock Text="{x:Bind Group.(local:SampleItemGroup.Key)}"
                               Style="{StaticResource SubtitleTextBlockStyle}"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </SemanticZoom.ZoomedOutView>
</SemanticZoom>

同步视图

放大视图和缩小视图应该同步,因此如果用户在缩小视图中选择某个组,则此相同组的详细信息应该显示在放大视图中。你可以使用 CollectionViewSource 或添加代码以同步视图。

绑定到同一 CollectionViewSource 的所有控件始终具有相同的当前项。 如果这两个视图使用同一 CollectionViewSource 作为它们的数据源,CollectionViewSource 将自动同步视图。 有关详细信息,请参阅 CollectionViewSource

如果你不使用 CollectionViewSource 同步视图,则应该处理 ViewChangeStarted 事件并在事件处理程序中同步项目,如下所示。

<SemanticZoom x:Name="semanticZoom" ViewChangeStarted="SemanticZoom_ViewChangeStarted">
private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
{
    if (e.IsSourceZoomedInView == false)
    {
        e.DestinationItem.Item = e.SourceItem.Item;
    }
}

建议

评论