Xamarin.Forms のコントロール テンプレート
Xamarin.Forms のコントロール テンプレートを使うと、ContentView
派生カスタム コントロールと ContentPage
派生ページのビジュアル構造を定義できます。 コントロール テンプレートを使うと、カスタム コントロールまたはページのユーザー インターフェイス (UI) を、コントロールまたはページを実装するロジックから分離できます。 また、事前に定義した場所にあるテンプレート化されたカスタム コントロールまたはテンプレート化されたページに追加のコンテンツを挿入することもできます。
たとえば、カスタム コントロールで提供された UI を再定義するコントロール テンプレートを作成できます。 コントロール テンプレートは、必要なカスタム コントロール インスタンスで使用できます。 または、アプリケーションの複数のページで使用される共通の UI を定義するコントロール テンプレートを作成できます。 コントロール テンプレートを複数のページに使用し、さらに各ページに固有のコンテンツを表示することができます。
ControlTemplate の作成
次の例は、CardView
カスタム コントロールのコードを示しています。
public class CardView : ContentView
{
public static readonly BindableProperty CardTitleProperty = BindableProperty.Create(nameof(CardTitle), typeof(string), typeof(CardView), string.Empty);
public static readonly BindableProperty CardDescriptionProperty = BindableProperty.Create(nameof(CardDescription), typeof(string), typeof(CardView), string.Empty);
// ...
public string CardTitle
{
get => (string)GetValue(CardTitleProperty);
set => SetValue(CardTitleProperty, value);
}
public string CardDescription
{
get => (string)GetValue(CardDescriptionProperty);
set => SetValue(CardDescriptionProperty, value);
}
// ...
}
ContentView
クラスから派生した CardView
クラスは、カードのようなレイアウトでデータを表示するカスタム コントロールを表します。 このクラスには、表示されるデータのプロパティが含まれており、バインド可能なプロパティに基づいています。 ただし、CardView
クラスを使って UI を定義することはできません。 代わりに、コントロール テンプレートを使って UI を定義します。 ContentView
派生カスタム コントロールの作成の詳細については、「Xamarin.Forms ContentView」を参照してください。
コントロール テンプレートは、ControlTemplate
型を使用して作成されます。 ControlTemplate
を作成するときは、View
オブジェクトを組み合わせてカスタム コントロールまたはページの UI を構築します。 ControlTemplate
は、ルート要素として View
を 1 つだけ持つ必要があります。 ただし、通常、ルート要素には他の View
オブジェクトが含まれます。 複数のオブジェクトを組み合わせて、コントロールの視覚的な構造を構成します。
ControlTemplate
をインラインで定義することはできますが、リソース ディクショナリのリソースとして ControlTemplate
を宣言する方法が一般的です。 コントロール テンプレートはリソースなので、すべてのリソースに適用されるものと同じスコープ規則に従います。 たとえば、アプリケーション定義 XAML ファイルのルート要素でコントロール テンプレートを宣言すると、そのテンプレートはアプリケーションのどこでも使用できます。 ページでテンプレートを定義すると、そのページでのみ、コントロール テンプレートを使用できます。 リソースの詳細については、「Xamarin.Forms のリソース ディクショナリ」を参照してください。
次の XAML の例は、CardView
オブジェクトの ControlTemplate
を示しています。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
...>
<ContentPage.Resources>
<ControlTemplate x:Key="CardViewControlTemplate">
<Frame BindingContext="{Binding Source={RelativeSource TemplatedParent}}"
BackgroundColor="{Binding CardColor}"
BorderColor="{Binding BorderColor}"
CornerRadius="5"
HasShadow="True"
Padding="8"
HorizontalOptions="Center"
VerticalOptions="Center">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75" />
<RowDefinition Height="4" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Frame IsClippedToBounds="True"
BorderColor="{Binding BorderColor}"
BackgroundColor="{Binding IconBackgroundColor}"
CornerRadius="38"
HeightRequest="60"
WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="{Binding IconImageSource}"
Margin="-20"
WidthRequest="100"
HeightRequest="100"
Aspect="AspectFill" />
</Frame>
<Label Grid.Column="1"
Text="{Binding CardTitle}"
FontAttributes="Bold"
FontSize="Large"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Start" />
<BoxView Grid.Row="1"
Grid.ColumnSpan="2"
BackgroundColor="{Binding BorderColor}"
HeightRequest="2"
HorizontalOptions="Fill" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{Binding CardDescription}"
VerticalTextAlignment="Start"
VerticalOptions="Fill"
HorizontalOptions="Fill" />
</Grid>
</Frame>
</ControlTemplate>
</ContentPage.Resources>
...
</ContentPage>
ControlTemplate
がリソースとして宣言される場合、リソース ディクショナリで識別できるように、x:Key
属性を使用して指定したキーが必要です。 この例では、CardViewControlTemplate
のルート要素は Frame
オブジェクトです。 Frame
オブジェクトでは、RelativeSource
マークアップ拡張機能を使用して、その BindingContext
をテンプレートが適用されるランタイム オブジェクト インスタンスに設定します。これは、"テンプレート化された親" と呼ばれます。 CardView
オブジェクトのビジュアル構造を定義するために、Frame
オブジェクトには Grid
、Frame
、Image
、Label
、および BoxView
の組み合わせが使用されます。 このようなオブジェクトのバインド式を使うと、ルート Frame
エレメントから BindingContext
が継承されるため、CardView
プロパティに対して解決されます。 RelativeSource
マークアップ拡張機能の詳細については、「Xamarin.Forms の相対的なバインディング」を参照してください。
ControlTemplate を使用する
ControlTemplate
を ContentView
派生カスタム コントロールに適用するには、その ControlTemplate
プロパティをコントロール テンプレート オブジェクトに設定します。 同様に、ControlTemplate
を ContentPage
派生ページに適用するには、その ControlTemplate
プロパティをコントロール テンプレート オブジェクトに設定します。 実行時に ControlTemplate
が適用されると、ControlTemplate
で定義されたすべてのコントロールがテンプレート化されたカスタム コントロールまたはテンプレート化されたページのビジュアル ツリーに追加されます。
次の例は、各 CardView
オブジェクトの ControlTemplate
プロパティに割り当てられた CardViewControlTemplate
を示します。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:ControlTemplateDemos.Controls"
...>
<StackLayout Margin="30">
<controls:CardView BorderColor="DarkGray"
CardTitle="John Doe"
CardDescription="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla elit dolor, convallis non interdum."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource CardViewControlTemplate}" />
<controls:CardView BorderColor="DarkGray"
CardTitle="Jane Doe"
CardDescription="Phasellus eu convallis mi. In tempus augue eu dignissim fermentum. Morbi ut lacus vitae eros lacinia."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource CardViewControlTemplate}" />
<controls:CardView BorderColor="DarkGray"
CardTitle="Xamarin Monkey"
CardDescription="Aliquam sagittis, odio lacinia fermentum dictum, mi erat scelerisque erat, quis aliquet arcu."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource CardViewControlTemplate}" />
</StackLayout>
</ContentPage>
この例では、CardViewControlTemplate
のコントロールは各 CardView
オブジェクトのビジュアルツリーの一部になります。 コントロール テンプレートのルート Frame
オブジェクトによって、その BindingContext
はテンプレート化された親に設定されるため、Frame
とその子では、各 CardView
オブジェクトのプロパティに対するバインディング式は解決されます。
次のスクリーンショットは、3 つの CardView
オブジェクトに適用された CardViewControlTemplate
を示します。
重要
ControlTemplate
がコントロール インスタンスに適用された時点を検出するには、テンプレート化されたカスタム コントロールまたはテンプレート化されたページで OnApplyTemplate
メソッドをオーバーライドします。 詳細については、「テンプレートから名前付き要素を取得する」を参照してください。
TemplateBinding を使用してパラメーターを渡す
TemplateBinding
マークアップ拡張機能を使うと、ControlTemplate
内にある要素のプロパティを、テンプレート化されたカスタム コントロールまたはテンプレート化されたページによって定義されたパブリック プロパティにバインドできます。 TemplateBinding
を使うと、コントロールのプロパティをテンプレートのパラメーターとして機能させることができます。 そのため、テンプレート化されたカスタム コントロールまたはテンプレート化されたページのプロパティを設定すると、TemplateBinding
が指定された要素にその値が渡されます。
重要
TemplateBinding
マークアップ式を使用すると、前のコントロール テンプレートからの RelativeSource
バインドを削除し、Binding
式を置き換えることができます。
TemplateBinding
マークアップ拡張機能を使うと、次のプロパティを定義できます。
Path
(string
型)。プロパティのパス。Mode
(BindingMode
型)。"ソース" と "ターゲット" の間で変更が反映される方向。Converter
(IValueConverter
型)。バインディング値コンバーター。ConverterParameter
(object
型)。バインディング値コンバーターへのパラメーター。StringFormat
(string
型)。バインディングの文字列形式。
TemplateBinding
マークアップ拡張機能の ContentProperty
は Path
です。 そのため、マークアップ拡張機能の "Path =" 部分は、そのパスが TemplateBinding
式の最初の項目である場合、省略できます。 バインディング式でこれらのプロパティを使用することの詳細については、「Xamarin.Forms のデータ バインディング」を参照してください。
警告
TemplateBinding
マークアップ拡張機能は、ControlTemplate
内でのみ使用することをお勧めします。 ただし、ControlTemplate
の外部で TemplateBinding
式を使用しようとしても、ビルド エラーや例外はスローされません。
次の XAML の例は、TemplateBinding
マークアップ拡張機能を使用する CardView
オブジェクトの ControlTemplate
を示します。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
...>
<ContentPage.Resources>
<ControlTemplate x:Key="CardViewControlTemplate">
<Frame BackgroundColor="{TemplateBinding CardColor}"
BorderColor="{TemplateBinding BorderColor}"
CornerRadius="5"
HasShadow="True"
Padding="8"
HorizontalOptions="Center"
VerticalOptions="Center">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75" />
<RowDefinition Height="4" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Frame IsClippedToBounds="True"
BorderColor="{TemplateBinding BorderColor}"
BackgroundColor="{TemplateBinding IconBackgroundColor}"
CornerRadius="38"
HeightRequest="60"
WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="{TemplateBinding IconImageSource}"
Margin="-20"
WidthRequest="100"
HeightRequest="100"
Aspect="AspectFill" />
</Frame>
<Label Grid.Column="1"
Text="{TemplateBinding CardTitle}"
FontAttributes="Bold"
FontSize="Large"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Start" />
<BoxView Grid.Row="1"
Grid.ColumnSpan="2"
BackgroundColor="{TemplateBinding BorderColor}"
HeightRequest="2"
HorizontalOptions="Fill" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{TemplateBinding CardDescription}"
VerticalTextAlignment="Start"
VerticalOptions="Fill"
HorizontalOptions="Fill" />
</Grid>
</Frame>
</ControlTemplate>
</ContentPage.Resources>
...
</ContentPage>
この例では、TemplateBinding
マークアップ拡張機能によって、各 CardView
オブジェクトのプロパティに対するバインド式が解決されます。 次のスクリーンショットは、3 つの CardView
オブジェクトに適用された CardViewControlTemplate
を示します。
重要
TemplateBinding
マークアップ拡張機能を使用することは、テンプレートのルート要素の BindingContext
を RelativeSource
マークアップ拡張機能を使用してテンプレート化された親に設定し、Binding
マークアップ拡張機能を使用して子オブジェクトのバインドを解決することと同等です。 実際、TemplateBinding
マークアップ拡張機能を使用すると、Source
が RelativeBindingSource.TemplatedParent
である Binding
が作成されます。
スタイルを使用して ControlTemplate を適用する
コントロール テンプレートは、スタイルを使用して適用することもできます。 これを実行するには、ControlTemplate
を使用する "暗黙的" または "明示的" なスタイルを作成します。
次の XAML の例は、CardViewControlTemplate
を使用する "暗黙的" なスタイルを示します。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:ControlTemplateDemos.Controls"
...>
<ContentPage.Resources>
<ControlTemplate x:Key="CardViewControlTemplate">
...
</ControlTemplate>
<Style TargetType="controls:CardView">
<Setter Property="ControlTemplate"
Value="{StaticResource CardViewControlTemplate}" />
</Style>
</ContentPage.Resources>
<StackLayout Margin="30">
<controls:CardView BorderColor="DarkGray"
CardTitle="John Doe"
CardDescription="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla elit dolor, convallis non interdum."
IconBackgroundColor="SlateGray"
IconImageSource="user.png" />
<controls:CardView BorderColor="DarkGray"
CardTitle="Jane Doe"
CardDescription="Phasellus eu convallis mi. In tempus augue eu dignissim fermentum. Morbi ut lacus vitae eros lacinia."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"/>
<controls:CardView BorderColor="DarkGray"
CardTitle="Xamarin Monkey"
CardDescription="Aliquam sagittis, odio lacinia fermentum dictum, mi erat scelerisque erat, quis aliquet arcu."
IconBackgroundColor="SlateGray"
IconImageSource="user.png" />
</StackLayout>
</ContentPage>
この例では、"暗黙的な" Style
は各 CardView
オブジェクトに自動的に適用され、各 CardView
の ControlTemplate
プロパティは CardViewControlTemplate
に設定されます。
スタイルの詳細については、Xamarin.Forms のスタイルに関するページを参照してください。
コントロールの UI を再定義する
ControlTemplate
がインスタンス化され、ContentView
派生カスタム コントロールまたは ContentPage
派生ページの ControlTemplate
プロパティに割り当てられると、カスタム コントロールまたはページに定義されたビジュアル構造は、ControlTemplate
で定義されたビジュアル構造に置き換えられます。
たとえば、CardViewUI
カスタム コントロールでは、次の XAML を使用してユーザー インターフェイスを定義します。
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ControlTemplateDemos.Controls.CardViewUI"
x:Name="this">
<Frame BindingContext="{x:Reference this}"
BackgroundColor="{Binding CardColor}"
BorderColor="{Binding BorderColor}"
CornerRadius="5"
HasShadow="True"
Padding="8"
HorizontalOptions="Center"
VerticalOptions="Center">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75" />
<RowDefinition Height="4" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Frame IsClippedToBounds="True"
BorderColor="{Binding BorderColor, FallbackValue='Black'}"
BackgroundColor="{Binding IconBackgroundColor, FallbackValue='Gray'}"
CornerRadius="38"
HeightRequest="60"
WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="{Binding IconImageSource}"
Margin="-20"
WidthRequest="100"
HeightRequest="100"
Aspect="AspectFill" />
</Frame>
<Label Grid.Column="1"
Text="{Binding CardTitle, FallbackValue='Card title'}"
FontAttributes="Bold"
FontSize="Large"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Start" />
<BoxView Grid.Row="1"
Grid.ColumnSpan="2"
BackgroundColor="{Binding BorderColor, FallbackValue='Black'}"
HeightRequest="2"
HorizontalOptions="Fill" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{Binding CardDescription, FallbackValue='Card description'}"
VerticalTextAlignment="Start"
VerticalOptions="Fill"
HorizontalOptions="Fill" />
</Grid>
</Frame>
</ContentView>
ただし、この UI を構成するコントロールは、ControlTemplate
で新しいビジュアル構造を定義し、それを CardViewUI
オブジェクトの ControlTemplate
プロパティに割り当てることで置き換えることができます。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
...>
<ContentPage.Resources>
<ControlTemplate x:Key="CardViewCompressed">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{TemplateBinding IconImageSource}"
BackgroundColor="{TemplateBinding IconBackgroundColor}"
WidthRequest="100"
HeightRequest="100"
Aspect="AspectFill"
HorizontalOptions="Center"
VerticalOptions="Center" />
<StackLayout Grid.Column="1">
<Label Text="{TemplateBinding CardTitle}"
FontAttributes="Bold" />
<Label Text="{TemplateBinding CardDescription}" />
</StackLayout>
</Grid>
</ControlTemplate>
</ContentPage.Resources>
<StackLayout Margin="30">
<controls:CardViewUI BorderColor="DarkGray"
CardTitle="John Doe"
CardDescription="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla elit dolor, convallis non interdum."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource CardViewCompressed}" />
<controls:CardViewUI BorderColor="DarkGray"
CardTitle="Jane Doe"
CardDescription="Phasellus eu convallis mi. In tempus augue eu dignissim fermentum. Morbi ut lacus vitae eros lacinia."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource CardViewCompressed}" />
<controls:CardViewUI BorderColor="DarkGray"
CardTitle="Xamarin Monkey"
CardDescription="Aliquam sagittis, odio lacinia fermentum dictum, mi erat scelerisque erat, quis aliquet arcu."
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource CardViewCompressed}" />
</StackLayout>
</ContentPage>
この例では、CardViewUI
オブジェクトのビジュアル構造が ControlTemplate
で再定義され、要約リストに適した、よりコンパクトなビジュアル構造になります。
コンテンツを ContentPresenter に置き換える
ContentPresenter
をコントロール テンプレートに配置して、テンプレート化されたカスタム コントロールまたはテンプレート化されたページに表示されるコンテンツが表示される場所をマークすることができます。 カスタム コントロールまたはコントロール テンプレートを使用するページを使うと、ContentPresenter
によって表示されるコンテンツを定義できます。 次の図は、青い四角形でマークされた ContentPresenter
を含め、多数のコントロールがあるページの ControlTemplate
を示しています。
次の XAML は、ビジュアル構造に ContentPresenter
を含む TealTemplate
というコントロール テンプレートを示します。
<ControlTemplate x:Key="TealTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*" />
<RowDefinition Height="0.8*" />
<RowDefinition Height="0.1*" />
</Grid.RowDefinitions>
<BoxView Color="Teal" />
<Label Margin="20,0,0,0"
Text="{TemplateBinding HeaderText}"
TextColor="White"
FontSize="Title"
VerticalOptions="Center" />
<ContentPresenter Grid.Row="1" />
<BoxView Grid.Row="2"
Color="Teal" />
<Label x:Name="changeThemeLabel"
Grid.Row="2"
Margin="20,0,0,0"
Text="Change Theme"
TextColor="White"
HorizontalOptions="Start"
VerticalOptions="Center">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="OnChangeThemeLabelTapped" />
</Label.GestureRecognizers>
</Label>
<controls:HyperlinkLabel Grid.Row="2"
Margin="0,0,20,0"
Text="Help"
TextColor="White"
Url="https://learn.microsoft.com/xamarin/xamarin-forms/"
HorizontalOptions="End"
VerticalOptions="Center" />
</Grid>
</ControlTemplate>
次の例は、ContentPage
派生ページの ControlTemplate
プロパティに割り当てられた TealTemplate
を示します。
<controls:HeaderFooterPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:ControlTemplateDemos.Controls"
ControlTemplate="{StaticResource TealTemplate}"
HeaderText="MyApp"
...>
<StackLayout Margin="10">
<Entry Placeholder="Enter username" />
<Entry Placeholder="Enter password"
IsPassword="True" />
<Button Text="Login" />
</StackLayout>
</controls:HeaderFooterPage>
実行時に TealTemplate
がページに適用されると、ページ コンテンツがコントロール テンプレートに定義されている ContentPresenter
に置き換えられます。
テンプレートから名前付き要素を取得する
コントロール テンプレート内の名前付き要素は、テンプレート化されたカスタム コントロールまたはテンプレート化されたページから取得できます。 これは GetTemplateChild
メソッドを使って実行できます。これを使うと、インスタンス化された ControlTemplate
ビジュアル ツリーの名前付き要素が返されます (見つかった場合)。 それ以外の場合は null
を返します。
コントロール テンプレートがインスタンス化された後、テンプレートの OnApplyTemplate
メソッドが呼び出されます。 そのため、GetTemplateChild
メソッドは、テンプレート コントロールまたはテンプレート ページの OnApplyTemplate
オーバーライドから呼び出す必要があります。
重要
GetTemplateChild
メソッドは、OnApplyTemplate
メソッドを呼び出した後にのみ呼び出す必要があります。
次の XAML は、ContentPage
派生ページに適用できる TealTemplate
というコントロール テンプレートを示します。
<ControlTemplate x:Key="TealTemplate">
<Grid>
...
<Label x:Name="changeThemeLabel"
Grid.Row="2"
Margin="20,0,0,0"
Text="Change Theme"
TextColor="White"
HorizontalOptions="Start"
VerticalOptions="Center">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="OnChangeThemeLabelTapped" />
</Label.GestureRecognizers>
</Label>
...
</Grid>
</ControlTemplate>
この例では、Label
要素には名前が付けられており、テンプレート化されたページのコードで取得できます。 これを実行するには、テンプレート化されたページの OnApplyTemplate
オーバーライドから GetTemplateChild
メソッドを呼び出します。
public partial class AccessTemplateElementPage : HeaderFooterPage
{
Label themeLabel;
public AccessTemplateElementPage()
{
InitializeComponent();
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
themeLabel = (Label)GetTemplateChild("changeThemeLabel");
themeLabel.Text = OriginalTemplate ? "Aqua Theme" : "Teal Theme";
}
}
この例では、ControlTemplate
がインスタンス化されると、changeThemeLabel
という Label
オブジェクトが取得されます。 これで、AccessTemplateElementPage
クラスで changeThemeLabel
にアクセスしたり操作したりできるようになります。 次のスクリーンショットは、Label
によって表示されるテキストが変更されたことを示します。
viewmodel にバインドする
ControlTemplate
がテンプレート化された親 (テンプレートが適用されるランタイム オブジェクト インスタンス) にバインドされている場合でも、ControlTemplate
を使うとデータを viewmodel にバインドできます。
次の XAML の例は、PeopleViewModel
という viewmodel を使用するページを示します。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ControlTemplateDemos"
xmlns:controls="clr-namespace:ControlTemplateDemos.Controls"
...>
<ContentPage.BindingContext>
<local:PeopleViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<DataTemplate x:Key="PersonTemplate">
<controls:CardView BorderColor="DarkGray"
CardTitle="{Binding Name}"
CardDescription="{Binding Description}"
ControlTemplate="{StaticResource CardViewControlTemplate}" />
</DataTemplate>
</ContentPage.Resources>
<StackLayout Margin="10"
BindableLayout.ItemsSource="{Binding People}"
BindableLayout.ItemTemplate="{StaticResource PersonTemplate}" />
</ContentPage>
この例では、ページの BindingContext
が PeopleViewModel
インスタンスに設定されています。 この viewmodel では、People
コレクションと、DeletePersonCommand
という名前の ICommand
が公開されています。 ページ上の StackLayout
では、バインド可能なレイアウトを使用して People
コレクションにデータをバインドします。また、バインド可能なレイアウトの ItemTemplate
は PersonTemplate
リソースに設定されます。 この DataTemplate
は、People
コレクションの各項目が CardView
オブジェクトを使用して表示されるように指定しています。 CardView
オブジェクトのビジュアル構造は、CardViewControlTemplate
という ControlTemplate
を使用して定義されます。
<ControlTemplate x:Key="CardViewControlTemplate">
<Frame BindingContext="{Binding Source={RelativeSource TemplatedParent}}"
BackgroundColor="{Binding CardColor}"
BorderColor="{Binding BorderColor}"
CornerRadius="5"
HasShadow="True"
Padding="8"
HorizontalOptions="Center"
VerticalOptions="Center">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75" />
<RowDefinition Height="4" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="{Binding CardTitle}"
FontAttributes="Bold"
FontSize="Large"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Start" />
<BoxView Grid.Row="1"
BackgroundColor="{Binding BorderColor}"
HeightRequest="2"
HorizontalOptions="Fill" />
<Label Grid.Row="2"
Text="{Binding CardDescription}"
VerticalTextAlignment="Start"
VerticalOptions="Fill"
HorizontalOptions="Fill" />
<Button Text="Delete"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:PeopleViewModel}}, Path=DeletePersonCommand}"
CommandParameter="{Binding CardTitle}"
HorizontalOptions="End" />
</Grid>
</Frame>
</ControlTemplate>
この例では、ControlTemplate
のルート要素は Frame
オブジェクトです。 Frame
オブジェクトでは、RelativeSource
マークアップ拡張機能を使用して、その BindingContext
をテンプレート化された親に設定します。 BindingContext
はルートの Frame
要素から継承されるため、Frame
オブジェクトとその子のバインディング式は、CardView
プロパティに対して解決されます。 次のスクリーンショットは、3 つの項目で構成される People
コレクションを表示するページを示します。
ControlTemplate
のオブジェクトはテンプレート化された親のプロパティにバインドされますが、コントロール テンプレート内の Button
は、テンプレート化された親と、viewmodel 内の DeletePersonCommand
の両方にバインドされます。 これは、バインディング コンテキストの種類が PeopleViewModel
(StackLayout
) である祖先のバインディング コンテキストになるように、Button.Command
プロパティによってバインディング ソースが再定義されているためです。 そのため、バインディング式の Path
部分によって DeletePersonCommand
プロパティを解決できます。 ただし、Button.CommandParameter
プロパティによってバインディング ソースが変更されることはなく、代わりに ControlTemplate
の親から継承されます。 そのため、CommandParameter
プロパティは CardView
の CardTitle
プロパティにバインドされます。
Button
のバインディングの全体的な効果として、Button
がタップされると、PeopleViewModel
クラスの DeletePersonCommand
が実行され、CardName
プロパティの値が DeletePersonCommand
に渡されるようになります。 その結果、指定された CardView
がバインド可能なレイアウトから削除されます。
相対的なバインディングの詳細については、「Xamarin.Forms の相対的なバインディング」を参照してください。