首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 8 Store Apps 開發實戰(13):SemanticZoom 控件 — 語義縮放與自定義視圖實作

     編輯:關於.NET

前言

SemanticZoom 是 Windows 8 獨有的導航控件,讓使用者在 「詳細視圖」 與 「概覽視圖」 之間以語義關係無縫切換。典型場景:長清單的字母分組導航、相簿的年份/月份縮覽、新聞分類的主題跳轉。本文將完整示範標準用法,並深入剖析如何透過實作 ISemanticZoomInformation 介面,讓任意控件(如 FlipView)成為可語義縮放的視圖。

一、SemanticZoom 核心架構

屬性/事件說明
ZoomedInView放大視圖:顯示詳細資料(如 GridView、ListView)
ZoomedOutView縮小視圖:顯示概覽/導航項(如分組標題)
IsZoomedInViewActive目前顯示哪個視圖
CanChangeViews是否允許切換
ToggleActiveView()程式碼切換視圖
ViewChangeStarted / ViewChangeCompleted視圖切換動畫事件

二、標準範例:GridView 分組 + SemanticZoom

利用 CollectionViewSource 對資料分組,GridView 內建支援 ISemanticZoomInformation,兩視圖自動關聯。

資料模型

class NavigationModel
{
    public string Title { get; set; }
    public string Url { get; set; }
    public List<NavigationModel> Items { get; set; }  // 子項集合
}

XAML 完整結構

<SemanticZoom x:Name="semanticZoom" HorizontalAlignment="Left">
    <!-- 詳細視圖:分組 GridView -->
    <SemanticZoom.ZoomedInView>
        <GridView x:Name="gridViewDetails"
                  SelectionMode="None" IsSwipeEnabled="false"
                  Padding="120 0 0 0">

            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid Background="#022a56" Width="200" Height="65"
                          Tapped="Grid_Tapped" Tag="{Binding}">
                        <TextBlock Text="{Binding Title}"
                                   FontSize="14.667" Foreground="White"
                                   Padding="5 0" VerticalAlignment="Center"/>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>

            <GridView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Title}"
                                       FontSize="26.67" Height="30" Margin="0 0 0 20"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>

                    <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 Content="{TemplateBinding Content}"
                                                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                                                Margin="{TemplateBinding Padding}"/>
                                                <ItemsControl x:Name="ItemsControl"
                                                              ItemsSource="{Binding GroupItems}"
                                                              Grid.Row="1"/>
                                            </Grid>
                                        </Border>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>

                    <GroupStyle.Panel>
                        <ItemsPanelTemplate>
                            <VariableSizedWrapGrid Orientation="Vertical"
                                                   Margin="0 0 50 0"
                                                   ItemHeight="75"/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </GridView.GroupStyle>

            <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
        </GridView>
    </SemanticZoom.ZoomedInView>

    <!-- 概覽視圖:分組標題 GridView -->
    <SemanticZoom.ZoomedOutView>
        <GridView x:Name="gridViewSummary"
                  HorizontalAlignment="Left" Padding="120 0 0 0">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid Background="#022a56" Width="100" Height="100">
                        <TextBlock Text="{Binding Group.Title}"
                                   Foreground="White" Opacity="0.9"
                                   FontSize="14.667" Margin="5"
                                   TextAlignment="Center"
                                   VerticalAlignment="Center"
                                   HorizontalAlignment="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>

後台資料綁定與導航邏輯

public sealed partial class Index : Page
{
    private double _scrollViewerHorizontalOffset = 0;

    public Index()
    {
        this.InitializeComponent();
        LoadDataAndBind();
        this.NavigationCacheMode = NavigationCacheMode.Required; // 必須緩存頁面
        gridViewDetails.Loaded += gridViewDetails_Loaded;
    }

    private void LoadDataAndBind()
    {
        var root = XElement.Load("SiteMap.xml");
        var items = LoadData(root); // 遞迴解析 XML 產生巢狀 NavigationModel

        var groupedData = new CollectionViewSource
        {
            IsSourceGrouped = true,
            Source = items,
            ItemsPath = new PropertyPath("Items") // 指定子集合屬性
        };

        // 詳細視圖綁定完整分組檢視
        gridViewDetails.ItemsSource = groupedData.View;
        // 概覽視圖綁定分組集合
        gridViewSummary.ItemsSource = groupedData.View.CollectionGroups;
    }

    private List<NavigationModel> LoadData(XElement root)
    {
        if (root == null) return null;
        return (from n in root.Elements("node")
                select new NavigationModel
                {
                    Title = (string)n.Attribute("title"),
                    Url = (string)n.Attribute("url"),
                    Items = LoadData(n)
                }).ToList();
    }

    // 導航離開前保存水平捲動位置
    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        var sv = Helper.GetVisualChild<ScrollViewer>(gridViewDetails);
        _scrollViewerHorizontalOffset = sv.HorizontalOffset;
    }

    // 導航返回時恢復捲動位置
    void gridViewDetails_Loaded(object sender, RoutedEventArgs e)
    {
        var sv = Helper.GetVisualChild<ScrollViewer>(gridViewDetails);
        sv.ScrollToHorizontalOffset(_scrollViewerHorizontalOffset);
    }

    // 項目點擊導航到對應 Demo 頁
    private void Grid_Tapped(object sender, TappedRoutedEventArgs e)
    {
        var model = (NavigationModel)((FrameworkElement)sender).Tag;
        MainPage.Current.SubTitle = model.Title;
        MainPage.Current.Container.Navigate(Type.GetType(model.Url));
    }
}

三、進階:自定義視圖實作 ISemanticZoomInformation

GridView/ListView 內建實作 ISemanticZoomInformation,但若想用 FlipView、ListBox 或自定義控件作為視圖,必須自行實作該介面。

介面關鍵方法

方法呼叫時機核心任務
StartViewChangeTo(source, destination)ZoomedOut → ZoomedIn 開始從 source.Item 取得被選分組,將其子項指派給 destination.Item
MakeVisible(item)視圖切換完成後重設視圖資料源、捲動到對應項目
StartViewChangeFrom / CompleteViewChangeFromZoomedIn → ZoomedOut通常無需特殊處理
InitializeViewChange / CompleteViewChange切換初始化/完成通常留空

MyFlipView.cs — 可語義縮放的 FlipView

using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;

namespace XamlDemo.Controls.SemanticZoom
{
    public class MyFlipView : FlipView, ISemanticZoomInformation
    {
        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 SemanticZoom SemanticZoomOwner { get; set; }

        public void StartViewChangeFrom(SemanticZoomLocation source, SemanticZoomLocation destination) { }

        // 關鍵:ZoomedOutView 選中項 → 傳遞其子項給 ZoomedInView
        public void StartViewChangeTo(SemanticZoomLocation source, SemanticZoomLocation destination)
        {
            // source.Item 為被選中的 NavigationModel(父項)
            var model = source.Item as NavigationModel;
            // 將父項的所有子項傳遞給 destination
            destination.Item = model.Items;
        }

        // 切換完成後:重設 FlipView 的 ItemsSource
        public void MakeVisible(SemanticZoomLocation item)
        {
            this.ItemsSource = item.Item; // item.Item 即為 model.Items
        }
    }
}

CustomView.xaml — 使用自定義 FlipView

<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 x:Name="gridView" HorizontalAlignment="Left">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid Background="#022a56" Width="100" Height="100">
                        <TextBlock Text="{Binding Title}"
                                   Foreground="White" Opacity="0.9"
                                   FontSize="14.667" Margin="5"
                                   TextAlignment="Center"
                                   VerticalAlignment="Center"
                                   HorizontalAlignment="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>

CustomView.xaml.cs — 綁定概覽視圖資料

public sealed partial class CustomView : Page
{
    public CustomView()
    {
        this.InitializeComponent();
        var root = XElement.Load("SiteMap.xml");
        gridView.ItemsSource = LoadData(root); // 直接綁定巢狀資料
    }

    private List<NavigationModel> LoadData(XElement root)
    {
        if (root == null) return null;
        return (from n in root.Elements("node")
                select new NavigationModel
                {
                    Title = (string)n.Attribute("title"),
                    Url = (string)n.Attribute("url"),
                    Items = LoadData(n)
                }).ToList();
    }
}

public class NavigationModel
{
    public string Title { get; set; }
    public string Url { get; set; }
    public List<NavigationModel> Items { get; set; }
}

四、運作流程圖解

  1. 使用者在 ZoomedOutView (GridView) 點選某分組項目
  2. SemanticZoom 呼叫 ZoomedInView (MyFlipView).StartViewChangeTo(source, destination)
  3. MyFlipView 從 source.Item 取得被選 NavigationModel,將其 Items 子集合指派給 destination.Item
  4. 動畫過渡至 ZoomedInView
  5. 動畫完成後呼叫 MyFlipView.MakeVisible(item)
  6. MyFlipView 將 ItemsSource = item.Item,顯示該分組下的所有子項

五、小結

SemanticZoom 以 語義關係 連結兩個視圖,核心在於:

  1. 標準用法:CollectionViewSource + GridView/ListView 內建 ISemanticZoomInformation,零程式碼實現關聯切換
  2. 自定義視圖:實作 ISemanticZoomInformation,重點在 StartViewChangeTo 傳遞資料與 MakeVisible 重設資料源
  3. 資料模型需巢狀結構(父項含 Items 子集合),配合 ItemsPath 指定子屬性名稱

掌握此機制,即可為任何集合控件賦予語義縮放能力,打造極具 Windows 風格的導航體驗。


原始碼下載:https://files.cnblogs.com/webabcd/Windows8.rar

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved