重新想象 Windows 8 Store Apps (13) - 控件之 SemanticZoom
作者:webabcd
介绍
重新想象 Windows 8 Store Apps 之 SemanticZoom
- 演示 SemanticZoom 的应用
- 通过 ISemanticZoomInformation 接口实现自定义 SemanticZoom 的 View
示例
1、演示 SemanticZoom 的应用
Index.xaml
<Page
x:Class="XamlDemo.Index"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<SemanticZoom x:Name="semanticZoom" HorizontalAlignment="Left">
<!--
放大后的视图,详细数据
-->
<SemanticZoom.ZoomedInView>
<GridView x:Name="gridViewDetails" SelectionMode="None" IsSwipeEnabled="false" Padding="120 0 0 0">
<!--分组后,details 的数据模板-->
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="#022a56" Width="200" Height="65" Tapped="Grid_Tapped_1" Tag="{Binding}">
<TextBlock TextWrapping="Wrap" FontSize="14.667" Foreground="#ffffff" Padding="5 0"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{Binding Title}"/>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<!--分组的样式-->
<GridView.GroupStyle>
<GroupStyle>
<!--分组后,header 的数据模板-->
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" FontSize="26.67" Height="30" Margin="0 0 0 20" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<!--分组后,每组数据(包括 header 和 details)的样式-->
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}"/>
<!--
每组数据的 details 数据源来自 ICollectionViewGroup.GroupItems
-->
<ItemsControl x:Name="ItemsControl" ItemsSource="{Binding GroupItems}" Grid.Row="1"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
<!--分组后,每组数据(包括 header 和 details)的 panel-->
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" Margin="0 0 50 0" ItemHeight="75" Loaded="VariableSizedWrapGrid_Loaded_1" />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
<!--整体数据(一组数据算一个元素)的 panel-->
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</SemanticZoom.ZoomedInView>
<!--
缩小后的视图,概述数据
-->
<SemanticZoom.ZoomedOutView>
<GridView Name="gridViewSummary" HorizontalAlignment="Left" Padding="120 0 0 0">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="#022a56" Width="100" Height="100">
<!--
每组数据的 header 数据源来自 ICollectionViewGroup.Group
-->
<TextBlock Text="{Binding Group.Title}" Foreground="#ffffff" Opacity="0.9" FontSize="14.667" Margin="5" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid MaximumRowsOrColumns="8" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="5 5 5 30" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</Grid>
</Page>
Index.xaml.cs
/*
* 本页为此 app 的导航页,同时用于演示:
* 1、ItemsControl 控件如何分组
* 2、SemanticZoom 控件的应用
*
*
* SemanticZoom - 用于关联两个有语义关系的视图
* ZoomedInView - 放大后的视图,详细数据
* ZoomedOutView - 缩小后的视图,概述数据
* IsZoomedInViewActive - ZoomedInView 是否为当前正在显示的视图
* CanChangeViews - 是否可在两个视图间切换
* IsZoomOutButtonEnabled - 是否显示用于切换视图的按钮
* ToggleActiveView() - 切换视图
* ViewChangeStarted - 视图开始切换时触发的事件
* ViewChangeCompleted - 视图切换完成时触发的事件
*
*
* ItemsControl - 一个用于呈现集合数据的控件(GridView, ListView, FlipView, ListBox 等均直接或间接地继承了 ItemsControl)
* ItemsControl 控件提供了分组功能
*
* CollectionViewSource - 对集合数据启用分组支持
* Source - 数据源
* View - 获取视图对象,返回一个实现了 ICollectionView 接口的对象
* IsSourceGrouped - 数据源是否是一个被分组的数据
* ItemsPath - 数据源中,子数据集合的属性名称
*
* ICollectionView - 支持数据分组
* CollectionGroups - 组数据集合
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using XamlDemo.Common;
namespace XamlDemo
{
public sealed partial class Index : Page
{
// 本页所用到的 GridView 的水平方向上的滚动值
private double _scrollViewerHorizontalOffset = 0;
public Index()
{
this.InitializeComponent();
XElement root = XElement.Load("SiteMap.xml");
var items = LoadData(root);
// 构造数据源
CollectionViewSource groupedNavigationData = new CollectionViewSource();
groupedNavigationData.IsSourceGrouped = true;
groupedNavigationData.Source = items;
groupedNavigationData.ItemsPath = new PropertyPath("Items");
// 绑定数据
gridViewDetails.ItemsSource = groupedNavigationData.View; // 全部数据
gridViewSummary.ItemsSource = groupedNavigationData.View.CollectionGroups; // 关联的组数据集合
// 必须缓存此页
this.NavigationCacheMode = NavigationCacheMode.Required;
gridViewDetails.Loaded += gridViewDetails_Loaded;
}
// 导航到其它页之前,保存本页所用到的 GridView 的水平方向上的滚动值
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
var scrollViewer = Helper.GetVisualChild<ScrollViewer>(gridViewDetails);
_scrollViewerHorizontalOffset = scrollViewer.HorizontalOffset;
}
// GridView 加载之后,指定其水平方向上的滚动值
void gridViewDetails_Loaded(object sender, RoutedEventArgs e)
{
var scrollViewer = Helper.GetVisualChild<ScrollViewer>(gridViewDetails);
scrollViewer.ScrollToHorizontalOffset(_scrollViewerHorizontalOffset);
}
// 获取数据
private List<NavigationModel> LoadData(XElement root)
{
if (root == null)
return null;
var items = from n in root.Elements("node")
select new NavigationModel
{
Title = (string)n.Attribute("title"),
Url = (string)n.Attribute("url"),
Items = LoadData(n)
};
return items.ToList();
}
// 导航到指定的 Demo 页
private void Grid_Tapped_1(object sender, TappedRoutedEventArgs e)
{
var model = (NavigationModel)(sender as Grid).Tag;
MainPage.Current.SubTitle = model.Title;
MainPage.Current.Container.Navigate(Type.GetType(model.Url));
}
// 根据屏幕的 height 调整 VariableSizedWrapGrid 的 MaximumRowsOrColumns
// 本例会自动 wrap ,所以不需要以下逻辑,在需要的场景下可以用之
private void VariableSizedWrapGrid_Loaded_1(object sender, RoutedEventArgs e)
{
/*
var screenHeight = Window.Current.Bounds.Height; // CoreWindow.GetForCurrentThread().Bounds.Height
var vswg = sender as VariableSizedWrapGrid;
vswg.MaximumRowsOrColumns = (int)((screenHeight - 200) / 75);
*/
}
class NavigationModel
{
public string Title { get; set; }
public string Url { get; set; }
public List<NavigationModel> Items { get; set; }
}
}
}
2、演示如何自定义 SemanticZoom 视图
SemanticZoom/MyFlipView.cs
/*
* 开发一个实现了 ISemanticZoomInformation 接口的自定义 FlipView
*/
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
namespace XamlDemo.Controls.SemanticZoom
{
public class MyFlipView : FlipView, ISemanticZoomInformation
{
public MyFlipView()
: base()
{
}
public void CompleteViewChange() { }
public void CompleteViewChangeFrom(SemanticZoomLocation source, SemanticZoomLocation destination) { }
public void CompleteViewChangeTo(SemanticZoomLocation source, SemanticZoomLocation destination) { }
public void InitializeViewChange() { }
public bool IsActiveView { get; set; }
public bool IsZoomedInView { get; set; }
public Windows.UI.Xaml.Controls.SemanticZoom SemanticZoomOwner { get; set; }
public void StartViewChangeFrom(SemanticZoomLocation source, SemanticZoomLocation destination) { }
/// <summary>
/// ZoomedOutView -> ZoomedInView 时触发的事件
/// </summary>
/// <param name="source">在 ZoomedOutView 时被选中的数据</param>
/// <param name="destination">需要传递给 ZoomedInView 的数据</param>
public void StartViewChangeTo(SemanticZoomLocation source, SemanticZoomLocation destination)
{
/*
* 注:
* GridView 和 ListView 均实现了 ISemanticZoomInformation 接口
* 参见本 app 的关于 SemanticZoom 的 Demo,通过 CollectionViewSource 绑定数据即可使 SemanticZoom 中的两个视图进行有关联地切换
* 此时此处的 source.Item 便为一个 ICollectionViewGroup 类型的数据,其有两个属性:Group 和 GroupItems
*/
// 获取在 ZoomedOutView 中被选中的项,即被选中的父亲
NavigationModel model = source.Item as NavigationModel;
// 将此父亲的所有子数据传递给 ZoomedInView,接下来会执行 MakeVisible() 方法
destination.Item = model.Items;
}
/// <summary>
/// 开始 ZoomedOutView -> ZoomedInView 之后,会调用此方法
/// 一般在此处重整 ZoomedInView 的数据源,或者滚动 ZoomedInView 中的内容到指定的项以对应 ZoomedOutView 中被选中的数据
/// </summary>
/// <param name="item">由 StartViewChangeTo() 方法传递给 ZoomedInView 的数据</param>
public void MakeVisible(SemanticZoomLocation item)
{
// 将 FlipView 的数据源指定为被选中的父亲的所有子数据
this.ItemsSource = item.Item;
}
}
}
SemanticZoom/CustomView.xaml
<Page
x:Class="XamlDemo.Controls.SemanticZoom.CustomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo.Controls.SemanticZoom"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<SemanticZoom x:Name="semanticZoom" IsZoomedInViewActive="False" Margin="120 0 0 0">
<!--
放大后的视图,详细数据
-->
<SemanticZoom.ZoomedInView>
<local:MyFlipView x:Name="flipView" Width="600" Height="300" HorizontalAlignment="Left">
<FlipView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" FontSize="26.667" />
</DataTemplate>
</FlipView.ItemTemplate>
<FlipView.ItemContainerStyle>
<Style TargetType="FlipViewItem">
<Setter Property="Background" Value="Blue" />
</Style>
</FlipView.ItemContainerStyle>
</local:MyFlipView>
</SemanticZoom.ZoomedInView>
<!--
缩小后的视图,概述数据
-->
<SemanticZoom.ZoomedOutView>
<GridView Name="gridView" HorizontalAlignment="Left">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="#022a56" Width="100" Height="100">
<TextBlock Text="{Binding Title}" Foreground="#ffffff" Opacity="0.9" FontSize="14.667" Margin="5" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid MaximumRowsOrColumns="8" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="5 5 5 30" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</Grid>
</Page>
SemanticZoom/CustomView.xaml.cs
/*
* 演示如何自定义 SemanticZoom 视图
* 演示步骤:
* 1、在 ZoomedOutView 视图中的 GridView 中选中某一项
* 2、展开 ZoomedInView 视图后,其内的 FlipView 显示之前被选中项的所有子数据(需要开发一个实现了 ISemanticZoomInformation 接口的自定义 FlipView,参见 MyFlipView.cs)
*
* 注:
* GridView 和 ListView 均实现了 ISemanticZoomInformation 接口,所以可以在 SemanticZoom 的两个视图间有关联地切换
* 要让其它控件也实现类似的功能,就必须使其实现 ISemanticZoomInformation 接口
*/
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Windows.UI.Xaml.Controls;
namespace XamlDemo.Controls.SemanticZoom
{
public sealed partial class CustomView : Page
{
public CustomView()
{
this.InitializeComponent();
XElement root = XElement.Load("SiteMap.xml");
var items = LoadData(root);
// 绑定数据
gridView.ItemsSource = items;
}
// 获取数据
private List<NavigationModel> LoadData(XElement root)
{
if (root == null)
return null;
var items = from n in root.Elements("node")
select new NavigationModel
{
Title = (string)n.Attribute("title"),
Url = (string)n.Attribute("url"),
Items = LoadData(n)
};
return items.ToList();
}
}
public class NavigationModel
{
public string Title { get; set; }
public string Url { get; set; }
public List<NavigationModel> Items { get; set; }
}
}
OK
[源码下载]