かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Universal Windows appで同じページで電話とパソコンの見た目を切り替える方法

といったらVSMしかないでしょう。強引に#if~#endifで。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace App31
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private MainPageViewModel viewModel = new MainPageViewModel();
        public MainPage()
        {
            this.InitializeComponent();
            this.DataContext = this.viewModel;

            // VSMの切り替え
            Window.Current.CoreWindow.SizeChanged += (_, __) => this.ViewStateChange();
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            // VSMの切り替え
            this.ViewStateChange();

            // ダミーデータ突っ込み
            foreach (var num in Enumerable.Range(0, 100))
            {
                this.viewModel.Numbers.Add(num);
            }
        }

        private void ViewStateChange()
        {
            // 電話とパソコンでVSM切り替え
#if WINDOWS_APP
            VisualStateManager.GoToState(this, "WindowsState", true);
#else
            VisualStateManager.GoToState(this, "PhoneState", true);
#endif
        }
    }
}

あとは、Styleなりなんなりを切り替えるページを作りこむ。

<Page
    x:Class="App31.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App31"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DataContext="{d:DesignInstance local:MainPageViewModel, IsDesignTimeCreatable=True}" Loaded="Page_Loaded">
    <Page.Resources>
        <DataTemplate x:Key="NumberDataTemplate">
            <Grid>
                <Border BorderThickness="1" Width="250" Height="250" Background="#FF06007A">
                    <TextBlock TextWrapping="Wrap" Text="{Binding Mode=OneWay}" Style="{StaticResource HeaderTextBlockStyle}" VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Brush Script MT"/>
                </Border>
            </Grid>
        </DataTemplate>
        <Style x:Key="WindowStyle" TargetType="GridView">
            <Setter Property="ItemTemplate" Value="{StaticResource NumberDataTemplate}"/>
            <Setter Property="Padding" Value="120, 10, 0, 8" />
         </Style>
        <DataTemplate x:Key="PhoneNumberDataTemplate">
            <Grid Margin="5">
                <Border BorderThickness="1" Width="75" Height="75" Background="#FF000F53">
                    <TextBlock TextWrapping="Wrap" Text="{Binding Mode=OneWay}" Style="{StaticResource HeaderTextBlockStyle}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                </Border>
            </Grid>
        </DataTemplate>
        <Style x:Key="PhoneStyle" TargetType="GridView">
            <Setter Property="ItemTemplate" Value="{StaticResource PhoneNumberDataTemplate}"/>
            <Setter Property="Padding" Value="10, 10" />
        </Style>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="VisualStateGroup">
                <VisualState x:Name="WindowsState"/>
                <VisualState x:Name="PhoneState">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="appBarButton">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <Visibility>Collapsed</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.Style)" Storyboard.TargetName="gridView">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneStyle}">
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <AppBarButton x:Name="appBarButton" HorizontalAlignment="Stretch" Icon="Back" VerticalAlignment="Bottom" IsCompact="True" Margin="20,20,0,0"/>
            <TextBlock TextWrapping="Wrap" Text="GridView sample application" Style="{StaticResource HeaderTextBlockStyle}" Margin="20,30,0,0" FontFamily="Global User Interface"/>
        </StackPanel>
        <GridView x:Name="gridView" Grid.Row="1" ItemsSource="{Binding Numbers}" Margin="0,10,0,0" Style="{StaticResource WindowStyle}"/>

    </Grid>
</Page>

実行するとこんな感じのページになる。作りこめばいけなくないですね。

f:id:okazuki:20140413235142j:plain

f:id:okazuki:20140413235225j:plain

ただし

別々に作ったほうがページのコードの見通しがいいこともあるし、そもそも電話では使えないコントロールとかもあったりするので、ページまで同じコードにするかどうかはよくよく検討しよう。