XAML マークアップ拡張

完了

XAML 定義の多くは、コンパイル時に決定されます。 多くの場合、要素を配置する場所、使用する色とフォント、プロパティに割り当てるリテラル値はわかります。

ただし、時には、コンパイル時に決定できない値をプロパティ値に設定する場合もあります。 これらの値は、プログラム実行中のみわかります。 このような状況では、実行時に XAML に値を指定するオブジェクトを作成できます。 XAML では、この目的のためにマークアップ拡張をサポートしています。

このユニットでは、マークアップ拡張を作成して使用する方法について説明します。

マークアップ拡張とは

マークアップ拡張は、ランタイムの値にアクセスするために XAML で使うクラスです。 XAML UI で多くのラベルが定義されており、すべてのラベルのスタイルが一貫するように、アプリ全体で FontSize プロパティを同じ値に設定したいとします。 FontSize プロパティは、次に示すように、XAML を使って設定できます。

<Label Text="Hello, World!"
            Grid.Row="0"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="28"
            HorizontalOptions="CenterAndExpand"/>

この同じ設定をすべてのラベルに対して繰り返すことができますが、後でこの値を変更する場合はどうなるでしょうか。 このプロパティのすべてのインスタンスを見つけ、変更する必要があります。 また、どのような値を使うとよいか決められない場合を考えてください。デバイスの向きや画面の解像度、その他の考慮事項などの要因に基づいて実行時に計算される可能性もあります。 このような場合、ハードコーディングされたリテラルよりも高度なものが必要です。 そこで、マークアップ拡張が役に立ちます。 マークアップ拡張を使用すると、XAML で使用される値を取得する方法に柔軟性がもたらされます。

マークアップ拡張を作成する

マークアップ拡張は Microsoft.Maui.Controls.Xaml.IMarkupExtension インターフェイスを実装するクラスです。 このインターフェイスでは、次のシグネチャで ProvideValue というメソッドを 1 つ定義しています。

public object ProvideValue(IServiceProvider serviceProvider)
{
    ...
}

このメソッドの目的は、XAML マークアップに値を指定することです。 戻り値の型が object であることに注意してください。そのため、適切な場所で値が使われている限り、値を任意の型にすることができます。 たとえば、フォント サイズを計算して返すマークアップ拡張では、戻り値の型は double にします。

serviceProvider パラメーターは、XAML コードの中でマークアップ拡張が使われている場所に関するコンテキスト情報が含まれています。その他の情報の中で、拡張機能が適用されているコントロールを識別します。

FontSize プロパティのマークアップ拡張は単純な状態に維持できます。 次の例では、MainPage クラスは MyFontSize という double フィールドを公開しています。 GlobalFontSizeExtension クラスは IMarkupExtension インターフェイスを実装し、ProvideValue メソッドは MyFontSize 変数の値を返します。

namespace MyMauiApp;

public partial class MainPage : ContentPage
{
    public const double MyFontSize = 28;

    public MainPage()
    {
        InitializeComponent();
        ...
    }
    ...
}

public class GlobalFontSizeExtension : IMarkupExtension
{
    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return MainPage.MyFontSize;
    }
}

Note

MyFontSize フィールドは、ProvideValue メソッドの中でこの方法で参照できるようにするには、MainPage クラスの static メンバーである必要があります。 適切な方法は、この場合、変数も定数であることです。 const の値は static です。

ProvideValue メソッドでは、方向とデバイスのフォーム ファクターに応じて、返される値を調整することもできます。

XAML のコントロールにマークアップ拡張を適用する

XAML コードでマークアップ拡張を使うには、GlobalFontSizeExtension クラスを含む名前空間を ContentPage タグの中の名前空間の一覧に追加します。 次の例では、この名前空間に mycodeという別名を付けています。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:mycode="clr-namespace:MyMauiApp"
             x:Class="MyMauiApp.MainPage">

マークアップ拡張を使用すると、このように FontSize プロパティを設定できます。 マークアップ拡張は、その名前に Extension というサフィックスを持つという規則に注意してください。 XAML はこのサフィックスを認識し、XAML コードから拡張機能を呼び出すときにサフィックスを含める必要はありません。 次の例では、GlobalFontSizeExtension クラスは、単に GlobalFontSize として参照されています。

<Label Text="Hello, World!"
            Grid.Row="0"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="{mycode:GlobalFontSize}"
            HorizontalOptions="CenterAndExpand"/>

フォント サイズを指定する必要があるすべてのコントロールに対して、XAML コード全体で同じマークアップ拡張を適用できます。 後で、フォント サイズを変更する場合は、MyFontSize 変数の定義を MainPage クラスで変更するだけで済みます。

StaticExtension クラス

GlobalFontSize マークアップ拡張と同様に便利ですが、このような拡張を作成することはあまりありません。 その理由は簡単で、.NET MAUI にはより一般化された拡張機能が既にあり、コード内のあらゆる静的な値を参照できるからです。 この拡張機能は StaticExtension、略して Static という名前です。 次のコードは、この拡張クラスの基本的な概要を示しています。

[ContentProperty ("Member")]
public class StaticExtension : IMarkupExtension
{
    public string Member {get; set;}
    public object ProvideValue (IServiceProvider serviceProvider)
    {
        ...
    }
}

Note

カスタム マークアップ拡張の目的は、単純な静的ケースよりも複雑な状況に対処できるようにすることです。 たとえば、デバイスのフォーム ファクターに基づいてフォント サイズを動的に変更することが必要な場合があります。

XAML コードでこのクラスを使うには、Member プロパティで参照したい静的変数の名前を指定し、ProvideValue メソッドがこの変数の値を返します。 次の例は、その使い方を示します。

<Label Text="Hello, World!"
            Grid.Row="0"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="{x:Static Member=mycode:MainPage.MyFontSize}"
            HorizontalOptions="CenterAndExpand"/>

.NET MAUI には、データ バインディング、動的リソースとスタイルの参照、データの配列の処理などのシナリオに使用できる、他のマークアップ拡張クラスのセットが用意されています。

知識チェック

1.

どのマークアップ拡張を使うと、XAML プロパティを分離コード クラスで定義される静的な値に設定できますか?

2.

カスタム マークアップ拡張を作成するにはどのインターフェイスを使いますか。