XAML を使用してアプリをスタイル設定する
.NET Multi-Platform App UI (.NET MAUI) アプリには、多くの場合、外観が同じ複数のコントロールが含まれています。 たとえば、1 つのアプリに、同じフォント オプションとレイアウト オプションを持つ複数の Label インスタンスがある場合があります。
<Label Text="These labels"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
<Label Text="are not"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
<Label Text="using styles"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
この例では、各 Label オブジェクトには、Label で表示されるテキストの外観を制御するための同じプロパティ値があります。 しかし、各コントロールの外観を設定することは、繰り返しが多く、エラーが発生しやすくなります。 代わりに、外観を定義するスタイルを作成でき、必要なコントロールに適用できます。
スタイルの概要
Style クラスを使用してアプリをスタイル設定し、プロパティ値のコレクションを 1 つのオブジェクトにグループ化して、複数のビジュアル要素に適用できるようにします。 これにより、繰り返されるマークアップを減らして、アプリの外観をより簡単に変更できるようにします。
スタイルは主に XAML ベースのアプリ用に設計されていますが、C# で作成することもできます。
- XAML で作成された Style オブジェクトは、一般的に ResourceDictionary で定義されます。これは、コントロール、ページの
Resources
コレクション、またはアプリのResources
コレクションに割り当てられます。 - C# で作成された Style オブジェクトは、通常、ページのクラス、またはグローバルにアクセスできるクラスで定義されます。
Style を定義する場所の選択は、それを使用できる場所に影響します。
- コントロール レベルで定義された Style インスタンスは、ページとその子にのみ適用できます。
- ページ レベルで定義された Style インスタンスは、ページとその子にのみ適用できます。
- アプリ レベルで定義された Style インスタンスは、アプリ全体に適用できます。
各 Style オブジェクトには、1 つ以上の Setter オブジェクトのコレクションが含まれています。Setter にはそれぞれ Property
と Value
があります。 Property
は、スタイルが適用される要素のバインド可能なプロパティの名前で、Value
はプロパティに適用される値です。
各 Style オブジェクトは、明示的または暗黙的に指定できます。
- 明示的なStyle オブジェクトは、
TargetType
とx:Key
の値を指定し、ターゲット要素の Style プロパティをx:Key
参照に設定することによって定義されます。 詳細については、「Explicit styles」をご覧ください。 - 暗黙的な Style オブジェクトは、
TargetType
のみを指定することで定義されます。 その後、Style オブジェクトはその型のすべての要素に自動的に適用されます。 ただし、TargetType
のサブクラスには Style は自動的には適用されません。 詳細については、「Implicit styles」をご覧ください。
Style を作成する場合は、TargetType
プロパティが常に必要です。 次のコード例は、明示的なスタイルを示しています。
<Style x:Key="labelStyle" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
</Style>
Style を適用するには、ターゲット オブジェクトが Style の TargetType
プロパティ値と一致する VisualElement である必要があります。
<Label Text="Demonstrating an explicit style" Style="{StaticResource labelStyle}" />
ビュー階層で下位にあるスタイルは、上位の定義済みスタイルよりも優先されます。 たとえば、アプリ レベルで Label.TextColor
を Red
に設定する Style を設定すると、Label.TextColor
を Green
に設定するページ レベルのスタイルによってオーバーライドされます。 同様に、ページ レベルのスタイルはコントロール レベルのスタイルによってオーバーライドされます。 さらに、Label.TextColor
がコントロール プロパティに直接設定されている場合は、どのスタイルよりも優先されます。
スタイルはプロパティの変更に応答せず、アプリの実行中は変更されません。 ただし、アプリは動的リソースを使用して実行時にスタイルの変更に動的に応答できます。 詳細については、「Dynamic styles」をご覧ください。
明示的なスタイル
ページ レベルで Style を作成するには、ResourceDictionary をページに追加し、次に 1 つ以上の Style の宣言を ResourceDictionary に含めます。 Style は、宣言に x:Key
属性を指定することで明示的に行われ、ResourceDictionary 内で説明を含むキーを指定します。 明示的なスタイルは、Style プロパティを設定して特定のビジュアル要素に適用する必要があります。
次の例は、ResourceDictionary ページの明示的なスタイルを示し、Label ページのオブジェクトに適用します。
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="labelRedStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Red" />
</Style>
<Style x:Key="labelGreenStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Green" />
</Style>
<Style x:Key="labelBlueStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Blue" />
</Style>
</ContentPage.Resources>
<StackLayout>
<Label Text="These labels"
Style="{StaticResource labelRedStyle}" />
<Label Text="are demonstrating"
Style="{StaticResource labelGreenStyle}" />
<Label Text="explicit styles,"
Style="{StaticResource labelBlueStyle}" />
<Label Text="and an explicit style override"
Style="{StaticResource labelBlueStyle}"
TextColor="Teal" />
</StackLayout>
</ContentPage>
この例では、ResourceDictionary はページの Label オブジェクトに明示的に設定される 3 つのスタイルを定義します。 それぞれ Style を使用して、テキストを異なる色で表示しながら、フォント サイズ、水平方向と垂直方向のレイアウト オプションも設定します。 StaticResource
マークアップ拡張機能を使用して Style プロパティを設定することで、それぞれの Style は異なる Label に適用されます。 さらに、最終の Label には、Style が設定されていますが、TextColor
プロパティも別の Color 値にオーバーライドされます。
暗黙的なスタイル
ページ レベルで Style を作成するには、ResourceDictionary をページに追加し、次に 1 つ以上の Style の宣言を ResourceDictionary に含めます。 Style は、x:Key
属性を指定しないことによって暗黙的に行われます。 スタイルは、TargetType
に正確に一致するスコープ内のビジュアル要素に適用されますが、TargetType
値から派生した要素には適用されません。
次のコード例は、ページの ResourceDictionary 内の暗黙的なスタイルを示し、ページの Entry オブジェクトに適用されます。
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="Entry">
<Setter Property="HorizontalOptions" Value="Fill" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="BackgroundColor" Value="Yellow" />
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Blue" />
</Style>
</ContentPage.Resources>
<StackLayout>
<Entry Text="These entries" />
<Entry Text="are demonstrating" />
<Entry Text="implicit styles," />
<Entry Text="and an implicit style override"
BackgroundColor="Lime"
TextColor="Red" />
<local:CustomEntry Text="Subclassed Entry is not receiving the style" />
</StackLayout>
</ContentPage>
この例では、ResourceDictionary は、ページの Entry オブジェクトに暗黙的に設定される、単一の暗黙的スタイルを定義します。 Style は、他の外観オプションを設定しながら、黄色の背景に青色のテキストを表示するために使用されます。 x:Key
属性を指定せずに、Style がページの ResourceDictionary に追加されます。 そのため、Style は、すべての Entry オブジェクトに暗黙的に適用されます。これらのオブジェクトは、Style の TargetType
プロパティと正確に一致するからです。 ただし、Style は、サブクラス化された Entry である CustomEntry
オブジェクトには適用されません。 さらに、4 番目の Entry は、スタイルの BackgroundColor
プロパティと TextColor
プロパティを別の Color 値にオーバーライドします。
スタイルを派生型に適用
Style.ApplyToDerivedTypes
プロパティを使用すると、TargetType
プロパティによって参照される基本タイプから派生したコントロールに、スタイルを適用できます。 そのため、このプロパティを true
に設定すると、タイプが TargetType
プロパティで指定された基本タイプから派生する場合、単一のスタイルで複数のタイプをターゲティングできるようになります。
次の例は、Button インスタンスの背景色を赤色に設定する、暗黙的なスタイルを示しています。
<Style TargetType="Button"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="Red" />
</Style>
このスタイルをページ レベルの ResourceDictionary に配置すると、ページ上のすべての Button オブジェクトと、Button から派生するコントロールに適用されます。 ただし、ApplyToDerivedTypes
プロパティが設定されていない場合、スタイルは Button オブジェクトにのみ適用されます。
グローバル スタイル
スタイルをアプリのリソース ディクショナリに追加し、グローバルに定義できます。 これらのスタイルは、アプリ全体で使用でき、ページやコントロール間でのスタイルの重複を回避するのに役立ちます。
次の例は、アプリレベルで定義された Style を示しています。
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Styles"
x:Class="Styles.App">
<Application.Resources>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="HorizontalOptions"
Value="Center" />
<Setter Property="VerticalOptions"
Value="CenterAndExpand" />
<Setter Property="BorderColor"
Value="Lime" />
<Setter Property="CornerRadius"
Value="5" />
<Setter Property="BorderWidth"
Value="5" />
<Setter Property="WidthRequest"
Value="200" />
<Setter Property="TextColor"
Value="Teal" />
</Style>
</Application.Resources>
</Application>
この例では、ResourceDictionary は、単一の明示的なスタイルである buttonStyle
を定義します。このスタイルは、Button オブジェクトの外観を設定するために使用されます。
Note
グローバル スタイルは、明示的または暗黙的に定義できます。
次の例は、ページの Button オブジェクトの buttonStyle
を使用するページを示しています。
<ContentPage ...>
<StackLayout>
<Button Text="These buttons"
Style="{StaticResource buttonStyle}" />
<Button Text="are demonstrating"
Style="{StaticResource buttonStyle}" />
<Button Text="application styles"
Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage>
スタイルの継承
スタイルは、他のスタイルから継承して重複を減らし、再利用できます。 これを行うには、Style.BasedOn
プロパティを既存の Style に設定します。 XAML でこれを行うには、BasedOn
プロパティを StaticResource
マークアップ拡張に設定します。このマークアップ拡張は、以前に作成した Style を参照します。
基本スタイルから継承するスタイルには、新しいプロパティの Setter インスタンスを含めたり、それらのスタイルを使用して基本スタイルのセッターをオーバーライドしたりできます。 さらに、基本スタイルを継承するスタイルは、同じタイプ、または基本スタイルの対象となるタイプから派生したタイプをターゲットにする必要があります。 たとえば、基本スタイルが View オブジェクトをターゲットとする場合、基本スタイルに基づくスタイルは、View オブジェクト、または View クラスから派生するタイプ (Label オブジェクトや Button オブジェクトなど) をターゲットにすることができます。
スタイルは、ビュー階層内の同じレベル以上のスタイルからのみ継承できます。 これは、次のことを意味します。
- アプリ レベルのスタイルは、他のアプリ レベルのスタイルからのみ継承できます。
- ページ レベルのスタイルは、アプリ レベルのスタイルやその他のページ レベルのスタイルから継承できます。
- コントロール レベルのスタイルは、アプリ レベルのスタイル、ページ レベルのスタイル、その他のコントロール レベルのスタイルから継承できます。
次の例は、明示的なスタイルの継承を示しています。
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</ContentPage.Resources>
<StackLayout>
<StackLayout.Resources>
<Style x:Key="labelStyle"
TargetType="Label"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Teal" />
</Style>
<Style x:Key="buttonStyle"
TargetType="Button"
BasedOn="{StaticResource baseStyle}">
<Setter Property="BorderColor" Value="Lime" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="BorderWidth" Value="5" />
<Setter Property="WidthRequest" Value="200" />
<Setter Property="TextColor" Value="Teal" />
</Style>
</StackLayout.Resources>
<Label Text="This label uses style inheritance"
Style="{StaticResource labelStyle}" />
<Button Text="This button uses style inheritance"
Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage>
この例では、baseStyle
は View オブジェクトをターゲットにし、HorizontalOptions
プロパティと VerticalOptions
プロパティを設定します。 baseStyle
は、どのコントロールにも直接設定されません。 代わりに、labelStyle
と buttonStyle
がそれを継承し、追加のバインド可能なプロパティ値を設定します。 次に、labelStyle
オブジェクトと buttonStyle
オブジェクトが、Label と Button に設定されます。
重要
暗黙的なスタイルは明示的なスタイルから派生できますが、明示的なスタイルは暗黙的なスタイルから派生できません。
動的なスタイル
スタイルはプロパティの変更に応答せず、アプリの実行中は変更されません。 たとえば、Style をビジュアル要素に割り当てた後、Setter オブジェクトの 1 つが変更、削除、または新しい Setter が追加された場合、それらの変更はビジュアル要素に適用されません。 ただし、アプリは動的リソースを使用して実行時にスタイルの変更に動的に応答できます。
DynamicResource
マークアップ拡張は、辞書キーを使用して ResourceDictionary から値をフェッチするという点で、StaticResource
マークアップ拡張と似ています。 ただし、StaticResource
は単一の辞書検索を実行しますが、DynamicResource
は辞書キーへのリンクを維持します。 そのため、キーに関連付けられている辞書エントリが置き換えられると、変更がビジュアル要素に適用されます。 これにより、ランタイム スタイルの変更をアプリで行えます。
次の例は、動的スタイルを示しています。
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
<Style x:Key="blueSearchBarStyle"
TargetType="SearchBar"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="PlaceholderColor" Value="Blue" />
</Style>
<Style x:Key="greenSearchBarStyle"
TargetType="SearchBar">
<Setter Property="FontAttributes" Value="None" />
<Setter Property="PlaceholderColor" Value="Green" />
</Style>
</ContentPage.Resources>
<StackLayout>
<SearchBar Placeholder="SearchBar demonstrating dynamic styles"
Style="{DynamicResource blueSearchBarStyle}" />
</StackLayout>
</ContentPage>
この例では、SearchBar オブジェクトが DynamicResource
マークアップ拡張を使用して、blueSearchBarStyle
という名前の Style を設定しています。 その後、SearchBar の Style の定義はコードで更新できます。
Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];
この例では、greenSearchBarStyle
の定義の値を使用するように blueSearchBarStyle
の定義を更新しています。 このコードが実行されると、SearchBar は greenSearchBarStyle
で定義された Setter オブジェクトを使用するように更新されます。
動的スタイルの継承
Style.BasedOn
プロパティを使用して動的スタイルからスタイルを派生させることはできません。 その代わりに、Style クラスには BaseResourceKey
プロパティが含まれています。このプロパティを、値を動的に変更できるディクショナリ キーに設定できます。
次の例は、動的スタイルの継承を示しています。
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
<Style x:Key="blueSearchBarStyle"
TargetType="SearchBar"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Blue" />
</Style>
<Style x:Key="greenSearchBarStyle"
TargetType="SearchBar">
<Setter Property="FontAttributes" Value="None" />
<Setter Property="TextColor" Value="Green" />
</Style>
<Style x:Key="tealSearchBarStyle"
TargetType="SearchBar"
BaseResourceKey="blueSearchBarStyle">
<Setter Property="BackgroundColor" Value="Teal" />
<Setter Property="CancelButtonColor" Value="White" />
</Style>
</ContentPage.Resources>
<StackLayout>
<SearchBar Text="SearchBar demonstrating dynamic style inheritance"
Style="{StaticResource tealSearchBarStyle}" />
</StackLayout>
</ContentPage>
この例では、SearchBar オブジェクトは StaticResource
マークアップ拡張を使用して、tealSearchBarStyle
という名前の Style を参照します。 この Style には、いくつかの追加のプロパティが設定されていて、BaseResourceKey
プロパティを使用して blueSearchBarStyle
を参照します。 tealSearchBarStyle
が変更されないので、派生元となる Style を除き、DynamicResource
マークアップ拡張は必要ありません。 したがって、tealSearchBarStyle
は blueSearchBarStyle
へのリンクを維持し、基本スタイルの変更時に更新されます。
blueSearchBarStyle
の定義はコードで更新できます。
Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];
この例では、greenSearchBarStyle
の定義の値を使用するように blueSearchBarStyle
の定義を更新しています。 このコードが実行されると、SearchBar は greenSearchBarStyle
で定義された Setter オブジェクトを使用するように更新されます。
スタイル クラス
スタイル クラスを使用すると、スタイルの継承に頼ることなく、複数のスタイルをコントロールに適用できます。
スタイル クラスは、Style の Class
プロパティを、クラス名を表す string
に設定することで作成できます。 x:Key
属性を使用して明示的なスタイルを定義する場合と比べて、この利点は、複数のスタイル クラスを VisualElement に適用できることです。
重要
複数のスタイルが異なる型を対象としている場合、同じクラス名を共有できます。 これにより、同じ名前を持つ複数のスタイル クラスで、異なる型を対象とすることができます。
次の例は、3 つの BoxView スタイル クラスと 1 つの VisualElement スタイル クラスを示しています。
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="BoxView"
Class="Separator">
<Setter Property="BackgroundColor"
Value="#CCCCCC" />
<Setter Property="HeightRequest"
Value="1" />
</Style>
<Style TargetType="BoxView"
Class="Rounded">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="10" />
</Style>
<Style TargetType="BoxView"
Class="Circle">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="WidthRequest"
Value="100" />
<Setter Property="HeightRequest"
Value="100" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="50" />
</Style>
<Style TargetType="VisualElement"
Class="Rotated"
ApplyToDerivedTypes="true">
<Setter Property="Rotation"
Value="45" />
</Style>
</ContentPage.Resources>
</ContentPage>
この例では、Separator
、Rounded
、Circle
の各スタイル クラスの BoxView プロパティが、個別の値に設定されています。 Rotated
スタイル クラスの TargetType
は VisualElement で、これは VisualElement インスタンスにのみ適用できることを意味します。 ただし、その ApplyToDerivedTypes
プロパティは true
に設定されているので、VisualElement から派生する BoxView などのすべてのコントロールに適用できます。 派生型にスタイルを適用する方法について詳しくは、「スタイルを派生型に適用」をご覧ください。
スタイル クラスを使用するには、コントロールの StyleClass
プロパティ (IList<string>
型) に、スタイル クラス名のリストを設定します。 コントロールの型がスタイル クラスの TargetType
と一致すれば、そのスタイル クラスが適用されます。
次の例では、3 つの BoxView インスタンスに、それぞれ異なるスタイル クラスを設定しています。
<ContentPage ...>
<ContentPage.Resources>
...
</ContentPage.Resources>
<StackLayout>
<BoxView StyleClass="Separator" />
<BoxView WidthRequest="100"
HeightRequest="100"
HorizontalOptions="Center"
StyleClass="Rounded, Rotated" />
<BoxView HorizontalOptions="Center"
StyleClass="Circle" />
</StackLayout>
</ContentPage>
この例では、1 つ目の BoxView は行区切りのスタイルになり、3 つ目の BoxView は円形になります。 2 つ目の BoxView には 2 つのスタイル クラスが適用されていて、角が丸くなり、45 度回転します。
重要
StyleClass
プロパティは IList<string>
型であるため、コントロールに複数のスタイル クラスを適用できます。 この場合、スタイル クラスはリストの昇順に適用されます。 したがって、複数のスタイル クラスに同一のプロパティが設定されている場合、リストの最上位にあるスタイル クラスのプロパティが優先されます。
.NET MAUI
フィードバック
https://aka.ms/ContentUserFeedback。
近日公開予定: 2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub イシューを段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、以下を参照してください:フィードバックの送信と表示