第 3 部 XAML マークアップ拡張

サンプルのダウンロードサンプルのダウンロード

XAML マークアップ拡張機能は、他のソースから間接的に参照されるオブジェクトまたは値にプロパティを設定できるようにする XAML の重要な機能です。 XAML マークアップ拡張機能は、オブジェクトを共有したり、アプリケーション全体で使用される定数を参照したりするために特に重要ですが、データ バインディングで最も優れたユーティリティが見つかります。

XAML マークアップ拡張

一般に、XAML を使用して、オブジェクトのプロパティを明示的な値 (文字列、数値、列挙メンバー、またはバックグラウンドの値に変換される文字列など) に設定します。

ただし、プロパティが他の場所で定義されている値を参照する必要がある場合や、実行時にコードによる処理が少し必要になる場合があります。 このような目的で、XAML マークアップ拡張機能 を使用できます。

これらの XAML マークアップ拡張機能は XML の拡張機能ではありません。 XAML は完全に有効な XML です。 これらは、 を実装 IMarkupExtensionするクラスのコードによってサポートされるため、"拡張機能" と呼ばれます。 独自のカスタム マークアップ拡張機能を記述できます。

多くの場合、XAML マークアップ拡張機能は、中かっこ { と }で区切られた属性設定として表示されますが、マークアップ拡張機能がマークアップで従来の要素として表示される場合があるため、XAML ファイルですぐに認識できます。

共有リソース

一部の XAML ページには、プロパティが同じ値に設定された複数のビューが含まれています。 たとえば、これらの Button オブジェクトのプロパティ設定の多くは同じです。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

        <Button Text="Do that!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

        <Button Text="Do the other thing!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

    </StackLayout>
</ContentPage>

これらのプロパティのいずれかを変更する必要がある場合は、変更を 3 回ではなく 1 回だけ行う必要があります。 これがコードの場合は、定数と静的な読み取り専用オブジェクトを使用して、このような値の一貫性と変更を容易に保つことができます。

XAML では、一般的な解決策の 1 つは、このような値またはオブジェクトを リソース ディクショナリに格納することです。 クラスは VisualElement 、 型 という名前 Resources のプロパティを定義します。これは、 型 ResourceDictionaryのキーと 型 stringobject値を持つディクショナリです。 このディクショナリにオブジェクトを配置し、XAML でマークアップから参照できます。

ページでリソース ディクショナリを使用するには、プロパティ要素タグの Resources ペアを含めます。 これらはページの上部に配置するのが最も便利です。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>

    </ContentPage.Resources>
    ...
</ContentPage>

タグを明示的に含める ResourceDictionary 必要もあります。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>

        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

さまざまな型のオブジェクトと値をリソース ディクショナリに追加できるようになりました。 これらの型は即時可能である必要があります。 たとえば、抽象クラスにすることはできません。 これらの型には、パラメーターなしのパブリック コンストラクターも必要です。 各項目には、 属性で指定されたディクショナリ キーが x:Key 必要です。 次に例を示します。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions"
                           Alignment="Center" />

            <LayoutOptions x:Key="vertOptions"
                           Alignment="Center"
                           Expands="True" />
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

これら 2 つの項目は構造体型 LayoutOptionsの値であり、それぞれに一意のキーと 1 つまたは 2 つのプロパティが設定されています。 コードとマークアップでは、 の静的フィールド LayoutOptionsを使用する方がはるかに一般的ですが、ここではプロパティを設定する方が便利です。

次に、これらのボタンの プロパティと VerticalOptions プロパティをこれらのリソースに設定HorizontalOptionsする必要があります。これは XAML マークアップ拡張機能を使用してStaticResource行われます。

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="3"
        Rotation="-15"
        TextColor="Red"
        FontSize="24" />

StaticResourceマークアップ拡張機能は常に中かっこで区切られ、ディクショナリ キーが含まれます。

この名前 StaticResource は、 と区別 DynamicResourceされます。これも Xamarin.Forms サポートしています。 DynamicResource は、実行時に変更される可能性がある値に関連付けられているディクショナリ キーを対象とします。一方 StaticResource 、ページ上の要素が構築されると、ディクショナリから 1 回だけ要素にアクセスします。

プロパティの BorderWidth 場合は、ディクショナリに double を格納する必要があります。 XAML は、 や x:Int32のようなx:Double一般的なデータ型のタグを簡単に定義します。

<ContentPage.Resources>
    <ResourceDictionary>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />

        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center"
                       Expands="True" />

        <x:Double x:Key="borderWidth">
            3
        </x:Double>
    </ResourceDictionary>
</ContentPage.Resources>

3 行に配置する必要はありません。 この回転角度のこの辞書エントリは、1 行のみを占めます。

<ContentPage.Resources>
    <ResourceDictionary>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />

        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center"
                       Expands="True" />

         <x:Double x:Key="borderWidth">
            3
         </x:Double>

        <x:Double x:Key="rotationAngle">-15</x:Double>
    </ResourceDictionary>
</ContentPage.Resources>

これらの 2 つのリソースは、値と同じ方法 LayoutOptions で参照できます。

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        Rotation="{StaticResource rotationAngle}"
        TextColor="Red"
        FontSize="24" />

型のリソースでは、これらの型 Colorの属性を直接割り当てるときに使用するのと同じ文字列表現を使用できます。 型コンバーターは、リソースの作成時に呼び出されます。 型のリソースを次に示します Color

<Color x:Key="textColor">Red</Color>

多くの場合、プログラムはFontSize、 などのLarge列挙体のメンバーにプロパティをNamedSize設定します。 クラスは FontSizeConverter バックグラウンドで動作し、 メソッドを使用して Device.GetNamedSized プラットフォームに依存する値に変換します。 ただし、フォント サイズ リソースを定義する場合は、次のように型として示す数値を使用する方が理に x:Double かなっています。

<x:Double x:Key="fontSize">24</x:Double>

を除く Text すべてのプロパティは、リソース設定によって定義されます。

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        Rotation="{StaticResource rotationAngle}"
        TextColor="{StaticResource textColor}"
        FontSize="{StaticResource fontSize}" />

また、リソース ディクショナリ内で を使用 OnPlatform して、プラットフォームのさまざまな値を定義することもできます。 オブジェクトを OnPlatform さまざまなテキストの色のリソース ディクショナリに含める方法を次に示します。

<OnPlatform x:Key="textColor"
            x:TypeArguments="Color">
    <On Platform="iOS" Value="Red" />
    <On Platform="Android" Value="Aqua" />
    <On Platform="UWP" Value="#80FF80" />
</OnPlatform>

OnPlatformディクショナリ内のオブジェクトであるため属性とx:Key、ジェネリック クラスであるため属性の両方をx:TypeArguments取得していることに注意してください。 、Android、および UWP 属性はiOS、オブジェクトの初期化時にColor値に変換されます。

6 つの共有値にアクセスする 3 つのボタンを含む最終的な完全な XAML ファイルを次に示します。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions"
                           Alignment="Center" />

            <LayoutOptions x:Key="vertOptions"
                           Alignment="Center"
                           Expands="True" />

            <x:Double x:Key="borderWidth">3</x:Double>

            <x:Double x:Key="rotationAngle">-15</x:Double>

            <OnPlatform x:Key="textColor"
                        x:TypeArguments="Color">
                <On Platform="iOS" Value="Red" />
                <On Platform="Android" Value="Aqua" />
                <On Platform="UWP" Value="#80FF80" />
            </OnPlatform>

            <x:Double x:Key="fontSize">24</x:Double>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

        <Button Text="Do that!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

        <Button Text="Do the other thing!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

    </StackLayout>
</ContentPage>

スクリーンショットでは、一貫性のあるスタイル設定と、プラットフォームに依存するスタイルを確認します。

スタイル付きコントロール

ページの上部にコレクションをResources定義するのが最も一般的ですが、 プロパティは によってVisualElement定義され、ページ上の他の要素にコレクションを含ResourcesめることができることにResources注意してください。 たとえば、次の例の に 1 つを StackLayout 追加してみてください。

<StackLayout>
    <StackLayout.Resources>
        <ResourceDictionary>
            <Color x:Key="textColor">Blue</Color>
        </ResourceDictionary>
    </StackLayout.Resources>
    ...
</StackLayout>

ボタンのテキストの色が青になっていることがわかります。 基本的には、XAML パーサーがマークアップ拡張機能を StaticResource 検出するたびに、ビジュアル ツリーを検索し、そのキーを含む最初 ResourceDictionary に検出された を使用します。

リソース ディクショナリ Xamarin.FormsStyleに格納されているオブジェクトの最も一般的な種類の 1 つは、 で、プロパティ設定のコレクションを定義します。 スタイルについては、「スタイル」を参照 してください

XAML を初めて使用する開発者は、 や などのLabelButtonビジュアル要素を に配置できるかどうかを疑問にResourceDictionary思うことがあります。 確かに可能ですが、あまり意味がありません。 の ResourceDictionary 目的は、オブジェクトを共有することです。 ビジュアル要素は共有できません。 同じインスタンスを 1 つのページに 2 回表示することはできません。

x:Static マークアップ拡張機能

彼らの名前の類似点にもかかわらず、 x:StaticStaticResource は非常に異なります。 StaticResource はリソース ディクショナリから オブジェクトを返しますが、 x:Static 次のいずれかにアクセスします。

  • パブリック静的フィールド
  • パブリック静的プロパティ
  • パブリック定数フィールド
  • 列挙メンバー。

StaticResourceマークアップ拡張機能は、リソース ディクショナリを定義する XAML 実装でサポートされていますが、x:Staticプレフィックスが明らかにするように x XAML の組み込み部分です。

静的フィールドと列挙メンバーを明示的に参照する方法 x:Static を示す例をいくつか次に示します。

<Label Text="Hello, XAML!"
       VerticalOptions="{x:Static LayoutOptions.Start}"
       HorizontalTextAlignment="{x:Static TextAlignment.Center}"
       TextColor="{x:Static Color.Aqua}" />

今のところ、これはあまり印象的ではありません。 ただし、マークアップ拡張機能は、独自の x:Static コードから静的フィールドまたはプロパティを参照することもできます。 たとえば、アプリケーション全体で複数の AppConstants ページで使用する静的フィールドを含むクラスを次に示します。

using System;
using Xamarin.Forms;

namespace XamlSamples
{
    static class AppConstants
    {
        public static readonly Thickness PagePadding;

        public static readonly Font TitleFont;

        public static readonly Color BackgroundColor = Color.Aqua;

        public static readonly Color ForegroundColor = Color.Brown;

        static AppConstants()
        {
            switch (Device.RuntimePlatform)
            {
                case Device.iOS:
                    PagePadding = new Thickness(5, 20, 5, 0);
                    TitleFont = Font.SystemFontOfSize(35, FontAttributes.Bold);
                    break;

                case Device.Android:
                    PagePadding = new Thickness(5, 0, 5, 0);
                    TitleFont = Font.SystemFontOfSize(40, FontAttributes.Bold);
                    break;

                case Device.UWP:
                    PagePadding = new Thickness(5, 0, 5, 0);
                    TitleFont = Font.SystemFontOfSize(50, FontAttributes.Bold);
                    break;
            }
        }
    }
}

XAML ファイルでこのクラスの静的フィールドを参照するには、このファイルが配置されている XAML ファイル内でを示す何らかの方法が必要です。 これを行うには、XML 名前空間宣言を使用します。

標準 Xamarin.Forms XAML テンプレートの一部として作成された XAML ファイルには、クラスへのアクセス用と XAML に組み込まれているタグと属性の Xamarin.Forms 参照用の 2 つの XML 名前空間宣言が含まれていることを思い出してください。

xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

他のクラスにアクセスするには、追加の XML 名前空間宣言が必要です。 追加の XML 名前空間宣言ごとに、新しいプレフィックスが定義されます。 などの AppConstants共有アプリケーション .NET Standard ライブラリのローカルクラスにアクセスするには、XAML プログラマは多くの場合、プレフィックス localを使用します。 名前空間宣言は、CLR (共通言語ランタイム) 名前空間名 (.NET 名前空間名とも呼ばれます) を示す必要があります。これは、C# namespace 定義またはディレクティブに using 表示される名前です。

xmlns:local="clr-namespace:XamlSamples"

.NET Standard ライブラリが参照する任意のアセンブリで、.NET 名前空間の XML 名前空間宣言を定義することもできます。 たとえば、netstandard アセンブリにある標準の .NET System 名前空間のプレフィックスを次sysに示します。 これは別のアセンブリであるため、アセンブリ名 (この場合は netstandard) も指定する必要があります。

xmlns:sys="clr-namespace:System;assembly=netstandard"

キーワード (keyword)clr-namespaceの後にコロン、.NET 名前空間名、セミコロン、キーワード (keyword)assembly、等号、アセンブリ名が続きます。

はい。コロンは の後に続きます clr-namespace が、等号は の後に続きます assembly。 構文は意図的に定義されました。ほとんどの XML 名前空間宣言は、 などの httpURI スキーム名を開始する URI を参照します。これは常にコロンが続きます。 この文字列の部分は clr-namespace 、その規則を模倣することを目的としています。

これらの名前空間宣言はどちらも StaticConstantsPage サンプルに含まれています。 ディメンションは BoxViewMath.EMath.PI設定されていますが、100 倍にスケーリングされます。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             xmlns:sys="clr-namespace:System;assembly=netstandard"
             x:Class="XamlSamples.StaticConstantsPage"
             Title="Static Constants Page"
             Padding="{x:Static local:AppConstants.PagePadding}">

    <StackLayout>
       <Label Text="Hello, XAML!"
              TextColor="{x:Static local:AppConstants.BackgroundColor}"
              BackgroundColor="{x:Static local:AppConstants.ForegroundColor}"
              Font="{x:Static local:AppConstants.TitleFont}"
              HorizontalOptions="Center" />

      <BoxView WidthRequest="{x:Static sys:Math.PI}"
               HeightRequest="{x:Static sys:Math.E}"
               Color="{x:Static local:AppConstants.ForegroundColor}"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="100" />
    </StackLayout>
</ContentPage>

画面に対する相対的な BoxView 結果のサイズは、プラットフォームに依存します。

x:Static Markup Extension を使用するコントロール

その他の標準マークアップ拡張機能

いくつかのマークアップ拡張機能は XAML に組み込まれており、XAML ファイルで Xamarin.Forms サポートされています。 これらの一部は頻繁に使用されませんが、必要な場合は不可欠です。

  • プロパティに既定では値 null 以外の値があるが、 に null設定する場合は、マークアップ拡張機能に {x:Null} 設定します。
  • プロパティが 型Typeの場合は、マークアップ拡張 {x:Type someClass}Type使用してオブジェクトに割り当てることができます。
  • XAML では、マークアップ拡張機能を使用して配列を x:Array 定義できます。 このマークアップ拡張には、配列内の要素の型を示す という名前 Type の必須属性があります。
  • マークアップ拡張機能については Bindingパート 4 で説明します。データ バインディングの基本
  • マークアップ拡張機能については RelativeSource 、「 相対バインディング」を参照してください。

ConstraintExpression マークアップ拡張機能

マークアップ拡張機能はプロパティを持つことができますが、XML 属性のように設定されません。 マークアップ拡張機能では、プロパティ設定はコンマで区切られ、中かっこ内に引用符は表示されません。

これは、 クラスで使用される という名前ConstraintExpressionのXamarin.Formsマークアップ拡張機能でRelativeLayout示すことができます。 子ビューの場所またはサイズを定数として指定することも、親ビューやその他の名前付きビューを基準にして指定することもできます。 のConstraintExpression構文を使用すると、ビューの位置またはサイズを、別のビューのプロパティに 加えて Constantを使用してFactor設定できます。 コードを必要とするよりも複雑なもの。

次に例を示します。

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

    <RelativeLayout>

        <!-- Upper left -->
        <BoxView Color="Red"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}" />
        <!-- Upper right -->
        <BoxView Color="Green"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=1,
                                            Constant=-40}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}" />
        <!-- Lower left -->
        <BoxView Color="Blue"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=1,
                                            Constant=-40}" />
        <!-- Lower right -->
        <BoxView Color="Yellow"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=1,
                                            Constant=-40}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=1,
                                            Constant=-40}" />

        <!-- Centered and 1/3 width and height of parent -->
        <BoxView x:Name="oneThird"
                 Color="Red"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=0.33}"
                 RelativeLayout.WidthConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.HeightConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=0.33}"  />

        <!-- 1/3 width and height of previous -->
        <BoxView Color="Blue"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=X}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Y}"
                 RelativeLayout.WidthConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.HeightConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Height,
                                            Factor=0.33}"  />
    </RelativeLayout>
</ContentPage>

おそらく、このサンプルから取るべき最も重要なレッスンは、マークアップ拡張機能の構文です。マークアップ拡張機能の中かっこ内に引用符を含める必要はありません。 XAML ファイルにマークアップ拡張機能を入力するときは、プロパティの値を引用符で囲むのが自然です。 誘惑に抵抗!

実行されているプログラムを次に示します。

制約を使用した相対レイアウト

まとめ

ここに示す XAML マークアップ拡張機能は、XAML ファイルの重要なサポートを提供します。 しかし、おそらく最も重要な XAML マークアップ拡張機能は Bindingです。これは、このシリーズの次のパートである パート 4 で説明します。データ バインディングの基本