第 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.Android、XamlSamples.iOS、ユニバーサル Windows プラットフォーム ソリューションである XamlSamples.UWP の 4 つのプロジェクトが作成されます。
XamlSamples ソリューションを作成した後、さまざまなプラットフォーム プロジェクトをソリューション スタートアップ プロジェクトとして選び、プロジェクト テンプレートによって作成された単純なアプリケーションをビルドして電話エミュレーターまたは実際のデバイスのいずれかにデプロイすることで、開発環境をテストできます。
プラットフォーム固有のコードを記述する必要がない限り、プログラミング時間のほぼすべてを共有の XamlSamples .NET Standard ライブラリ プロジェクトに使うことになります。 これらの記事はそのプロジェクトの範囲を超えるものではありません。
XAML ファイルの構造
XamlSamples .NET Standard ライブラリ内には、次の名前のファイルのペアがあります。
- App.xaml (XAML ファイル)、および
- App.xaml.cs (XAML ファイルに関連付けられた C# "分離コード" ファイル)。
分離コード ファイルを表示するには、App.xaml の横にある矢印をクリックする必要があります。
App.xaml と App.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
要素がページの中央に表示されます。
より関心を引くビジュアルに必要なのは、興味を起こさせる XAML です。
新しい XAML ページの追加
他の XAML ベースの ContentPage
クラスをプロジェクトに追加するには、XamlSamples .NET Standard ライブラリ プロジェクトを選び、右クリックして [追加] > [新しい項目] を選びます。[新しい項目の追加] ダイアログで、[Visual C# の項目] >[Xamarin.Forms]> [コンテンツ ページ] を選びます (コードのみのページを作成する [コンテンツ ページ (C#)] や、ページではない [コンテンツ ビュー] ではありません)。 ページに名前を付けます (たとえば、HelloXamlPage)。
HelloXamlPage.xaml と分離コード ファイル HelloXamlPage.xaml.cs の 2 つのファイルがプロジェクトに追加されます。
ページ コンテンツの設定
HelloXamlPage.xaml ファイルを編集して、タグが ContentPage
と ContentPage.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
タグは、"プロパティ要素" タグと呼ばれます。 Content
は ContentPage
のプロパティであり、通常は 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 クラス (ContentPage
や Label
など) は、XAML ファイルに XML 要素として表示されます。 そのクラスのプロパティ (ContentPage
の Title
と Label
の 7 つのプロパティを含む) は、通常、XML 属性として表現されます。
これらのプロパティの値を設定するための多くのショートカットが存在します。 一部のプロパティは基本的なデータ型です。たとえば、Title
プロパティと Text
プロパティは String
型、Rotation
は Double
型、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="“Hello, XAML!”" … />
次のように表示されます。
XAML とコードの相互作用
HelloXamlPage サンプルには、ページ上に 1 つの Label
しか含まれていませんが、これは非常にまれなことです。 ほとんどの ContentPage
派生関数は、Content
プロパティを、StackLayout
などの何らかのレイアウトに設定します。 StackLayout
の Children
プロパティは、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 ファイルは構文的には完全で、次のようになります。
ただし、このプログラムは機能が不足していると思われるでしょう。 おそらく、Slider
は Label
に現在の値を表示させることを目的としており、Button
は多分プログラム内で何かを行うことを目的としています。
「パート 4. データ バインディングの基礎」で説明するように、Label
を使って Slider
値を表示するジョブは、データ バインディングを使って XAML で完全に処理できます。 ただし、最初にコードのソリューションを確認すると便利です。 それでも、Button
クリックを処理するには間違いなくコードが必要です。 これは、XamlPlusCodePage
の分離コード ファイルには、Slider
の ValueChanged
イベントと Button
の Clicked
イベントのハンドラーが含まれている必要があることを意味します。 それらを追加してみましょう。
namespace XamlSamples
{
public partial class XamlPlusCodePage
{
public XamlPlusCodePage()
{
InitializeComponent();
}
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
}
void OnButtonClicked(object sender, EventArgs args)
{
}
}
}
これらのイベント ハンドラーはパブリックである必要はありません。
XAML ファイルに戻り、Slider
および Button
タグには、これらのハンドラーを参照する ValueChanged
と Clicked
イベントの属性を含めなくてはなりません。
<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>
イベントにハンドラーを割り当てる構文は、プロパティに値を割り当てる場合と同じ構文であることに注意してください。
Slider
の ValueChanged
イベントのハンドラーが 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 構文」で説明します。