Udostępnij za pośrednictwem


Szablony kontrolek

Przeglądaj przykład. Przeglądanie przykładu

Szablony kontrolek interfejsu użytkownika aplikacji wieloplatformowej platformy .NET (.NET MAUI) umożliwiają definiowanie struktury wizualnej ContentView pochodnych 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 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 słowniku zasobów na poziomie 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 Słowniki zasobów.

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

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             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}"
                 ...>
              <!-- UI objects that define the CardView visual structure -->
          </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 kontrolek do zdefiniowania 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 Powiązania względne.

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 CardViewControlTemplate ControlTemplate do właściwości dwóch CardView obiektów:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             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}" />
    </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.

Poniższy zrzut ekranu przedstawia CardViewControlTemplate zastosowane do CardView obiektów:

Zrzut ekranu przedstawiający dwa szablony obiektów CardView.

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:

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 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://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             ...>
    <ContentPage.Resources>
        <ControlTemplate x:Key="CardViewControlTemplate">
            <Frame BackgroundColor="{TemplateBinding CardColor}"
                   BorderColor="{TemplateBinding BorderColor}"
                   ...>
                <!-- UI objects that define the CardView visual structure -->                   
            </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. Poniższy zrzut ekranu przedstawia CardViewControlTemplate zastosowane do CardView obiektów:

Zrzut ekranu przedstawiający szablonowe obiekty CardView.

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 Binding Source 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://schemas.microsoft.com/dotnet/2021/maui"
             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" />
        ...
    </StackLayout>
</ContentPage>

W tym przykładzie niejawna Style 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 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://schemas.microsoft.com/dotnet/2021/maui"
             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}"
           ...>
        <!-- UI objects that define the CardView visual structure -->           
    </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://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             ...>
    <ContentPage.Resources>
        <ControlTemplate x:Key="CardViewCompressed">
            <Grid RowDefinitions="100"
                  ColumnDefinitions="100, *">
                <Image Source="{TemplateBinding IconImageSource}"
                       BackgroundColor="{TemplateBinding IconBackgroundColor}"
                       ...>
                <!-- Other UI objects that define the CardView visual structure -->
            </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}" />
        ...
    </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:

Zrzut ekranu przedstawiający szablonowe obiekty CardViewUI.

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:

Szablon kontrolki dla strony ContentPage.

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

<ControlTemplate x:Key="TealTemplate">
    <Grid RowDefinitions="0.1*, 0.8*, 0.1*">
        <BoxView Color="Teal" />
        <Label Margin="20,0,0,0"
               Text="{TemplateBinding HeaderText}"
               ... />
        <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"
               ...>
            <Label.GestureRecognizers>
                <TapGestureRecognizer Tapped="OnChangeThemeLabelTapped" />
            </Label.GestureRecognizers>
        </Label>
        <controls:HyperlinkLabel Grid.Row="2"
                                 Margin="0,0,20,0"
                                 Text="Help"
                                 Url="https://learn.microsoft.com/dotnet/maui/"
                                 ... />
    </Grid>
</ControlTemplate>

W poniższym przykładzie pokazano TealTemplate przypisaną ControlTemplate do właściwości strony pochodnej ContentPage :

<controls:HeaderFooterPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                           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:

Zrzut ekranu przedstawiający obiekt strony szablonu.

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"
               Text="Change Theme"
               ...>
            <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 . Poniższy zrzut ekranu pokazuje, że tekst wyświetlany przez Label element został zmieniony:

Zrzut ekranu przedstawiający zmieniony obiekt strony szablonu.

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://schemas.microsoft.com/dotnet/2021/maui"
             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}"
           ...>
        <!-- UI objects that define the CardView visual structure -->           
    </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 . Poniższy zrzut ekranu przedstawia stronę wyświetlającą People kolekcję:

Zrzut ekranu przedstawiający trzy szablony obiektów CardView, które są powiązane z modelem widoku.

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ą CardTitle CardView.

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 elementu z układu możliwego do powiązania.

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