Xamarin の FormsGallery を XAML で書き直してみる

Xamarin.Forms のサンプルに FormsGallery があります。Xamarin.Forms で使っているひと通りのコントロールが載っているので、どんなものがあるのか見るのに便利なのです。ただ、中のコードは、C# でひとつひとつのコントロールを作るスタイルになっているので、XAML で作る場合にはどうするのか、いまひとつ解りづらい…ので、コンバートしてみました。

https://github.com/moonmile/XFormsGallery

ところどころ未完成なところがあるのですが、ひとまず1日で出来上がったところまで。
共通プロジェクトは PCL を使っています。XAML を素直に共通化するだけであれば PCL で良いでしょう。WinStore/WinPhone のユニバーサルプロジェクトのようにコードビハイドに手を加える場合には、共有プロジェクトを使うと良いかと。

■注意

現時点の Xamarin.Formsのバージョンは「1.1.1.6206」になっているのですが、Visual Stuido 2013 に含まれているテンプレートでは、「1.0.6186」になっています。ほとんど変わらないですが TableView の概観がちょっと違うので、Xamarin の Sample のように、最新のバージョンに合わせています。プロジェクトごとに NuGet で最新にしてください。

以下、ざっと XAML にするときに手順を書き下しておきます。

■App.cs を書き換える

プロジェクトテンプレートで作ると、PCL プロジェクトの App.cs にページを作るコードがあります。ここをごっそり削って、Page クラスのインスタンスを返します。ただし、サンプルの場合には特殊でナビゲーションを使っています。この場合は特別に NavigationPage のインスタンスを作成します。

public class App
{
    public static Page GetMainPage()
    {
        return new NavigationPage(new HomePage());
    }
}

■簡単な LabelDemoPage を作る

ラベルだけが表示されるページはこんな感じです。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
					   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
					   x:Class="XFormsGallery.LabelDemoPage">
	<ContentPage.Padding>
		<OnPlatform x:TypeArguments="Thickness">
			<OnPlatform.iOS>10,20,10,5</OnPlatform.iOS>
			<OnPlatform.Android>10,0,10,5</OnPlatform.Android>
			<OnPlatform.WinPhone>10,0,10,5</OnPlatform.WinPhone>
		</OnPlatform>
	</ContentPage.Padding>
	<StackLayout>
		<Label Text="Label" Font="50" HorizontalOptions="Center"></Label>
		<Label Font="Large" VerticalOptions="CenterAndExpand">
			<Label.Text>Xamarin.Forms is a cross-platform natively backed UI toolkit abstraction that allows developers to easily create user interfaces that can be shared across Android, iOS, and Windows Phone.</Label.Text>
		</Label>
	</StackLayout>						
</ContentPage>

OnPlatform のところは、OSによってコードを切り替えらるところで、元のサンプルの Device.OnPlatform にあたります。Top だけ切り替えられるような気もするのですが、うまくいかなかったので Thickness まるごと切り替えます。iOS だけ 20 ドット広げてあるのは、iPhone のステータスバー用です。ただし、ナビゲーターの場合は自動でステータスバーに対応しているらしく、これはあまり意味がありません。全画面表示するページを作るときに使う技ですね。

元ネタの LabelDemoPage を見ると、次のようになるので、解りやすいような解りにくいような。

    class LabelDemoPage : ContentPage
    {
        public LabelDemoPage()
        {
            Label header = new Label
            {
                Text = "Label",
                Font = Font.BoldSystemFontOfSize(50),
                HorizontalOptions = LayoutOptions.Center
            };

            Label label = new Label
            {
                Text =
                    "Xamarin.Forms is a cross-platform natively " +
                    "backed UI toolkit abstraction that allows " +
                    "developers to easily create user interfaces " +
                    "that can be shared across Android, iOS, and " +
                    "Windows Phone.",

                Font = Font.SystemFontOfSize(NamedSize.Large),
                VerticalOptions = LayoutOptions.CenterAndExpand
            };

            // Accomodate iPhone status bar.
            this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);

            // Build the page.
            this.Content = new StackLayout
            {
                Children = 
                {
                    header,
                    label
                }
            };
        }
    }

XAML 自体は、Xamarin Studio 上で手書きをします。Visual Studio 上でも書けないことはないのですが、タグのコード補完が効かないので、非常にやりにくいです。本当はデザイナが欲しいところでしょうが、スマートフォンのような小さな画面の場合には自由度が少ないので、ほとんど StackLayout か Grid を使ってしまうのであまり関係ないでしょう。ですが、iPad や Surface のようなタブレットPC のレイアウトをするときにはデザイナは必須ですよね。このあたりは、今後どうするのか不明です。

Windows Phone(SilverLightですが)の XAML に直す時に ConvertPageToUIElement メソッドを呼び出しているので、この逆変換ができれば Visual Studio 上で XAML のデザインができるようになるかもしれません。

■HomePage ページを作る

トップページは、メニュー用のページです。
TableView でリスト表示をしているのですが、この親子関係が解りづらいところです。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
				xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
				x:Class="XFormsGallery.HomePage"
				Title="XAML Forms Gallery">
	<TableView Intent="Menu">
		<TableView.Root>
			<TableSection Title="Views for Presentation">
				<TextCell Text="Label" x:Name="cellLabel" ></TextCell>
				<TextCell Text="Image" x:Name="cellImage"></TextCell>
				<TextCell Text="BoxView" x:Name="cellBoxView"></TextCell>
				<TextCell Text="WebView" x:Name="cellWebView"></TextCell>
				<TextCell Text="Map" x:Name="cellMap"></TextCell>
			</TableSection>
			<TableSection Title="Views that Initiate Commands">
				<TextCell Text="Button" x:Name="cellButton" ></TextCell>
				<TextCell Text="SearchBar" x:Name="cellSearchBar"></TextCell>
			</TableSection>
...

ただし、元ネタの HomePage.cs を覗いてみると、TableView <- TableView.Root <- TableRoot <- TableSection <- TextCell <- TextCell の関係が解るので、そのまま書き写しています。XAML では TableRoot を省略しても動作するようです(TableView.Rootのほうは省略できないんですよね。ここは Children にして欲しかった)。

this.Title = "Forms Gallery";
this.Content = new TableView
    {
        Intent = TableIntent.Menu,
        Root = new TableRoot
        {
            new TableSection("Views for Presentation")
            {
                new TextCell
                {
                    Text = "Label",
                    Command = navigateCommand,
                    CommandParameter = typeof(LabelDemoPage)
                },

                new TextCell
                {
                    Text = "Image",
                    Command = navigateCommand,
                    CommandParameter = typeof(ImageDemoPage)
                },

タップなどの操作は非常に制限されています。労力を減らすたか、これから実装するのか解りませんが、TextCell の場合は Command パターンで呼び出しをしています。ただし、主要なボタンコントロールなどには Clicked イベントが用意されているので、そのままコードビハイドで書くことができます。


WPFやWinStoreで XAML を手書きしたことがある人ならば、デザイナがなくても大体想像して書けるかなという感じですね。Xamarin.Forms の DTD は無いようなので、親子関係はマニュアル http://iosapi.xamarin.com/?link=N%3aXamarin.Forms を見る必要があるのですが、まあ元ネタを見比べていけば、これも想像で書ける範囲かと思います。

カテゴリー: C#, Xamarin パーマリンク