2014年9月19日 星期五

Full View 與 Fill View 的 Layout 切換方法

在 Windows 8.1 上使用 Windows RT 開發的 App 時可以將畫面切為 Full 全螢幕、Fill 3/4 螢幕 與 Snap 320px 寬的尺寸,但在撰寫 Universal Apps 時會發現 Snap 的模式不見了,最窄也僅能讓 App 呈現寬 500px 的尺寸,在 Windows Store App 中移除了 Snap View 這種呈現方式,僅剩下三種,簡易的說明如下。

FullScreenLandscape:全螢幕
FullScreenPortrait:寬度為 500px ~ 螢幕的一半以下
Filled:寬度大於螢幕的一半 ~ 螢幕寬度 - 500px

大致上如果想在這三種不同的尺寸下指定不同的排版樣式,搭配 MVVM 是最節省作業系統資源及開發成本的方式,在 Windows RT 時期常會使用 LayoutAwarePage 來簡單達到效果,在 Store App 時方式差不多,但需要經過小部分改寫才能使用,搜尋了一下實在找不到官方對於 LayoutAwarePage 的介紹,可能暫時得開 Windows RT 的 Template 來參考,另外官方建議在將專案轉至 8.1 時將 LayoutAwarePage 移除的方式在這:API changes for Windows 8.1(XAML)

以下我將 LayoutAwarePage 對於 WindowsSizeChange 時切換 View 的方式集中起來讓 Windows Store App 也可以根據視窗尺寸改變排版方式,這裡注意我使用了 #if WINDOWS_APP 的方式將類別內容全部包起來了,因為我將這個類別放在 Universal Apps 的 Shared Project 內,而這個類別內有太多 Windows Phone SDK 不支援的 API。

public class WindowsLayoutSupportPage : Page
{
#if WINDOWS_APP
    private List<Control> _layoutAwareControls;

    public WindowsLayoutSupportPage()
    {
        if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
        {
            Loaded += OnWindowsLayoutSupportPageLoaded;
            Unloaded += OnWindowsLayoutSupportPageUnloaded;
        }
    }

    void OnWindowsLayoutSupportPageLoaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
    {
        StartLayoutUpdates(sender, e);
    }

    protected void OnWindowsLayoutSupportPageUnloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
    {
        StopLayoutUpdates(sender, e);
    }

    public void StartLayoutUpdates(object sender, RoutedEventArgs e)
    {
        var control = sender as Control;
        if (control == null) return;
        if (this._layoutAwareControls == null)
        {
            SizeChanged += this.OnPageSizeChanged;
            this._layoutAwareControls = new List<Control>();
        }
        this._layoutAwareControls.Add(control);

        VisualStateManager.GoToState(control, DetermineVisualState(ApplicationView.Value), false);
    }

    public void StopLayoutUpdates(object sender, RoutedEventArgs e)
    {
        var control = sender as Control;
        if (control == null || this._layoutAwareControls == null) return;
        this._layoutAwareControls.Remove(control);
        if (this._layoutAwareControls.Count == 0)
        {
            this._layoutAwareControls = null;
            SizeChanged -= this.OnPageSizeChanged;
        }
    }

    protected string DetermineVisualState(ApplicationViewState viewState)
    {
        return viewState.ToString();
    }

    public void InvalidateVisualState()
    {
        if (this._layoutAwareControls != null)
        {
            string visualState = DetermineVisualState(ApplicationView.Value);
            foreach (var layoutAwareControl in this._layoutAwareControls)
            {
                VisualStateManager.GoToState(layoutAwareControl, visualState, false);
            }
        }
    }

    private void OnPageSizeChanged(object sender, SizeChangedEventArgs e)
    {
        InvalidateVisualState();
    }
#endif
}

有了上面這個類別後,我們將需要支援改變 View 的 Page 改為這個類別,即可在最上方所列出來的三種尺寸切換不同的容器,配合同樣的 DataContext 其實增加的負擔不會太大,以下這個例子我讓 MainPage 可以在 FullL、FullP、Fill 三種尺寸下各選擇不同的容器,僅需針對 XAML 做修改即可在改變尺寸時讓視窗內容在藍、綠、紅三個 Grid 做切換

<local:WindowsLayoutSupportPage
    x:Class="UAPractice.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UAPractice"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
   
    <Grid>
        <Grid x:Name="FullLandscapeView"
              Background="#00AED8"
              AutomationProperties.AutomationId="FullLandscapeView">
        </Grid>

        <Grid x:Name="FullPortraitView"
              Background="#99FF99"
              AutomationProperties.AutomationId="FullPortraitView"
              Visibility="Collapsed">
        </Grid>
       
        <Grid x:Name="FillView"
              Background="#FF9999"
              AutomationProperties.AutomationId="FillView"
              Visibility="Collapsed">
        </Grid>
       
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="FullScreenLandscape"/>
                <VisualState x:Name="Filled">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FillView" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FullLandscapeView" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="FullScreenPortrait">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FullPortraitView" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FullLandscapeView" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Grid>
   
</local:WindowsLayoutSupportPage>



沒有留言:

張貼留言