第 1 部 XAML の概要

"Xamarin.Forms アプリケーションでは、XAML は主にページのビジュアル コンテンツを定義するために使われ、C# 分離コード ファイルと連携して機能します。"

コードビハインド ファイルは、マークアップのコード サポートを提供します。 これら 2 つのファイルは、子ビューとプロパティの初期化を含む新しいクラス定義に貢献します。 XAML ファイル内では、クラスとプロパティは XML 要素と属性で参照され、マークアップとコード間のリンクが確立されます。

ソリューションの作成

最初の XAML ファイルの編集を開始するには、Visual Studio または Visual Studio for Mac を使って新しい Xamarin.Forms ソリューションを作成します (使用する環境に応じて以下のタブを選びます)。

Windows で Visual Studio 2019 を起動し、スタート ウィンドウで [新しいプロジェクトの作成] をクリックして新しいプロジェクトを作成します。

[新しいソリューション] ウィンドウ

[新しいプロジェクトを作成する] ウィンドウの [プロジェクト タイプ] ドロップ ダウンで [モバイル] を選択し、[モバイル アプリ (Xamarin.Forms] テンプレートを選択して、[次へ] ボタンをクリックします。

[新しいプロジェクト] ウィンドウ

[新しいプロジェクトの構成] ウィンドウで、[プロジェクト名]XamlSamples (または任意の名前) に設定し、[作成] ボタンをクリックします。

[新しいクロス プラットフォーム アプリ] ダイアログで、[空白] をクリックし、[OK] ボタンをクリックします。

[新しいアプリ] ダイアログ

このソリューションでは、XamlSamples .NET Standard ライブラリ、XamlSamples.AndroidXamlSamples.iOS、ユニバーサル Windows プラットフォーム ソリューションである XamlSamples.UWP の 4 つのプロジェクトが作成されます。

XamlSamples ソリューションを作成した後、さまざまなプラットフォーム プロジェクトをソリューション スタートアップ プロジェクトとして選び、プロジェクト テンプレートによって作成された単純なアプリケーションをビルドして電話エミュレーターまたは実際のデバイスのいずれかにデプロイすることで、開発環境をテストできます。

プラットフォーム固有のコードを記述する必要がない限り、プログラミング時間のほぼすべてを共有の XamlSamples .NET Standard ライブラリ プロジェクトに使うことになります。 これらの記事はそのプロジェクトの範囲を超えるものではありません。

XAML ファイルの構造

XamlSamples .NET Standard ライブラリ内には、次の名前のファイルのペアがあります。

  • App.xaml (XAML ファイル)、および
  • App.xaml.cs (XAML ファイルに関連付けられた C# "分離コード" ファイル)。

分離コード ファイルを表示するには、App.xaml の横にある矢印をクリックする必要があります。

App.xamlApp.xaml.cs は両方とも、Applicationから派生した App という名前のクラスに貢献します。 XAML ファイルを使う他のほとんどのクラスは、ContentPage から派生するクラスに組み入れられます。これらのファイルは XAML を使ってページ全体のビジュアル コンテンツを定義します。 これは、XamlSamples プロジェクト内の他の 2 つのファイルにも当てはまります。

  • MainPage.xaml (XAML ファイル)、および
  • MainPage.xaml.cs (C# 分離コード ファイル)。

MainPage.xaml ファイルは次のようになります (ただし、形式は少し異なる場合があります)。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             x:Class="XamlSamples.MainPage">

    <StackLayout>
        <!-- Place new controls here -->
        <Label Text="Welcome to Xamarin Forms!"
               VerticalOptions="Center"
               HorizontalOptions="Center" />
    </StackLayout>

</ContentPage>

2 つの XML 名前空間 (xmlns) 宣言は URI を参照しており、最初のは Xamarin の Web サイトにあり、2 番目は Microsoft にあるようです。 これらの URI が何を指しているのかを確認する必要はありません。 そこには何もありません。 これらは単に Xamarin と Microsoft が所有する URI であり、基本的にバージョン識別子として機能します。

最初の XML 名前空間宣言は、XAML ファイル内でプレフィックスなしで定義されたタグが、Xamarin.Forms のクラス (たとえば、ContentPage) を参照することを意味します。 2 番目の名前空間宣言では、x のプレフィックスを定義します。 これは、XAML 自体に組み込まれ、XAML の他の実装でサポートされている複数の要素と属性で使用されます。 ただし、これらの要素と属性は、URI に埋め込まれた年によって若干異なります。 Xamarin.Forms は 2009 XAML 仕様をサポートしていますが、そのすべてがサポートされているわけではありません。

local 名前空間宣言を使うと、.NET Standard ライブラリ プロジェクトから他のクラスにアクセスできます。

最初のタグの最後で、x プレフィックスが Class という属性に使われます。 この x プレフィックスの使用は XAML 名前空間で実質的に汎用なので、Class のような XAML 属性はほとんどの場合、x:Class として参照されます。

x:Class 属性は、完全修飾 .NET クラス名 (XamlSamples 名前空間の MainPage クラス) を指定します。 これは、この XAML ファイルが、XamlSamples 名前空間に MainPage という名前の新しいクラスを定義し、ContentPage (x:Class 属性が指定されるタグ) から派生していることを意味します。

x:Class 属性は、派生した C# クラスを定義するために XAML ファイルのルート要素にのみ表示できます。 これは、XAML ファイルで定義されている唯一の新しいクラスです。 XAML ファイルに表示されるその他のすべてのものは、代わりに既存のクラスから単純にインスタンスが作成され、初期化されます。

MainPage.xaml.cs ファイルは次のようになります (使われていない using ディレクティブは除きます)。

using Xamarin.Forms;

namespace XamlSamples
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }
}

MainPage クラスは ContentPage から派生しますが、partial クラス定義に注意してください。 これは、MainPage には別の部分クラス定義が必要であることを示唆していますが、それはどこにあるのでしょうか? そして、この InitializeComponent メソッドとは何でしょうか?

Visual Studio はプロジェクトをビルドするときに、XAML ファイルを解析して C# コード ファイルを生成します。 XamlSamples\XamlSamples\obj\Debug ディレクトリを見ると、XamlSamples.MainPage.xaml.g.cs というファイルが見つかります。 'g' は生成されたことを表します。 これは、MainPage コンストラクターから呼び出される InitializeComponent メソッドの定義を含む、MainPage のその他の部分クラス定義です。 これら 2 つの部分 MainPage クラス定義は、一緒にコンパイルできます。 XAML がコンパイルされているかどうかに応じて、XAML ファイルまたはバイナリ形式の XAML ファイルが実行可能ファイルに埋め込まれます。

実行時に、特定のプラットフォーム プロジェクトのコードは LoadApplication メソッドを呼び出し、.NET Standard ライブラリの App クラスの新しいインスタンスをそれに渡します。 App クラス コンストラクターは MainPage のインスタンスを作成します。 そのクラスのコンストラクターは InitializeComponent を呼び出し、次に、.NET Standard ライブラリから XAML ファイル (またはそのコンパイルされたバイナリ) を抽出する LoadFromXaml メソッドを呼び出します。 LoadFromXaml は、XAML ファイルで定義されているすべてのオブジェクトを初期化し、それらを親子リレーションシップで接続し、コードで定義されたイベント ハンドラーを XAML ファイルで設定されたイベントにアタッチし、結果として得られるオブジェクトのツリーをページのコンテンツとして設定します。

通常、生成されたコード ファイルに多くの時間を費やす必要はありませんが、生成されたファイル内のコードでランタイム例外が発生する場合があるため、これらについて理解しておく必要があります。

このプログラムをコンパイルして実行すると、XAML が示すように、Label 要素がページの中央に表示されます。

既定の Xamarin.Forms の表示

より関心を引くビジュアルに必要なのは、興味を起こさせる XAML です。

新しい XAML ページの追加

他の XAML ベースの ContentPage クラスをプロジェクトに追加するには、XamlSamples .NET Standard ライブラリ プロジェクトを選び、右クリックして [追加] > [新しい項目] を選びます。[新しい項目の追加] ダイアログで、[Visual C# の項目] >[Xamarin.Forms]> [コンテンツ ページ] を選びます (コードのみのページを作成する [コンテンツ ページ (C#)] や、ページではない [コンテンツ ビュー] ではありません)。 ページに名前を付けます (たとえば、HelloXamlPage)。

[新しい項目の追加] ダイアログ

HelloXamlPage.xaml と分離コード ファイル HelloXamlPage.xaml.cs の 2 つのファイルがプロジェクトに追加されます。

ページ コンテンツの設定

HelloXamlPage.xaml ファイルを編集して、タグが ContentPageContentPage.Content のみになるようにします。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.HelloXamlPage">
    <ContentPage.Content>

    </ContentPage.Content>
</ContentPage>

ContentPage.Content タグは、XAML の固有の構文の一部です。 一見、無効な XML のように見えるかもしれませんが、有効です。 ピリオドは XML では特殊文字ではありません。

ContentPage.Content タグは、"プロパティ要素" タグと呼ばれます。 ContentContentPage のプロパティであり、通常は 1 つのビューまたは子ビューを含むレイアウトに設定されます。 通常、プロパティは XAML の属性になりますが、複雑なオブジェクトに Content 属性を設定するのは困難です。 そのため、プロパティはクラス名とプロパティ名をピリオドで区切った XML 要素として表現されます。 これで、次のように Content プロパティを ContentPage.Content タグの間に設定できるようになります。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.HelloXamlPage"
             Title="Hello XAML Page">
    <ContentPage.Content>

        <Label Text="Hello, XAML!"
               VerticalOptions="Center"
               HorizontalTextAlignment="Center"
               Rotation="-15"
               IsVisible="true"
               FontSize="Large"
               FontAttributes="Bold"
               TextColor="Blue" />

    </ContentPage.Content>
</ContentPage>

また、ルート タグに Title 属性が設定されていることにも注目してください。

この時点で、クラス、プロパティ、XML 間のリレーションシップが明らかになります。Xamarin.Forms クラス (ContentPageLabel など) は、XAML ファイルに XML 要素として表示されます。 そのクラスのプロパティ (ContentPageTitleLabel の 7 つのプロパティを含む) は、通常、XML 属性として表現されます。

これらのプロパティの値を設定するための多くのショートカットが存在します。 一部のプロパティは基本的なデータ型です。たとえば、Title プロパティと Text プロパティは String 型、RotationDouble 型、IsVisible (既定では true であり、ここでは説明のためにのみ設定されています) は Boolean 型です。

HorizontalTextAlignment プロパティは TextAlignment 型 (列挙型) です。 任意の列挙型のプロパティの場合、指定する必要があるのはメンバー名のみです。

ただし、より複雑な型のプロパティの場合は、XAML の解析にコンバーターが使用されます。 これらは、TypeConverter から派生した Xamarin.Forms のクラスです。 多くはパブリック クラスですが、一部はそうではありません。 この特定の XAML ファイルでは、次のクラスのいくつかが舞台裏で役割を果たします。

  • VerticalOptions プロパティの LayoutOptionsConverter
  • FontSize プロパティの FontSizeConverter
  • TextColor プロパティの ColorTypeConverter

これらのコンバーターは、プロパティ設定の許容される構文を制御します。

ThicknessTypeConverter は、コンマで区切られた 1 つ、2 つ、または 4 つの数値を処理できます。 1 つの数値を指定すると、その数値が 4 つの辺すべてに適用されます。 2 つの数値の場合、1 つ目は左右のパディング、2 つ目は上下のです。 4 つの数値の場合は、左、上、右、下の順です。

LayoutOptionsConverter は、LayoutOptions 構造体のパブリック静的フィールドの名前を LayoutOptions 型の値に変換できます。

FontSizeConverter は、NamedSize メンバーまたは数値のフォント サイズを処理できます。

ColorTypeConverter はアルファ チャネルの有無に関係なく、Color 構造体のパブリック静的フィールドの名前、または先頭にシャープ記号 (#) を付けた 16 進数の RGB 値を指定できます。 アルファ チャネルを含まない構文は次のとおりです。

TextColor="#rrggbb"

それぞれの小さな文字は 16 進数です。 アルファ チャネルを含む方法を次に示します。

TextColor="#aarrggbb">

アルファ チャネルの場合、FF は完全に不透明であり、00 は完全に透明であることに留意してください。

他の 2 つの形式では、各チャネルに 1 つの 16 進数のみを指定できます。

TextColor="#rgb" TextColor="#argb"

このような場合、数字が繰り返されて値を形成します。 たとえば、#CF3 は RGB カラー CC-FF-33 です。

XamlSamples プログラムを実行すると、MainPage が表示されます。 新しい HelloXamlPage を表示するには、それを App.xaml.cs ファイルで新しいスタートアップ ページとして設定するか、MainPage から新しいページに移動します。

ナビゲーションを実装するには、まず App.xaml.cs コンストラクターのコードを変更して、NavigationPage オブジェクトが作成されるようにします。

public App()
{
    InitializeComponent();
    MainPage = new NavigationPage(new MainPage());
}

MainPage.xaml.cs コンストラクターで、単純な Button を作成し、イベント ハンドラーを使って HelloXamlPage に移動できます。

public MainPage()
{
    InitializeComponent();

    Button button = new Button
    {
        Text = "Navigate!",
        HorizontalOptions = LayoutOptions.Center,
        VerticalOptions = LayoutOptions.Center
    };

    button.Clicked += async (sender, args) =>
    {
        await Navigation.PushAsync(new HelloXamlPage());
    };

    Content = button;
}

ページの Content プロパティを設定すると、XAML ファイルの Content プロパティの設定が置き換えられます。 このプログラムの新しいバージョンをコンパイルしてデプロイすると、画面にボタンが表示されます。 これを押すと、HelloXamlPage に移動します。 iPhone、Android、UWP での結果のページは次のとおりです。

回転ラベル テキスト

iOS では [< 戻る] ボタンを使って、Android ではページの上部または電話の下部にある左矢印を使って、Windows 10 ではページの上部にある左矢印を使って MainPage に戻ることができます。

Label をレンダリングするさまざまな方法について、XAML を自由に試してみてください。 Unicode 文字をテキストに埋め込む必要がある場合は、標準の XML 構文を使用できます。 たとえば、挨拶をスマート クォートで囲むには、次のように使います。

<Label Text="&#x201C;Hello, XAML!&#x201D;" … />

次のように表示されます。

Unicode 文字を含む回転ラベル テキスト

XAML とコードの相互作用

HelloXamlPage サンプルには、ページ上に 1 つの Label しか含まれていませんが、これは非常にまれなことです。 ほとんどの ContentPage 派生関数は、Content プロパティを、StackLayout などの何らかのレイアウトに設定します。 StackLayoutChildren プロパティは、IList<View> 型であるように定義されていますが、実際には ElementCollection<View> 型のオブジェクトであり、そのコレクションには複数のビューまたは他のレイアウトを設定できます。 XAML では、これらの親子リレーションシップは通常の XML 階層で確立されます。 以下は、XamlPlusCodePage という新しいページの XAML ファイルです。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
    <StackLayout>
        <Slider VerticalOptions="CenterAndExpand" />

        <Label Text="A simple Label"
               Font="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

この XAML ファイルは構文的には完全で、次のようになります。

ページ上の複数のコントロール

ただし、このプログラムは機能が不足していると思われるでしょう。 おそらく、SliderLabel に現在の値を表示させることを目的としており、Button は多分プログラム内で何かを行うことを目的としています。

パート 4. データ バインディングの基礎」で説明するように、Label を使って Slider 値を表示するジョブは、データ バインディングを使って XAML で完全に処理できます。 ただし、最初にコードのソリューションを確認すると便利です。 それでも、Button クリックを処理するには間違いなくコードが必要です。 これは、XamlPlusCodePage の分離コード ファイルには、SliderValueChanged イベントと ButtonClicked イベントのハンドラーが含まれている必要があることを意味します。 それらを追加してみましょう。

namespace XamlSamples
{
    public partial class XamlPlusCodePage
    {
        public XamlPlusCodePage()
        {
            InitializeComponent();
        }

        void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
        {

        }

        void OnButtonClicked(object sender, EventArgs args)
        {

        }
    }
}

これらのイベント ハンドラーはパブリックである必要はありません。

XAML ファイルに戻り、Slider および Button タグには、これらのハンドラーを参照する ValueChangedClicked イベントの属性を含めなくてはなりません。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
    <StackLayout>
        <Slider VerticalOptions="CenterAndExpand"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="A simple Label"
               Font="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                Clicked="OnButtonClicked" />
    </StackLayout>
</ContentPage>

イベントにハンドラーを割り当てる構文は、プロパティに値を割り当てる場合と同じ構文であることに注意してください。

SliderValueChanged イベントのハンドラーが Label を使って現在の値を表示する場合、ハンドラーはコードからそのオブジェクトを参照する必要があります。 Label には名前が必要で、これは x:Name 属性で指定されます。

<Label x:Name="valueLabel"
       Text="A simple Label"
       Font="Large"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />

x:Name 属性の x のプレフィックスは、この属性が XAML に組み込まれていることを示します。

x:Name 属性に割り当てる名前には、C# 変数名と同じ規則があります。 たとえば、文字またはアンダースコアで始まり、スペースを埋め込まないなどです。

これで、ValueChanged イベント ハンドラーで、新しい Slider 値を表示するように Label を設定できるようになりました。 新しい値はイベント引数から取得できます。

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = args.NewValue.ToString("F3");
}

または、ハンドラーでは、このイベントを生成している Slider オブジェクトを sender 引数から取得し、そこから Value プロパティを取得することもできます。

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = ((Slider)sender).Value.ToString("F3");
}

プログラムを初めて実行するときは、ValueChanged イベントがまだ発生していないため、Label には Slider 値が表示されません。 ただし、Slider を操作すると値が表示されます。

表示されるスライダー値

次に、Button について説明します。 ボタンの Text でアラートを表示することで、Clicked イベントへの応答をシミュレートしてみましょう。 イベント ハンドラーは、sender 引数を Button に安全にキャストし、そのプロパティにアクセスできます。

async void OnButtonClicked(object sender, EventArgs args)
{
    Button button = (Button)sender;
    await DisplayAlert("Clicked!",
        "The button labeled '" + button.Text + "' has been clicked",
        "OK");
}

DisplayAlert メソッドは非同期であり、メソッドの完了時に返される await 演算子を先頭に付ける必要があるため、メソッドは async として定義されます。 このメソッドは sender 引数からイベントを発生させる Button を取得するため、複数のボタンで同じハンドラーを使用できます。

XAML で定義されたオブジェクトが、分離コード ファイルで処理されるイベントを発生できること、分離コード ファイルが、x:Name 属性で割り当てられた名前を使って XAML で定義されたオブジェクトにアクセスできることがわかりました。 これらは、コードと XAML が相互作用する 2 つの基本的な方法です。

新しく生成された XamlPlusCode.xaml.g.cs ファイルを調べることで、XAML のしくみに関する追加の分析情報を収集できます。このファイルには、プライベート フィールドとして x:Name 属性に割り当てられた名前が含まれています。 そのファイルの簡略化されたバージョンを次に示します。

public partial class XamlPlusCodePage : ContentPage {

    private Label valueLabel;

    private void InitializeComponent() {
        this.LoadFromXaml(typeof(XamlPlusCodePage));
        valueLabel = this.FindByName<Label>("valueLabel");
    }
}

このフィールドを宣言すると、管轄下の XamlPlusCodePage 部分クラス ファイル内の任意の場所で変数を自由に使用できるようになります。 実行時、XAML が解析された後にフィールドが割り当てられます。 これは、valueLabel フィールドは、XamlPlusCodePage コンストラクターの開始時には null ですが、InitializeComponent が呼び出された後は有効であることを意味します。

InitializeComponent が制御をコンストラクターに戻した後、ページのビジュアルは、コード内でインスタンス化および初期化されたかのように構築されています。 XAML ファイルはクラス内で何の役割も果たさなくなりました。 ページ上のこれらのオブジェクトは、StackLayout にビューを追加したり、ページの Content プロパティをまったく別のものに設定したりするなど、任意の方法で操作できます。 ページの Content プロパティとレイアウトの Children コレクション内の項目を調べることで、"ツリーをたどる" ことができます。 この方法でアクセスしたビューのプロパティを設定したり、ビューにイベント ハンドラーを動的に割り当てたりすることができます。

ご自由にどうぞ。 これはあなたのページであり、XAML はそのコンテンツを構築するためのツールにすぎません。

まとめ

この概要では、XAML ファイルとコード ファイルがクラス定義にどのように組み入れられるか、そして XAML とコード ファイルがどのように相互作用するかを見てきました。 ただし、XAML には、非常に柔軟な方法で使用できる固有の構文機能もあります。 これらについては、「パート 2. 基本的な XAML 構文」で説明します。