2014年8月16日 星期六

Store App 加入 App Bar

轉換到 Store Apps 的 Windows Phone 8.1 專案,雖然和 Windows 8.1 同樣使用 Windows Runtime SDK,但之前提過,這代表著兩者大部份的 API 相同,並非全部,而 App Bar 剛好就是一個有差異的例子。Phone 8.1 使用 App Bar 的撰寫方式和 Win 8.1 是相似的,但行為概念和舊的 ApplicationBar 是相似的,在這裡一併介紹 Windows 上的 App Bar 與 Phone 上面的 App Bar。

官方文件:Adding app bars (XAML)

首先建立一個 Universal Apps 專案,在 Phone Project 的 MainPage.xaml 中加入 ApplicationBar 會發現這是不可行的,在 Windows Runtime SDK 中沒有這個元件,取而代之的是 BottomAppBar 這個 Page 的 Property,我們在 Windows 及 Phone 的 MainPage.xaml 皆加入以下片段。

<Page.BottomAppBar>
    <CommandBar>
        <AppBarToggleButton Icon="Shuffle" Label="Shuffle"/>
        <AppBarToggleButton Icon="RepeatAll" Label="Repeat"/>
        <AppBarSeparator/>
        <AppBarButton Icon="Back" Label="Back"/>
        <AppBarButton Icon="Stop" Label="Stop"/>
        <AppBarButton Icon="Play" Label="Play"/>
        <AppBarButton Icon="Forward" Label="Forward"/>
        <CommandBar.SecondaryCommands>
            <AppBarButton Icon="Like" Label="Like"/>
            <AppBarButton Icon="Dislike" Label="Dislike"/>
        </CommandBar.SecondaryCommands>
    </CommandBar>
</Page.BottomAppBar>

在兩個 XAML 的預覽頁可以看到相同 Template 卻有不同的表現,這一方面是 Universal Apps 所提供的優點,一方面也是開發者需要額外花力氣注意的部份,執行後會發現 Phone 平台會乎略 AppBarSeparator 這個元件,且按鍵僅會顯示最前面 4 顆,在 SecondaryCommands 的部份會藏在 more 內僅顯示 Label Property 如下圖一,而 Windows 的部份全部都顯示在右側,較特殊的部份在於 SecondaryCommands 顯示在左側如下圖二。



這是在 BottomAppBar 內放 CommandBar 的部份,目前 Phone 的 Store App 僅支援這個方式建立 AppBar,而在 Windows 方面除了 CommandBar 一種 Bar 的樣式之外,還可以選擇使用 AppBar 元件,使用 AppBar 元件後內部可以隨意放置任何的 UI 元件,例如 Grid 或 StackPanel 等容器,再包含其他 Image、Button、Rectangle 等元件,且 Windows 的 XAML 內除了 BottomAppBar 置底的樣式之外,還提供了 TopAppBar 置頂的樣式,簡單的示範 AppBar 的使用。

<Page.TopAppBar>
    <AppBar x:Name="bottomAppBar" IsSticky="True">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <StackPanel x:Name="leftAppBarPanel" Orientation="Horizontal">
                <AppBarButton Label="Save" Icon="Save"/>
                <AppBarButton Label="Discard" Icon="Delete"/>
                <AppBarButton Label="Edit" Icon="Edit"/>
                <AppBarButton Label="Undo" Icon="Undo"/>
                <AppBarButton Label="Redo" Icon="Redo"/>
            </StackPanel>
            <StackPanel x:Name="rightAppBarPanel"
                    Orientation="Horizontal" HorizontalAlignment="Right">
                <AppBarButton Label="Skip Back" Icon="Previous"/>
                <AppBarButton Label="Skip Ahead" Icon="Next"/>
                <AppBarButton Label="Play" Icon="Play"/>
                <AppBarButton Label="Pause" Icon="Pause"/>
                <AppBarButton Label="Stop" Icon="Stop"/>
            </StackPanel>
        </Grid>
    </AppBar>
</Page.TopAppBar>

另一個需要特別為 Windows App 注意的部份在於 Windows 上支援 Snap View 這種模式,最窄可以許可應用程式的寬度至 320 個像素寬,在這種情況,上面的 TopAppBar 就會發生按鍵重疊的情況,官方提到了一個簡單的作法,是在視窗尺寸變小時,將 AppBarButton 的 IsCompact 設為 True 即可讓每個 AppBarButton 的按鍵縮小,方式如下

public MainPage()
{
    this.InitializeComponent();

    // Register for the SizeChanged event.
    this.SizeChanged += MainPage_SizeChanged;
}

void MainPage_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if e.NewSize.Width < 1000)
    {
        // 將所有 AppBarButton 的 IsCompact 設為 True
    }
    else
    {
        // 將所有 AppBarButton 的 IsCompact 設為 False
    }
}

但上面這個方法只能將按鍵之間的距離縮小至一定程度,在按鍵過多的情況,應用程式實際寬度還是可能讓按鍵之間重疊,所以另一個更適合的方式是使用 AppBar 而非使用 CommandBar,並且在 Grid 建立一個以上的列,依螢幕的寬度決定哪些按鍵是否要放到其他列,可以參考 MSDN 上的詳細範例:To move buttons into 2 rows

<AppBar>
    <Grid>
        <Grid.RowDefinitions>
           <RowDefinition Height="Auto"/>
           <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
             
        <StackPanel x:Name="leftAppBarPanel" Orientation="Horizontal">
            <!-- Buttons -- >
        </StackPanel>
             
        <StackPanel x:Name="rightAppBarPanel" Orientation="Horizontal"
                    HorizontalAlignment="Right">
            <!-- Buttons -- >
        </StackPanel>
    </Grid>
</AppBar>

// 在 Page 的 SizeChanged 事件時處理以下動作
private void UpdateBottomAppBarLayout(double newWidth)
{
    if (newWidth < 600)
    {
        ToggleIsCompact(true);
        rightAppBarPanel.SetValue(Grid.RowProperty, 1);
        rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Left);
    }
    else if (newWidth < 1000)
    {
        ToggleIsCompact(true);
        rightAppBarPanel.SetValue(Grid.RowProperty, 0);
        rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Right);
    }
    else
    {
        ToggleIsCompact(false);
        rightAppBarPanel.SetValue(Grid.RowProperty, 0);
        rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Right);
    }
}

另外一個實例上的應用可能會有需要讓每一頁的 App Bar 都提供一樣的功能,這種情況下其實不需要每一頁都撰寫一樣的 App Bar 內容與按鍵的事件處理,可以與 Store Apps Navigation 所提到的 Frame 一起搭配建立 Global 層級的 App Bar,在該 Frame 內做頁面的導覽即可,簡單的撰寫 XAML Code 做示範。

<Page
    x:Class="AppBarPractice.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AppBarPractice"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
 
    <Page.BottomAppBar>
        <AppBar x:Name="bottomAppBar" IsSticky="True">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <StackPanel x:Name="leftAppBarPanel" Orientation="Horizontal">
                    <AppBarButton Label="Save" Icon="Save"/>
                    <AppBarButton Label="Discard" Icon="Delete"/>
                    <AppBarButton Label="Edit" Icon="Edit"/>
                    <AppBarButton Label="Undo" Icon="Undo"/>
                    <AppBarButton Label="Redo" Icon="Redo"/>
                </StackPanel>

                <StackPanel x:Name="rightAppBarPanel"
                        Orientation="Horizontal" HorizontalAlignment="Right">
                    <AppBarButton Label="Skip Back" Icon="Previous"/>
                    <AppBarButton Label="Skip Ahead" Icon="Next"/>
                    <AppBarButton Label="Play" Icon="Play"/>
                    <AppBarButton Label="Pause" Icon="Pause"/>
                    <AppBarButton Label="Stop" Icon="Stop"/>
                </StackPanel>
            </Grid>
        </AppBar>
    </Page.BottomAppBar>

    <!-- 在這個 Frame 裡做 Page 的導覽 -->
    <Frame x:Name="GlobalFrame"/>
</Page>



沒有留言:

張貼留言