Xamarin.Forms Modelos de controle
Xamarin.Forms Os modelos de controle permitem que você defina a estrutura visual de ContentView
controles personalizados derivados e ContentPage
páginas derivadas. Os modelos de controle separam a IU (interface do usuário) para uma página ou controle personalizado da lógica que implementa o controle ou a página. Também é possível inserir conteúdo adicional no controle modelo personalizado ou na página modelo, em um local predefinido.
Por exemplo, é possível criar um modelo de controle que redefine a interface do usuário fornecida por um controle personalizado. O modelo de controle pode então ser consumido pela instância de controle personalizado necessária. Como alternativa, um modelo de controle pode ser criado para definir qualquer interface do usuário comum que será usada por várias páginas em um aplicativo. O modelo de controle pode, então, ser consumido por várias páginas, com cada página ainda exibindo o conteúdo exclusivo.
Criar um ControlTemplate
O exemplo a seguir mostra o código de um controle personalizado de 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);
}
// ...
}
A classe CardView
, que deriva da classe ContentView
, representa um controle personalizado que exibe dados em um layout de tipo de cartão. A classe contém propriedades, que são apoiadas por propriedades associáveis, para os dados que ela exibe. No entanto, a classe CardView
não define nenhuma interface do usuário. Em vez disso, a interface do usuário será definida com um modelo de controle. Para obter mais informações sobre como criar ContentView
controles personalizados derivados, consulte Xamarin.Forms ContentView.
Um modelo de controle é criado com o tipo ControlTemplate
. Ao criar um ControlTemplate
, você combina objetos View
para criar a interface do usuário para uma página ou controle personalizado. Um ControlTemplate
deve ter apenas um View
como seu elemento raiz. No entanto, o elemento raiz geralmente contém outros objetos View
. A combinação de objetos compõe a estrutura visual do controle.
Embora um ControlTemplate
possa ser definido em linha, a abordagem típica para declarar um ControlTemplate
é como um recurso em um dicionário de recursos. Como os modelos de controle são recursos, eles obedecem às mesmas regras de escopo que se aplicam a todos os recursos. Por exemplo, se você declarar um modelo de controle no elemento raiz do arquivo do seu arquivo XAML de definição de aplicativo, o modelo poderá ser utilizado em qualquer lugar no aplicativo. Se você definir o modelo em uma página, somente essa página poderá usar o modelo de controle. Para obter mais informações sobre recursos, consulte Xamarin.Forms Dicionários de recursos.
O exemplo de XAML a seguir mostra um ControlTemplate
para objetos CardView
:
<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>
Quando um ControlTemplate
é declarado como um recurso, ele precisa ter uma chave especificada com o atributo x:Key
para que ele possa ser identificado no dicionário de recursos. Neste exemplo, o elemento raiz do CardViewControlTemplate
é um objeto Frame
. O objeto Frame
usa a extensão de marcação RelativeSource
para definir o próprio BindingContext
para a instância de objeto de runtime à qual o modelo será aplicado, que é conhecida como o pai modelo. O objeto Frame
usa uma combinação de objetos Grid
, Frame
, Image
, Label
e BoxView
para definir a estrutura visual de um objeto CardView
. As expressões de associação desses objetos são resolvidas em relação às propriedades CardView
, devido à herança do BindingContext
do elemento raiz Frame
. Para obter mais informações sobre a extensão de RelativeSource
marcação, consulte Xamarin.Forms Associações relativas.
Consumir um ControlTemplate
Um ControlTemplate
pode ser aplicado a um controle personalizado derivado de ContentView
definindo a propriedade ControlTemplate
dele para o objeto de modelo de controle. Semelhantemente, um ControlTemplate
pode ser aplicado a uma página derivada de ContentPage
definindo a propriedade ControlTemplate
dele para o objeto de modelo de controle. No runtime, quando um ControlTemplate
é aplicado, todos os controles definidos no ControlTemplate
são adicionados à árvore visual do controle modelo personalizado ou da página modelo.
O exemplo a seguir mostra o CardViewControlTemplate
que está sendo atribuído à propriedade ControlTemplate
de cada objeto CardView
:
<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>
Neste exemplo, os controles no CardViewControlTemplate
tornam-se parte da árvore visual para cada objeto CardView
. Já que o objeto Frame
raiz para o modelo de controle define o respectivo BindingContext
para o pai modelo, o Frame
e os filhos dele resolvem as respectivas expressões de associação em relação às propriedades de cada objeto CardView
.
As capturas de tela a seguir mostram o CardViewControlTemplate
aplicado aos três objetos CardView
:
Importante
O ponto no tempo em que um ControlTemplate
é aplicado a uma instância de controle pode ser detectado pela substituição do método OnApplyTemplate
no controle modelo personalizado ou na página modelada. Para obter mais informações, veja Obter um elemento nomeado de um modelo.
Passar parâmetros com TemplateBinding
A extensão de marcação TemplateBinding
associa uma propriedade de um elemento que está no ControlTemplate
a uma propriedade pública que é definida pelo controle modelo personalizado ou pela página modelo. Quando você usa um TemplateBinding
, você habilita propriedades do controle para atuar como parâmetros para o modelo. Portanto, quando uma propriedade é definida em um controle modelo personalizado ou em uma página modelo, esse valor é passado para o elemento que contém o TemplateBinding
.
Importante
A expressão de marcação TemplateBinding
permite que a associação RelativeSource
do modelo de controle anterior seja removida e substitui as expressões Binding
.
A extensão de marcação TemplateBinding
define as seguintes propriedades:
Path
, do tipostring
, o caminho para a propriedade.Mode
, do tipoBindingMode
, a direção em que as alterações se propagam entre a origem e o destino.Converter
, do tipoIValueConverter
, o conversor de valor de associação.ConverterParameter
, do tipoobject
, o parâmetro do conversor de valor de associação.StringFormat
, do tipostring
, o formato da cadeia de caracteres para a associação.
O ContentProperty
para a extensão de marcação TemplateBinding
é Path
. Portanto, a parte "Path=" da extensão de marcação poderá ser omitida se o caminho for o primeiro item na expressão TemplateBinding
. Para obter mais informações sobre como usar essas propriedades em uma expressão de associação, consulte Xamarin.Forms Associação de dados.
Aviso
A extensão de marcação TemplateBinding
só deve ser usada dentro de um ControlTemplate
. No entanto, tentar usar uma expressão TemplateBinding
fora de um ControlTemplate
não resultará em um erro de build nem na geração de uma exceção.
O XAML de exemplo a seguir mostra um ControlTemplate
para objetos CardView
, que usa a extensão de marcação TemplateBinding
:
<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>
Neste exemplo, a extensão de marcação TemplateBinding
resolve expressões de associação em relação às propriedades de cada objeto CardView
. As capturas de tela a seguir mostram o CardViewControlTemplate
aplicado aos três objetos CardView
:
Importante
O uso da extensão de marcação TemplateBinding
é equivalente a definir o BindingContext
do elemento raiz no modelo para o respectivo pai modelo com a extensão de marcação RelativeSource
e, em seguida, resolver associações de objetos filho com a extensão de marcação Binding
. Na verdade, a extensão de marcação TemplateBinding
cria um Binding
cujo Source
é RelativeBindingSource.TemplatedParent
.
Aplicar um ControlTemplate com um estilo
Os modelos de controle também podem ser aplicados com estilos. Isso é alcançado pela criação de um estilo implícito ou explícito que consome o ControlTemplate
.
O seguinte exemplo de XAML mostra um estilo implícito que consome o 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>
Neste exemplo, o Style
implícito é aplicado automaticamente a cada objeto CardView
e define a propriedade ControlTemplate
de cada CardView
para CardViewControlTemplate
.
Para obter mais informações sobre estilos, consulte Xamarin.Forms Estilos.
Redefinir a interface do usuário de um controle
Quando um ControlTemplate
é instanciado e atribuído à propriedade ControlTemplate
de um controle personalizado derivado de ContentView
ou uma página derivada de ContentPage
, a estrutura visual definida para a página ou controle personalizado é substituída pela estrutura visual definida no ControlTemplate
.
Por exemplo, o controle personalizado CardViewUI
define a própria interface do usuário usando o seguinte 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>
No entanto, os controles que compõem essa interface do usuário podem ser substituídos definindo uma nova estrutura visual em um ControlTemplate
e atribuindo-o à propriedade ControlTemplate
de um objeto CardViewUI
:
<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>
Neste exemplo, a estrutura visual do objeto CardViewUI
é redefinida em um ControlTemplate
que fornece uma estrutura visual mais compacta, que é adequada para uma lista condensada:
Substituir o conteúdo em um ContentPresenter
Um ContentPresenter
pode ser colocado em um modelo de controle para marcar o local em que o conteúdo a ser exibido pela página modelo ou controle modelo personalizado será exibido. A página ou controle personalizado ou a página que consome o modelo de controle definirá o conteúdo a ser exibido pelo ContentPresenter
. O diagrama a seguir ilustra um ControlTemplate
para uma página que contém um número de controles, incluindo um ContentPresenter
marcado por um retângulo azul:
O XAML a seguir mostra um modelo de controle chamado TealTemplate
que contém um ContentPresenter
na própria estrutura visual:
<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>
O exemplo a seguir mostra o TealTemplate
atribuído à propriedade ControlTemplate
de uma página derivada de ContentPage
:
<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>
No runtime, quando TealTemplate
é aplicado à página, o conteúdo da página é substituído no ContentPresenter
definido no modelo de controle:
obter um elemento nomeado de um modelo
Elementos nomeados dentro de um modelo de controle podem ser recuperados do controle modelo personalizado ou da página modelo. Isso pode ser obtido com o método GetTemplateChild
, que retorna o elemento nomeado na árvore visual ControlTemplate
instanciada, se encontrado. Caso contrário, ele retornará null
.
Depois que um modelo de controle foi instanciado, o método OnApplyTemplate
do modelo é chamado. O método GetTemplateChild
, portanto, deve ser chamado de uma substituição de OnApplyTemplate
no controle modelo ou na página modelo.
Importante
O método GetTemplateChild
só deve ser chamado após o método OnApplyTemplate
ser chamado.
O XAML a seguir mostra um modelo de controle chamado TealTemplate
que pode ser aplicado a páginas derivadas de ContentPage
:
<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>
Neste exemplo, o elemento Label
é nomeado e pode ser recuperado no código para a página modelo. Isso é feito chamando o método GetTemplateChild
da substituição OnApplyTemplate
para a página modelo:
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";
}
}
Neste exemplo, o objeto Label
chamado changeThemeLabel
será recuperado depois que o ControlTemplate
tiver sido instanciado. Então changeThemeLabel
pode ser acessado e manipulado pela classe AccessTemplateElementPage
. As capturas de tela a seguir mostram que o texto exibido pelo Label
foi alterado:
Associar a um ViewModel
Um ControlTemplate
pode associar dados a um ViewModel, mesmo quando o ControlTemplate
é associado ao pai modelo (a instância do objeto de runtime à qual o modelo é aplicado).
O seguinte exemplo de XAML mostra uma página que consome um ViewModel chamado PeopleViewModel
:
<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>
Neste exemplo, o BindingContext
da página é definido para uma instância de PeopleViewModel
. Esse ViewModel expõe uma coleção de People
e um ICommand
chamado DeletePersonCommand
. O StackLayout
na página usa um layout associável para associar dados à coleção de People
e o ItemTemplate
do layout associável é definido como o recurso PersonTemplate
. Este DataTemplate
especifica que cada item na coleção People
será exibido usando um objeto CardView
. A estrutura visual do objeto CardView
é definida usando um ControlTemplate
chamado CardViewControlTemplate
:
<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>
Neste exemplo, o elemento raiz do ControlTemplate
é um objeto Frame
. O objeto Frame
usa a extensão de marcação RelativeSource
para definir o próprio BindingContext
como o pai modelo. As expressões de associação do objeto Frame
e os respectivos filhos são resolvidas em relação às propriedades CardView
, devido à herança do BindingContext
do elemento raiz Frame
. As capturas de tela a seguir mostram a página que exibe a coleção People
, que consiste em três itens:
Embora os objetos no ControlTemplate
se associem a propriedades no respectivo pai modelo, o Button
dentro do modelo de controle se associa ao seu pai modelo e ao DeletePersonCommand
no ViewModel. Isso ocorre porque a propriedade Button.Command
redefine a própria origem de associação como o contexto de associação do ancestral cujo tipo de contexto de associação é PeopleViewModel
, que é o StackLayout
. A parte Path
das expressões de associação pode então resolver a propriedade DeletePersonCommand
. No entanto, a propriedade Button.CommandParameter
não altera a própria fonte de associação. Em vez disso, ela herda do próprio pai no ControlTemplate
. Portanto, a propriedade CommandParameter
é associada à propriedade CardTitle
do CardView
.
O efeito geral das associações Button
é que, quando o Button
é tocado, o DeletePersonCommand
na classe PeopleViewModel
é executado e o valor da propriedade CardName
é passado para o DeletePersonCommand
. Isso resulta na remoção do CardView
especificado do layout associável:
Para obter mais informações sobre associações relativas, consulte Xamarin.Forms Associações relativas.