スタイルを使用して一貫性のある UI を作成する

完了

.NET Multi-Platform App UI (MAUI) において、リソースは Extensible Application Markup Language (XAML) のマークアップ内で、ハードコーディングされた、重複した値を回避するのに適していますが、適用するのが面倒になる場合があります。 各プロパティ値を個別に割り当てると、XAML が煩雑かつ冗長になります。 このユニットでは、複数の設定を 1 つのスタイルにグループ化する方法を示します。これにより、コードを整理し、保守しやすくすることができます。

リソースによって XAML が煩雑になる理由

リソースで 1 つのプロパティの値を指定します。 ただし、多くのリソースを使用すると、XAML が冗長になります。 ボタンの外観をカスタマイズするとします。 まず、必要な値のリソースを作成します。 次に、各リソースをすべてのボタンに適用します。 次のコードは、XAML マークアップで 2 つのボタンを検索する方法を示しています。

<Button
    Text = "OK"
    BackgroundColor = "{StaticResource highlightColor}"
    BorderColor = "{StaticResource borderColor}"
    BorderWidth = "{StaticResource borderWidth}"
    TextColor = "{StaticResource textColor}" />

<Button
    Text = "Cancel"
    BackgroundColor = "{StaticResource highlightColor}"
    BorderColor = "{StaticResource borderColor}"
    BorderWidth = "{StaticResource borderWidth}"
    TextColor = "{StaticResource textColor}" />

ボタンのそれぞれに同じ 5 つのプロパティがどのように設定されているかに注意してください。 リソースを使用すると、そのうち 4 つで、繰り返しのハードコーディングされた値が不要になります。 しかし、この種の XAML マークアップはすぐに読みにくくなります。 また、コントロールごとに多数のプロパティを設定している場合は、そのうちの 1 つを誤って省略してしまい、コントロールの外観に不整合が生じるおそれがあります。 解決策は、一度に 4 つすべてのプロパティを割り当てるスタイルを作成することです。

セッターとは

セッター は、スタイルの作成に使用する重要なコンポーネントです。

セッターは、プロパティと値のペア用のコンテナーです。 代入ステートメントを表すものとして、セッターを考えることができます。 割り当てるプロパティおよび適用する値を指定します。 通常、XAML マークアップで Setter オブジェクトを作成します。 次の例では、TextColor プロパティの Setter オブジェクトを作成します。

<Setter Property="TextColor" Value="White" />

次のコードに示すように、セッターの値にリソースを使用できます。 この手法は、複数のセッターに同じ値を使用する場合に最適です。

<Setter Property="TextColor" Value="{StaticResource textColor}" />

Note

セッターで指定したプロパティ値は、バインド可能なプロパティとして実装する必要があります。 サフィックス Property で終わる .NET MAUI のコントロールのプロパティはすべて、バインド可能なプロパティです。 セッターで TextColor などのプロパティを使用しようとしている場合は、そのコントロールに対して TextColorProperty という名前の対応するバインド可能なプロパティがあることを確認します。 実際には、セッター内で使用するほぼすべてのプロパティは、この方法で実装されます。

スタイルとは

スタイルとは、特定のタイプのコントロールで対象とされるセッターのコレクションです。 .NET MAUI では、セッターのプロパティがターゲットの種類に存在することを確認できるように、そのターゲットの種類が必要です。

次のコードは、前の例の 4 つの値を結合するスタイルを示しています。 TargetTypeButton に設定され、セッター内のすべてのプロパティが Button クラスのメンバーであることに注意してください。 ラベルにこのスタイルを使用することはできません。Label クラスには BorderColor プロパティまたは BorderWidth プロパティが 含まれていないためです。

<Style TargetType="Button">
    <Setter Property="BackgroundColor" Value="#2A84D3" />
    <Setter Property="BorderColor" Value="#1C5F9B" />
    <Setter Property="BorderWidth" Value="3" />
    <Setter Property="TextColor" Value="White" />
</Style>

スタイルの定義

通常、スタイルは ResourceDictionary オブジェクト内のリソースとして定義します。 リソース ディクショナリを使用すると、同じページ内またはアプリケーション全体でも複数のコントロールにわたってスタイルを使いやすくなります。 次のコードは、ディクショナリ内にリソースとしてスタイルを定義する方法を示しています。 スタイルには、x:Key プロパティを使用して名前が付けられます。 スタイルに名前を付けると、XAML ページ内からスタイルを参照できます。

<ContentPage.Resources>
    <Style x:Key="MyButtonStyle" TargetType="Button">
        ...
    </Style>
</ContentPage.Resources>

スタイルを適用する

スタイルをコントロールにアタッチするには、名前を Style プロパティに割り当てます。 この割り当てによって、スタイルの Setter オブジェクトのそれぞれがターゲット コントロールに適用されます。 次のコードは、ボタン スタイルを 2 つのボタンに適用する方法を示しています。

<Button Text="OK" Style="{StaticResource MyButtonStyle}" />
<Button Text="Cancel" Style="{StaticResource MyButtonStyle}" />

前の例では、StaticResource マークアップ拡張機能を使用して、スタイルをコントロールにアタッチしました。 実行時に変更するスタイルが不要な場合には、この手法は最適です。 しかし、UI を変更する必要がある動的なテーマのような何かを実装する場合にはどうすればいいでしょうか。 この場合、DynamicResource マークアップ拡張機能を使用してスタイルを読み込むことができます。

<Button Text="Cancel" Style="{DynamicResource MyButtonStyle}" />

DynamicResource は、リソース ディクショナリ内の x:Key プロパティの置換をリッスンします。 新しいスタイルを同じ x:Key 値を含む ResourceDictionary に読み込むコードを記述する場合、新しいスタイルは自分の UI に自動的に適用されます。

複数のコントロールに暗黙的なスタイルを使用する

ご利用の UI にボタンが 50 個あり、それらすべてに同じスタイルを適用する必要があるとします。 これまでの知識では、各ボタンの Style プロパティに手動で割り当てる必要があります。 難しくはありませんが、それでも面倒です。

暗黙的なスタイルとは、リソース ディクショナリに x:Key 識別子を指定せずに追加するスタイルです。 暗黙的なスタイルは、TargetType オブジェクトのすべてのコントロールに自動的に適用されます。

次のコードは、暗黙的なスタイルとして宣言された前の例を示しています。 このスタイルは、ページのすべてのボタンに適用されます。

<ContentPage.Resources>
    <Style TargetType="Button">
        <Setter Property="BackgroundColor" Value="Blue" />
        <Setter Property="BorderColor" Value="Navy" />
        ...
    </Style>
</ContentPage.Resources>

重要

暗黙的なスタイルとコントロールの一致には、TargetType が完全に一致している必要があります。 対象の型から継承するコントロールは、スタイルを受け取りません。 継承されたコントロールに影響を与えるには、スタイルを定義するときに Style.ApplyToDerivedTypes 属性を True に設定します。 たとえば、Button 型にスタイルを適用し、Button から継承する任意のボタン (ImageButtonRadioButton、作成したカスタム型など) に影響を与えるには、このようなスタイルを使用できます。

<ContentPage.Resources>
    <Style TargetType="Button"
           ApplyToDerivedTypes="True">
        <Setter Property="BackgroundColor" Value="Black" />
    </Style>
</ContentPage.Resources>

スタイルのオーバーライド

スタイルではコントロールに既定値のセットを提供すると考えることができます。 既存のスタイルは要件に近い場合がありますが、必要のないセッターが 1 つか 2 つ含まれています。 その場合は、スタイルを適用してからプロパティを直接設定して、値をオーバーライドできます。 明示的な設定はスタイルの後に適用されるため、スタイルの値がオーバーライドされます。

ページにいくつかのボタンに次のスタイルを使用するとします。

<Style x:Key="MyButtonStyle" TargetType="Button">
    <Setter Property="BackgroundColor" Value="Blue" />
    <Setter Property="BorderRadius" Value="10" />
    <Setter Property="BorderWidth" Value="3" />
</Style>

このスタイルは、背景を赤にする必要がある [キャンセル] 以外のすべてのボタンで利用できるとします。 BackgroundColor プロパティを直接設定する限り、[キャンセル] ボタンにも同じスタイルを使用できます。 次のコードは、色の設定をオーバーライドする方法を示しています。

<Button
    Text="Cancel"
    Style="{StaticResource MyButtonStyle}"
    BackgroundColor="Red"
    ... />

親要素の型をターゲットにする

ボタンとラベルにカスタムの背景色が必要であるとします。 型ごとに個別のスタイルを作成することも、TargetType を VisualElement に設定して 1 つのスタイルを作成することもできます。 この手法は、VisualElementButtonLabel の両方の基底クラスであるため、利用できます。

次のコードは、2 つの異なる派生型に適用される基底クラスを対象とするスタイルを示しています。

<Style x:Key="MyVisualElementStyle" TargetType="VisualElement">
    <Setter Property="BackgroundColor" Value="#2A84D3" />
</Style>
...
<Button Style="{StaticResource MyVisualElementStyle}" ... />
<Label Style="{StaticResource MyVisualElementStyle}" ... />

この例では、x:Key を使用してスタイルを識別し、コントロールで明示的に適用します。 暗黙的なスタイルの TargetType はコントロールの種類に完全に一致する必要があるため、暗黙的なスタイルはここでは機能しません。

BasedOn を使用してスタイルから継承する

UI に一貫した外観を作りたいとします。 すべてのコントロールで一貫性のある背景色を使用することに決定します。 背景色の設定は、複数のスタイルで使用される可能性が高いです。 次のコードは、セッターの繰り返しを含む 2 つのスタイルを示しています。

<Style x:Key="MyButtonStyle" TargetType="Button">
    <Setter Property="BackgroundColor" Value="Blue" />
    <Setter Property="BorderColor" Value="Navy" />
    <Setter Property="BorderWidth" Value="5" />
</Style>

<Style x:Key="MyEntryStyle" TargetType="Entry">
    <Setter Property="BackgroundColor" Value="Blue" />
    <Setter Property="TextColor" Value="White" />
</Style>

スタイルの継承を使用して、その重複するセッターを基本スタイルに抽出できます。 派生スタイルを作成するには、基本スタイルを参照するように BasedOn プロパティを設定します。 新しいスタイルは、その基本スタイルからすべてのセッターを継承します。 派生スタイルでは、新しいセッターを追加したり、継承したセッターを別の値を含むセッターに置き換えたりすることもできます。

次のコードは、前の例のスタイルを階層にリファクタリングしたものを示しています。 一般的なセッターは、繰り返されず、基本スタイルでのみ表示されます。 StaticResource マークアップ拡張機能を使用して、基本スタイルを検索していることに注意してください。 この状況では、DynamicResource を使用することはできません。

<Style x:Key="MyVisualElementStyle" TargetType="VisualElement">
    <Setter Property="BackgroundColor" Value="Blue" />
</Style>

<Style x:Key="MyButtonStyle" TargetType="Button" BasedOn="{StaticResource MyVisualElementStyle}">
    <Setter Property="BorderColor" Value="Navy" />
    <Setter Property="BorderWidth" Value="5" />
</Style>

<Style x:Key="MyEntryStyle" TargetType="Entry" BasedOn="{StaticResource MyVisualElementStyle}">
    <Setter Property="TextColor" Value="White" />
</Style>

基本スタイルと派生スタイルの TargetType 値は、互換性がある必要があります。 スタイルが互換性を持つようにするには、同じ TargetType プロパティを持っているか、派生スタイルの TargetType が基本スタイルの TargetType の子孫である必要があります。