Xamarin.Forms szablony kontrolek

Download Sample Pobieranie przykładu

Xamarin.Forms szablony kontrolek umożliwiają definiowanie struktury wizualnej pochodnych ContentView kontrolek niestandardowych i ContentPage stron pochodnych. Szablony kontrolek oddzielają interfejs użytkownika dla kontrolki niestandardowej lub strony od logiki implementujące kontrolkę lub stronę. Dodatkową zawartość można również wstawić do szablonowej kontrolki niestandardowej lub strony szablonu w wstępnie zdefiniowanej lokalizacji.

Na przykład można utworzyć szablon kontrolki, który ponownie definiuje interfejs użytkownika udostępniany przez kontrolkę niestandardową. Następnie szablon kontrolki może być używany przez wymagane wystąpienie kontrolki niestandardowej. Alternatywnie można utworzyć szablon kontrolki, który definiuje dowolny wspólny interfejs użytkownika, który będzie używany przez wiele stron w aplikacji. Szablon kontrolki może być następnie używany przez wiele stron, a każda strona nadal wyświetla swoją unikatową zawartość.

Tworzenie elementu ControlTemplate

W poniższym przykładzie pokazano kod kontrolki niestandardowej 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);
    }
    // ...
}

Klasa CardView , która pochodzi z ContentView klasy, reprezentuje kontrolkę niestandardową, która wyświetla dane w układzie przypominającym kartę. Klasa zawiera właściwości, które są wspierane przez powiązane właściwości dla wyświetlanych danych. CardView Jednak klasa nie definiuje żadnego interfejsu użytkownika. Zamiast tego interfejs użytkownika zostanie zdefiniowany przy użyciu szablonu kontrolki. Aby uzyskać więcej informacji na temat tworzenia ContentView pochodnych kontrolek niestandardowych, zobacz Xamarin.Forms ContentView.

Szablon kontrolki jest tworzony przy użyciu ControlTemplate typu . Podczas tworzenia obiektu ControlTemplatepołączysz View obiekty w celu skompilowania interfejsu użytkownika dla kontrolki niestandardowej lub strony. Element ControlTemplate musi mieć tylko jeden View element główny. Jednak element główny zwykle zawiera inne View obiekty. Kombinacja obiektów tworzy strukturę wizualizacji kontrolki.

ControlTemplate Chociaż element można zdefiniować w tekście, typowe podejście do deklarowania elementu ControlTemplate jest jako zasób w słowniku zasobów. Ponieważ szablony kontrolek są zasobami, przestrzegają tych samych reguł określania zakresu, które mają zastosowanie do wszystkich zasobów. Jeśli na przykład zadeklarujesz szablon kontrolki w elemecie głównym pliku XAML definicji aplikacji, szablon może być używany w dowolnym miejscu w aplikacji. Jeśli zdefiniujesz szablon na stronie, tylko ta strona może używać szablonu kontrolki. Aby uzyskać więcej informacji na temat zasobów, zobacz Xamarin.Forms Słowniki zasobów.

Poniższy przykład XAML przedstawia ControlTemplate obiekt dla CardView obiektów:

<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>

Gdy element ControlTemplate jest zadeklarowany jako zasób, musi mieć klucz określony za pomocą atrybutu x:Key , aby można było go zidentyfikować w słowniku zasobów. W tym przykładzie element główny obiektu CardViewControlTemplate jest obiektem Frame . Obiekt Frame używa RelativeSource rozszerzenia znaczników, aby ustawić jego BindingContext wystąpienie obiektu środowiska uruchomieniowego, do którego zostanie zastosowany szablon, który jest znany jako szablon nadrzędny. Obiekt Frame używa kombinacji Gridobiektów , , FrameImage, Labeli BoxView do definiowania struktury wizualnej CardView obiektu. Wyrażenia powiązania tych obiektów są rozpoznawane względem CardView właściwości ze względu na dziedziczenie BindingContext elementu głównego Frame . Aby uzyskać więcej informacji na temat RelativeSource rozszerzenia znaczników, zobacz Xamarin.Forms Względne powiązania.

Korzystanie z kontrolkiTemplate

Obiekt ControlTemplate można zastosować do pochodnej kontrolki niestandardowej ContentView , ustawiając jej ControlTemplate właściwość na obiekt szablonu kontrolki. Podobnie obiekt można zastosować do strony pochodnejContentPage, ControlTemplate ustawiając jej ControlTemplate właściwość na obiekt szablonu kontrolki. W czasie wykonywania, gdy ControlTemplate element jest stosowany, wszystkie kontrolki zdefiniowane w obiekcie ControlTemplate są dodawane do drzewa wizualnego kontrolki niestandardowej szablonu lub strony szablonu.

W poniższym przykładzie pokazano przypisanie CardViewControlTemplateControlTemplate do właściwości każdego CardView obiektu:

<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>

W tym przykładzie CardViewControlTemplate kontrolki w stanie się częścią drzewa wizualnego dla każdego CardView obiektu. Ponieważ obiekt główny Frame szablonu kontrolki ustawia go BindingContext na szablon obiektu nadrzędnego, Frame a jego elementy podrzędne rozpoznają ich wyrażenia powiązania względem właściwości każdego CardView obiektu.

Na poniższych zrzutach ekranu przedstawiono CardViewControlTemplate zastosowane do trzech CardView obiektów:

Screenshots of templated CardView objects, on iOS and Android

Ważne

Punkt w czasie, ControlTemplate który jest stosowany do wystąpienia kontrolki, można wykryć przez zastąpienie OnApplyTemplate metody w szablonowej kontrolce niestandardowej lub stronie szablonu. Aby uzyskać więcej informacji, zobacz Pobieranie nazwanego elementu z szablonu.

Przekazywanie parametrów za pomocą funkcji TemplateBinding

TemplateBinding Rozszerzenie znaczników wiąże właściwość elementu, który znajduje się w ControlTemplate właściwości publicznej zdefiniowanej przez szablonową kontrolkę niestandardową lub stronę szablonu. Gdy używasz TemplateBindingelementu , włączasz właściwości kontrolki tak, aby działały jako parametry szablonu. W związku z tym po ustawieniu właściwości na niestandardowej kontrolce szablonu lub stronie szablonu ta wartość jest przekazywana do elementu, który ma TemplateBinding na nim wartość.

Ważne

Wyrażenie TemplateBinding znaczników umożliwia RelativeSource usunięcie powiązania z poprzedniego szablonu kontrolki i zastąpienie wyrażeń Binding .

Rozszerzenie TemplateBinding znaczników definiuje następujące właściwości:

  • Path, typu string, ścieżka do właściwości .
  • Mode, typu BindingMode, kierunek, w którym zmiany są propagowane między źródłem a obiektem docelowym.
  • Converter, typu IValueConverter, konwerter wartości powiązania.
  • ConverterParameter, typu object, parametr do konwertera wartości powiązania.
  • StringFormat, typu string, format ciągu dla powiązania.

TemplateBinding Dla ContentProperty rozszerzenia znaczników to Path. W związku z tym część "Path=" rozszerzenia znaczników można pominąć, jeśli ścieżka jest pierwszym elementem w wyrażeniu TemplateBinding . Aby uzyskać więcej informacji na temat używania tych właściwości w wyrażeniu powiązania, zobacz Xamarin.Forms Powiązanie danych.

Ostrzeżenie

Rozszerzenie TemplateBinding znaczników powinno być używane tylko w obrębie .ControlTemplate Jednak próba użycia TemplateBinding wyrażenia poza obiektem ControlTemplate nie spowoduje wystąpienia błędu kompilacji lub zgłoszenia wyjątku.

Poniższy przykład XAML przedstawia ControlTemplate obiekt dla CardView obiektów korzystających z TemplateBinding rozszerzenia znaczników:

<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>

W tym przykładzie TemplateBinding rozszerzenie znaczników rozpoznaje wyrażenia powiązania względem właściwości każdego CardView obiektu. Na poniższych zrzutach ekranu przedstawiono CardViewControlTemplate zastosowane do trzech CardView obiektów:

Screenshots of templated CardView objects

Ważne

TemplateBinding Użycie rozszerzenia znaczników jest równoważne ustawieniu BindingContext elementu głównego w szablonie na jego szablon nadrzędny RelativeSource z rozszerzeniem znaczników, a następnie rozpoznawanie powiązań obiektów podrzędnych z Binding rozszerzeniem znaczników. W rzeczywistości TemplateBinding rozszerzenie znaczników tworzy obiekt, którego BindingSource element ma wartość RelativeBindingSource.TemplatedParent.

Stosowanie kontrolkiTemplate z stylem

Szablony kontrolek można również stosować za pomocą stylów. Jest to osiągane przez utworzenie niejawnego lub jawnego stylu, który używa elementu ControlTemplate.

Poniższy przykład XAML przedstawia niejawny styl, który korzysta z elementu 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>

W tym przykładzie niejawnaStyle funkcja jest automatycznie stosowana do każdego CardView obiektu i ustawia ControlTemplate właściwość każdego CardView obiektu na CardViewControlTemplatewartość .

Aby uzyskać więcej informacji na temat stylów, zobacz Xamarin.Forms Style.

Ponowne definiowanie interfejsu użytkownika kontrolki

Gdy element ControlTemplate jest tworzony i przypisywany do ControlTemplate właściwości pochodnej ContentView kontrolki niestandardowej lub ContentPage strony pochodnej, struktura wizualizacji zdefiniowana dla kontrolki niestandardowej lub strony jest zastępowana strukturą wizualizacji zdefiniowaną w obiekcie ControlTemplate.

Na przykład kontrolka niestandardowa CardViewUI definiuje interfejs użytkownika przy użyciu następującego kodu 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>

Jednak kontrolki składające się z tego interfejsu użytkownika można zastąpić przez zdefiniowanie nowej struktury wizualizacji w ControlTemplateobiekcie i przypisanie jej do ControlTemplate właściwości CardViewUI obiektu:

<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>

W tym przykładzie struktura wizualizacji CardViewUI obiektu jest ponownie definiowana w obiekcie ControlTemplate , który zapewnia bardziej kompaktową strukturę wizualną odpowiednią dla listy skondensowanej:

Screenshots of templated CardViewUI objects, on iOS and Android

Podstawianie zawartości do elementu ContentPresenter

Element ContentPresenter można umieścić w szablonie kontrolki, aby oznaczyć, gdzie będzie wyświetlana zawartość za pomocą szablonowej kontrolki niestandardowej lub strony szablonu. Niestandardowa kontrolka lub strona korzystająca z szablonu kontrolki zdefiniuje zawartość, która ma być wyświetlana ContentPresenterprzez element . Na poniższym diagramie przedstawiono ControlTemplate stronę zawierającą wiele kontrolek, w tym ContentPresenter oznaczone niebieskim prostokątem:

Control Template for a ContentPage

Poniższy kod XAML przedstawia szablon kontrolki o nazwie TealTemplate zawierający element ContentPresenter w jego strukturze wizualnej:

<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>

W poniższym przykładzie pokazano TealTemplate przypisaną ControlTemplate do właściwości strony pochodnej 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>

W czasie wykonywania, gdy TealTemplate jest stosowany do strony, zawartość strony jest zastępowana w ContentPresenter zdefiniowanym w szablonie kontrolki:

Screenshots of templated page object, on iOS and Android

Pobieranie nazwanego elementu z szablonu

Nazwane elementy w szablonie kontrolki można pobrać z szablonu niestandardowej kontrolki lub strony szablonu. Można to osiągnąć za GetTemplateChild pomocą metody , która zwraca nazwany ControlTemplate element w utworzonej wizualizacji drzewa, jeśli zostanie znaleziona. W przeciwnym razie zwraca wartość null.

Po utworzeniu wystąpienia szablonu kontrolki wywoływana OnApplyTemplate jest metoda szablonu. W GetTemplateChild związku z tym metoda powinna być wywoływana z OnApplyTemplate przesłonięcia w kontrolce szablonu lub na stronie szablonu.

Ważne

Metoda GetTemplateChild powinna być wywoływana tylko po wywołaniu OnApplyTemplate metody.

Poniższy kod XAML przedstawia szablon kontrolki o nazwie TealTemplate , który można zastosować do ContentPage stron pochodnych:

<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>

W tym przykładzie Label element ma nazwę i można go pobrać w kodzie strony szablonu. Jest to osiągane przez wywołanie GetTemplateChild metody z OnApplyTemplate przesłonięcia dla strony szablonu:

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";
    }
}

W tym przykładzie Label obiekt o nazwie changeThemeLabel jest pobierany po utworzeniu ControlTemplate wystąpienia obiektu . changeThemeLabel następnie można uzyskać dostęp do klasy i manipulować nią AccessTemplateElementPage . Na poniższych zrzutach ekranu pokazano, że tekst wyświetlany przez Label element został zmieniony:

Screenshots of templated page object

Wiązanie z modelem widoku

Obiekt ControlTemplate może powiązać dane z modelem viewmodel, nawet jeśli ControlTemplate powiązanie z szablonem elementu nadrzędnego (wystąpienie obiektu środowiska uruchomieniowego, do którego zastosowano szablon).

Poniższy przykład XAML przedstawia stronę, która korzysta z modelu viewmodel o nazwie 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>

W tym przykładzie BindingContext strona jest ustawiona PeopleViewModel na wystąpienie. Ten model widoku uwidacznia People kolekcję i ICommand nazwę DeletePersonCommand. Na StackLayout stronie jest używany układ możliwy do People powiązania z danymi powiązanymi z kolekcją, a ItemTemplate układ wiązania jest ustawiony na PersonTemplate zasób. Określa, DataTemplate że każdy element w People kolekcji będzie wyświetlany przy użyciu CardView obiektu. Struktura CardView wizualna obiektu jest definiowana ControlTemplate przy użyciu nazwy 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>

W tym przykładzie element główny obiektu ControlTemplate jest obiektem Frame . Obiekt Frame używa RelativeSource rozszerzenia znaczników, aby ustawić go BindingContext na szablon elementu nadrzędnego. Wyrażenia powiązania obiektu i jego elementów podrzędnych Frame są rozpoznawane względem CardView właściwości z powodu dziedziczenia BindingContext z elementu głównego Frame . Na poniższych zrzutach ekranu przedstawiono stronę z wyświetloną kolekcją People składającą się z trzech elementów:

Screenshots of three templated CardView objects

Obiekty w powiązaniu ControlTemplate z właściwościami w szablonie elementu nadrzędnego, Button natomiast szablon kontrolki jest powiązany zarówno z elementem nadrzędnym szablonu, jak i z obiektem DeletePersonCommand w modelu viewmodel. Jest to spowodowane tym, że Button.Command właściwość ponownie definiuje jego źródło powiązania jako kontekst powiązania obiektu nadrzędnego, którego typ kontekstu powiązania to PeopleViewModel, czyli StackLayout. Część Path wyrażeń powiązania może następnie rozpoznać DeletePersonCommand właściwość . Button.CommandParameter Jednak właściwość nie zmienia źródła powiązania, zamiast tego dziedziczy ją z elementu nadrzędnego w obiekcie ControlTemplate. W związku z tym CommandParameter właściwość wiąże się z właściwością CardTitleCardView.

Ogólny efekt Button powiązań polega na tym, DeletePersonCommand że po Button naciśnięciu elementu w PeopleViewModel klasie wykonywana jest wartość CardName właściwości przekazywanej DeletePersonCommanddo klasy . Spowoduje to usunięcie określonego CardView układu możliwego do powiązania:

Screenshots of two templated CardView objects

Aby uzyskać więcej informacji na temat powiązań względnych, zobacz Xamarin.Forms Powiązania względne.