Szablony kontrolek
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:
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 TemplateBinding
elementu , 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 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:
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 CardViewControlTemplate
wartość .
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:
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:
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:
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:
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ę:
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 DeletePersonCommand
do 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.