* MessagingCenter.

* Triggers.

* Behaviors.

* Link fixed.

* Control templates.

* Fix links.

* Fix link.

* Data templates.

* Fix image links.

* Edits.

* Edits.

* Fix linting issue.
This commit is contained in:
David Britch 2022-02-28 09:49:13 +00:00 коммит произвёл GitHub
Родитель c85b5bc894
Коммит c1e6e5fb36
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
20 изменённых файлов: 1834 добавлений и 0 удалений

Просмотреть файл

@ -79,6 +79,8 @@
href: fundamentals/app-lifecycle.md
- name: App startup
href: fundamentals/app-startup.md
- name: Behaviors
href: fundamentals/behaviors.md
- name: Data binding
items:
- name: Overview
@ -121,10 +123,20 @@
href: fundamentals/bindable-properties.md
- name: Attached properties
href: fundamentals/attached-properties.md
- name: Publish and subscribe to messages
href: fundamentals/messagingcenter.md
- name: Resource dictionaries
href: fundamentals/resource-dictionaries.md
- name: Single project
href: fundamentals/single-project.md
- name: Templates
items:
- name: Control templates
href: fundamentals/controltemplate.md
- name: Data templates
href: fundamentals/datatemplate.md
- name: Triggers
href: fundamentals/triggers.md
- name: User interface
items:
- name: Animation

Просмотреть файл

@ -0,0 +1,321 @@
---
title: "Behaviors"
description: ".NET MAUI behaviors let you add functionality to user interface controls without having to subclass them. Instead, the functionality is implemented in a behavior class and attached to the control as if it was part of the control itself."
ms.date: 02/18/2022
---
# Behaviors
.NET Multi-platform App UI (.NET MAUI) behaviors let you add functionality to user interface controls without having to subclass them. Instead, the functionality is implemented in a behavior class and attached to the control as if it was part of the control itself.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
Behaviors enable you to implement code that you would normally have to write as code-behind, because it directly interacts with the API of the control in such a way that it can be concisely attached to the control and packaged for reuse across more than one application. They can be used to provide a full range of functionality to controls, such as:
- Adding an email validator to an `Entry`.
- Creating a rating control using a tap gesture recognizer.
- Controlling an animation.
.NET MAUI supports two different types of behaviors:
- Attached behaviors are `static` classes with one or more attached properties. For more information about attached behaviors, see [Attached behaviors](#attached-behaviors).
- .NET MAUI behaviors are classes that derive from the `Behavior` or `Behavior<T>` class, where `T` is the type of the control to which the behavior should apply. For more information, see [.NET MAUI Behaviors](#net-maui-behaviors).
## Attached behaviors
Attached behaviors are static classes with one or more attached properties. An attached property is a special type of bindable property. They are defined in one class but attached to other objects, and they are recognizable in XAML as attributes that contain a class and a property name separated by a period. For more information about attached properties, see [Attached properties](~/fundamentals/attached-properties.md).
An attached property can define a `propertyChanged` delegate that will be executed when the value of the property changes, such as when the property is set on a control. When the `propertyChanged` delegate executes, it's passed a reference to the control on which it is being attached, and parameters that contain the old and new values for the property. This delegate can be used to add new functionality to the control that the property is attached to by manipulating the reference that is passed in, as follows:
1. The `propertyChanged` delegate casts the control reference, which is received as a `BindableObject`, to the control type that the behavior is designed to enhance.
1. The `propertyChanged` delegate modifies properties of the control, calls methods of the control, or registers event handlers for events exposed by the control, to implement the core behavior functionality.
> [!WARNING]
> Attached behaviors are defined in a `static` class, with `static` properties and methods. This makes it difficult to create attached behaviors that have state.
### Create an attached behavior
An attached behavior can be implemented by creating a static class that contains an attached property that specifies a `propertyChanged` delegate.
The following example shows the `AttachedNumericValidationBehavior` class, which highlights the value entered by the user into an `Entry` control in red if it's not a `double`:
```csharp
public static class AttachedNumericValidationBehavior
{
public static readonly BindableProperty AttachBehaviorProperty =
BindableProperty.CreateAttached("AttachBehavior", typeof(bool), typeof(AttachedNumericValidationBehavior), false, propertyChanged: OnAttachBehaviorChanged);
public static bool GetAttachBehavior(BindableObject view)
{
return (bool)view.GetValue(AttachBehaviorProperty);
}
public static void SetAttachBehavior(BindableObject view, bool value)
{
view.SetValue(AttachBehaviorProperty, value);
}
static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
{
Entry entry = view as Entry;
if (entry == null)
{
return;
}
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{
entry.TextChanged += OnEntryTextChanged;
}
else
{
entry.TextChanged -= OnEntryTextChanged;
}
}
static void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Colors.Black : Colors.Red;
}
}
```
In this example, the `AttachedNumericValidationBehavior` class contains an attached property named `AttachBehavior` with a `static` getter and setter, which controls the addition or removal of the behavior to the control to which it will be attached. This attached property registers the `OnAttachBehaviorChanged` method that will be executed when the value of the property changes. This method registers or de-registers an event handler for the `TextChanged` event, based on the value of the `AttachBehavior` attached property. The core functionality of the behavior is provided by the `OnEntryTextChanged` method, which parses the value entered in the `Entry` and sets the `TextColor` property to red if the value isn't a `double`.
### Consume an attached behavior
An attached behavior can be consumed by setting its attached property on the target control.
The following example shows consuming the `AttachedNumericValidationBehavior` class on an `Entry` by adding the `AttachBehavior` attached property to the `Entry`:
```xaml
<ContentPage ...
xmlns:local="clr-namespace:BehaviorsDemos">
<Entry Placeholder="Enter a System.Double" local:AttachedNumericValidationBehavior.AttachBehavior="true" />
</ContentPage>
```
The equivalent `Entry` in C# is shown in the following code example:
```csharp
Entry entry = new Entry { Placeholder = "Enter a System.Double" };
AttachedNumericValidationBehavior.SetAttachBehavior(entry, true);
```
The following screenshot shows the attached behavior responding to invalid input:
:::image type="content" source="media/behaviors/behavior.png" alt-text="Screenshot of attached behavior responding to invalid input":::
> [!NOTE]
> Attached behaviors are written for a specific control type (or a superclass that can apply to many controls), and they should only be added to a compatible control.
### Remove an attached behavior
The `AttachedNumericValidationBehavior` class can be removed from a control by setting the `AttachBehavior` attached property to `false`:
```xaml
<Entry Placeholder="Enter a System.Double" local:AttachedNumericValidationBehavior.AttachBehavior="false" />
```
At runtime, the `OnAttachBehaviorChanged` method will be executed when the value of the `AttachBehavior` attached property is set to `false`. The `OnAttachBehaviorChanged` method will then de-register the event handler for the `TextChanged` event, ensuring that the behavior isn't executed as you interact with the control.
## .NET MAUI behaviors
.NET MAUI behaviors are created by deriving from the `Behavior` or `Behavior<T>` class.
The process for creating a .NET MAUI behavior is as follows:
1. Create a class that inherits from the `Behavior` or `Behavior<T>` class, where `T` is the type of the control to which the behavior should apply.
1. Override the `OnAttachedTo` method to perform any required setup.
1. Override the `OnDetachingFrom` method to perform any required cleanup.
1. Implement the core functionality of the behavior.
This results in the structure shown in the following example:
```csharp
public class MyBehavior : Behavior<View>
{
protected override void OnAttachedTo(View bindable)
{
base.OnAttachedTo(bindable);
// Perform setup
}
protected override void OnDetachingFrom(View bindable)
{
base.OnDetachingFrom(bindable);
// Perform clean up
}
// Behavior implementation
}
```
The `OnAttachedTo` method is called immediately after the behavior is attached to a control. This method receives a reference to the control to which it is attached, and can be used to register event handlers or perform other setup that's required to support the behavior functionality. For example, you could subscribe to an event on a control. The behavior functionality would then be implemented in the event handler for the event.
The `OnDetachingFrom` method is called when the behavior is removed from the control. This method receives a reference to the control to which it is attached, and is used to perform any required cleanup. For example, you could unsubscribe from an event on a control to prevent memory leaks.
The behavior can then be consumed by attaching it to the `Behaviors` collection of the control.
### Create a .NET MAUI Behavior
A .NET MAUI behavior can be implemented by creating a class that derives from the `Behavior` or `Behavior<T>` class, and overriding the `OnAttachedTo` and `OnDetachingFrom` methods.
The following example shows the `NumericValidationBehavior` class, which highlights the value entered by the user into an `Entry` control in red if it's not a `double`:
```csharp
public class NumericValidationBehavior : Behavior<Entry>
{
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
}
}
```
In this example, the `NumericValidationBehavior` class derives from the `Behavior<T>` class, where `T` is an `Entry`. The `OnAttachedTo` method registers an event handler for the `TextChanged` event, with the `OnDetachingFrom` method de-registering the `TextChanged` event to prevent memory leaks. The core functionality of the behavior is provided by the `OnEntryTextChanged` method, which parses the value entered in the `Entry` and sets the `TextColor` property to red if the value isn't a `double`.
> [!IMPORTANT]
> .NET MAUI does not set the `BindingContext` of a behavior, because behaviors can be shared and applied to multiple controls through styles.
### Consume a .NET MAUI behavior
Every .NET MAUI control has a `Behaviors` collection, to which one or more behaviors can be added:
```xaml
<Entry Placeholder="Enter a System.Double">
<Entry.Behaviors>
<local:NumericValidationBehavior />
</Entry.Behaviors>
</Entry>
```
The equivalent `Entry` in C# is shown in the following code example:
```csharp
Entry entry = new Entry { Placeholder = "Enter a System.Double" };
entry.Behaviors.Add(new NumericValidationBehavior());
```
The following screenshot shows the .NET MAUI behavior responding to invalid input:
:::image type="content" source="media/behaviors/behavior.png" alt-text="Screenshot of .NET MAUI behavior responding to invalid input":::
> [!WARNING]
> .NET MAUI behaviors are written for a specific control type (or a superclass that can apply to many controls), and they should only be added to a compatible control. Attempting to attach a .NET MAUI behavior to an incompatible control will result in an exception being thrown.
### Consume a .NET MAUI behavior with a style
.NET MAUI behaviors can be consumed by an explicit or implicit style. However, creating a style that sets the `Behaviors` property of a control is not possible because the property is read-only. The solution is to add an attached property to the behavior class that controls adding and removing the behavior. The process is as follows:
1. Add an attached property to the behavior class that will be used to control the addition or removal of the behavior to the control to which the behavior will attached. Ensure that the attached property registers a `propertyChanged` delegate that will be executed when the value of the property changes.
1. Create a `static` getter and setter for the attached property.
1. Implement logic in the `propertyChanged` delegate to add and remove the behavior.
The following code example shows an attached property that controls adding and removing the `NumericValidationBehavior`:
The following example shows the `NumericValidationStyleBehavior` class, which has an attached property that controls adding and removing the behavior:
```csharp
public class NumericValidationStyleBehavior : Behavior<Entry>
{
public static readonly BindableProperty AttachBehaviorProperty =
BindableProperty.CreateAttached("AttachBehavior", typeof(bool), typeof(NumericValidationStyleBehavior), false, propertyChanged: OnAttachBehaviorChanged);
public static bool GetAttachBehavior(BindableObject view)
{
return (bool)view.GetValue(AttachBehaviorProperty);
}
public static void SetAttachBehavior(BindableObject view, bool value)
{
view.SetValue(AttachBehaviorProperty, value);
}
static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
{
Entry entry = view as Entry;
if (entry == null)
{
return;
}
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{
entry.Behaviors.Add(new NumericValidationStyleBehavior());
}
else
{
Behavior toRemove = entry.Behaviors.FirstOrDefault(b => b is NumericValidationStyleBehavior);
if (toRemove != null)
{
entry.Behaviors.Remove(toRemove);
}
}
}
...
}
```
In this example, the `NumericValidationStyleBehavior` class contains an attached property named `AttachBehavior` with a `static` getter and setter, which controls the addition or removal of the behavior to the control to which it will be attached. This attached property registers the `OnAttachBehaviorChanged` method that will be executed when the value of the property changes. This method adds or removes the behavior to the control, based on the value of the `AttachBehavior` attached property.
The following code example shows an *explicit* style for the `NumericValidationStyleBehavior` that uses the `AttachBehavior` attached property, and which can be applied to `Entry` controls:
```xaml
<Style x:Key="NumericValidationStyle" TargetType="Entry">
<Style.Setters>
<Setter Property="local:NumericValidationStyleBehavior.AttachBehavior" Value="true" />
</Style.Setters>
</Style>
```
The `Style` can be applied to an `Entry` by setting its `Style` property to the style using the `StaticResource` markup extension:
```xaml
<Entry Placeholder="Enter a System.Double" Style="{StaticResource NumericValidationStyle}">
```
<!-- For more information about styles, see [Styles](~/user-interface/styles/xaml/index.md). -->
> [!NOTE]
> While you can add bindable properties to a behavior that is set or queried in XAML, if you do create behaviors that have state they should not be shared between controls in a `Style` in a `ResourceDictionary`.
### Remove a .NET MAUI behavior
The `OnDetachingFrom` method is called when a behavior is removed from a control, and is used to perform any required cleanup such as unsubscribing from an event to prevent a memory leak. However, behaviors are not implicitly removed from controls unless the control's `Behaviors` collection is modified by the `Remove` or `Clear` method:
```csharp
Behavior toRemove = entry.Behaviors.FirstOrDefault(b => b is NumericValidationStyleBehavior);
if (toRemove != null)
{
entry.Behaviors.Remove(toRemove);
}
```
Alternatively, the control's `Behaviors` collection can be cleared:
```csharp
entry.Behaviors.Clear();
```
> [!NOTE]
> .NET MAUI behaviors are not implicitly removed from controls when pages are popped from the navigation stack. Instead, they must be explicitly removed prior to pages going out of scope.

Просмотреть файл

@ -0,0 +1,603 @@
---
title: "Control templates"
description: ".NET MAUI control templates define the visual structure of ContentView derived custom controls, and ContentPage derived pages."
ms.date: 02/18/2022
---
# Control templates
.NET Multi-platform App UI (.NET MAUI) control templates enable you to define the visual structure of `ContentView` derived custom controls, and `ContentPage` derived pages. Control templates separate the user interface (UI) for a custom control, or page, from the logic that implements the control or page. Additional content can also be inserted into the templated custom control, or templated page, at a pre-defined location.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
For example, a control template can be created that redefines the UI provided by a custom control. The control template can then be consumed by the required custom control instance. Alternatively, a control template can be created that defines any common UI that will be used by multiple pages in an app. The control template can then be consumed by multiple pages, with each page still displaying its unique content.
## Create a ControlTemplate
The following example shows the code for a `CardView` custom control:
```csharp
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);
}
...
}
```
The `CardView` class, which derives from the `ContentView` class, represents a custom control that displays data in a card-like layout. The class contains properties, which are backed by bindable properties, for the data it displays. However, the `CardView` class does not define any UI. Instead, the UI will be defined with a control template. For more information about creating `ContentView` derived custom controls, see [ContentView](~/user-interface/controls/contentview.md).
A control template is created with the `ControlTemplate` type. When you create a `ControlTemplate`, you combine `View` objects to build the UI for a custom control, or page. A `ControlTemplate` must have only one `View` as its root element. However, the root element usually contains other `View` objects. The combination of objects makes up the control's visual structure.
While a `ControlTemplate` can be defined inline, the typical approach to declaring a `ControlTemplate` is as a resource in a resource dictionary. Because control templates are resources, they obey the same scoping rules that apply to all resources. For example, if you declare a control template in your app-level resource dictionary, the template can be used anywhere in your app. If you define the template in a page, only that page can use the control template. For more information about resources, see [Resource dictionaries](~/fundamentals/resource-dictionaries.md).
The following XAML example shows a `ControlTemplate` for `CardView` objects:
```xaml
<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}"
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>
```
When a `ControlTemplate` is declared as a resource, it must have a key specified with the `x:Key` attribute so that it can be identified in the resource dictionary. In this example, the root element of the `CardViewControlTemplate` is a `Frame` object. The `Frame` object uses the `RelativeSource` markup extension to set its `BindingContext` to the runtime object instance to which the template will be applied, which is known as the *templated parent*. The `Frame` object uses a combination of `Grid`, `Frame`, `Image`, `Label`, and `BoxView` objects to define the visual structure of a `CardView` object. The binding expressions of these objects resolve against `CardView` properties, due to inheriting the `BindingContext` from the root `Frame` element. For more information about the `RelativeSource` markup extension, see [Relative bindings](~/fundamentals/data-binding/relative-bindings.md).
## Consume a ControlTemplate
A `ControlTemplate` can be applied to a `ContentView` derived custom control by setting its `ControlTemplate` property to the control template object. Similarly, a `ControlTemplate` can be applied to a `ContentPage` derived page by setting its `ControlTemplate` property to the control template object. At runtime, when a `ControlTemplate` is applied, all of the controls that are defined in the `ControlTemplate` are added to the visual tree of the templated custom control, or templated page.
The following example shows the `CardViewControlTemplate` being assigned to the `ControlTemplate` property of each `CardView` object:
```xaml
<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>
```
In this example, the controls in the `CardViewControlTemplate` become part of the visual tree for each `CardView` object. Because the root `Frame` object for the control template sets its `BindingContext` to the templated parent, the `Frame` and its children resolve their binding expressions against the properties of each `CardView` object.
The following screenshot shows the `CardViewControlTemplate` applied to the three `CardView` objects:
:::image type="content" source="media/controltemplate/relativesource-controltemplate.png" alt-text="Screenshot of two templated CardView objects.":::
> [!IMPORTANT]
> The point in time that a `ControlTemplate` is applied to a control instance can be detected by overriding the `OnApplyTemplate` method in the templated custom control, or templated page. For more information, see [Get a named element from a template](#get-a-named-element-from-a-template).
## Pass parameters with TemplateBinding
The `TemplateBinding` markup extension binds a property of an element that is in a `ControlTemplate` to a public property that is defined by the templated custom control or templated page. When you use a `TemplateBinding`, you enable properties on the control to act as parameters to the template. Therefore, when a property on a templated custom control or templated page is set, that value is passed onto the element that has the `TemplateBinding` on it.
> [!IMPORTANT]
> The `TemplateBinding` markup expression enables the `RelativeSource` binding from the previous control template to be removed, and replaces the `Binding` expressions.
The `TemplateBinding` markup extension defines the following properties:
- `Path`, of type `string`, the path to the property.
- `Mode`, of type `BindingMode`, the direction in which changes propagate between the *source* and *target*.
- `Converter`, of type `IValueConverter`, the binding value converter.
- `ConverterParameter`, of type `object`, the parameter to the binding value converter.
- `StringFormat`, of type `string`, the string format for the binding.
The `ContentProperty` for the `TemplateBinding` markup extension is `Path`. Therefore, the "Path=" part of the markup extension can be omitted if the path is the first item in the `TemplateBinding` expression. For more information about using these properties in a binding expression, see [Data binding](~/fundamentals/data-binding/index.md).
> [!WARNING]
> The `TemplateBinding` markup extension should only be used within a `ControlTemplate`. However, attempting to use a `TemplateBinding` expression outside of a `ControlTemplate` will not result in a build error or an exception being thrown.
The following XAML example shows a `ControlTemplate` for `CardView` objects, that uses the `TemplateBinding` markup extension:
```xaml
<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}"
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>
```
In this example, the `TemplateBinding` markup extension resolves binding expressions against the properties of each `CardView` object. The following screenshot shows the `CardViewControlTemplate` applied to the `CardView` objects:
:::image type="content" source="media/controltemplate/templatebinding-controltemplate.png" alt-text="Screenshot of templated CardView objects.":::
> [!IMPORTANT]
> Using the `TemplateBinding` markup extension is equivalent to setting the `BindingContext` of the root element in the template to its templated parent with the `RelativeSource` markup extension, and then resolving bindings of child objects with the `Binding` markup extension. In fact, the `TemplateBinding` markup extension creates a `Binding` whose `Source` is `RelativeBindingSource.TemplatedParent`.
## Apply a ControlTemplate with a style
Control templates can also be applied with styles. This is achieved by creating an *implicit* or *explicit* style that consumes the `ControlTemplate`.
The following XAML example shows an *implicit* style that consumes the `CardViewControlTemplate`:
```xaml
<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" />
<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"/>
</StackLayout>
</ContentPage>
```
In this example, the *implicit* `Style` is automatically applied to each `CardView` object, and sets the `ControlTemplate` property of each `CardView` to `CardViewControlTemplate`.
<!-- For more information about styles, see [Styles](~/user-interface/styles/xaml/index.md). -->
## Redefine a controls UI
When a `ControlTemplate` is instantiated and assigned to the `ControlTemplate` property of a `ContentView` derived custom control, or a `ContentPage` derived page, the visual structure defined for the custom control or page is replaced with the visual structure defined in the `ControlTemplate`.
For example, the `CardViewUI` custom control defines its user interface using the following XAML:
```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}"
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>
```
However, the controls that comprise this UI can be replaced by defining a new visual structure in a `ControlTemplate`, and assigning it to the `ControlTemplate` property of a `CardViewUI` object:
```xaml
<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>
<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}" />
</StackLayout>
</ContentPage>
```
In this example, the visual structure of the `CardViewUI` object is redefined in a `ControlTemplate` that provides a more compact visual structure that's suitable for a condensed list:
:::image type="content" source="media/controltemplate/redefine-controltemplate.png" alt-text="Screenshot of templated CardViewUI objects.":::
## Substitute content into a ContentPresenter
A `ContentPresenter` can be placed in a control template to mark where content to be displayed by the templated custom control or templated page will appear. The custom control or page that consumes the control template will then define content to be displayed by the `ContentPresenter`. The following diagram illustrates a `ControlTemplate` for a page that contains a number of controls, including a `ContentPresenter` marked by a blue rectangle:
:::image type="content" source="media/controltemplate/controltemplate.png" alt-text="Control template for a ContentPage." border="false":::
The following XAML shows a control template named `TealTemplate` that contains a `ContentPresenter` in its visual structure:
```xaml
<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://docs.microsoft.com/dotnet/maui/"
HorizontalOptions="End"
VerticalOptions="Center" />
</Grid>
</ControlTemplate>
```
The following example shows `TealTemplate` assigned to the `ControlTemplate` property of a `ContentPage` derived page:
```xaml
<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>
```
At runtime, when `TealTemplate` is applied to the page, the page content is substituted into the `ContentPresenter` defined in the control template:
:::image type="content" source="media/controltemplate/tealtemplate-contentpage.png" alt-text="Screenshot of templated page object.":::
## Get a named element from a template
Named elements within a control template can be retrieved from the templated custom control or templated page. This can be achieved with the `GetTemplateChild` method, which returns the named element in the instantiated `ControlTemplate` visual tree, if found. Otherwise, it returns `null`.
After a control template has been instantiated, the template's `OnApplyTemplate` method is called. The `GetTemplateChild` method should therefore be called from a `OnApplyTemplate` override in the templated control or templated page.
> [!IMPORTANT]
> The `GetTemplateChild` method should only be called after the `OnApplyTemplate` method has been called.
The following XAML shows a control template named `TealTemplate` that can be applied to `ContentPage` derived pages:
```xaml
<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>
```
In this example, the `Label` element is named, and can be retrieved in the code for the templated page. This is achieved by calling the `GetTemplateChild` method from the `OnApplyTemplate` override for the templated page:
```csharp
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";
}
}
```
In this example, the `Label` object named `changeThemeLabel` is retrieved once the `ControlTemplate` has been instantiated. `changeThemeLabel` can then be accessed and manipulated by the `AccessTemplateElementPage` class. The following screenshot shows that the text displayed by the `Label` has been changed:
:::image type="content" source="media/controltemplate/get-named-element.png" alt-text="Screenshot of templated page object that's changed.":::
## Bind to a viewmodel
A `ControlTemplate` can data bind to a viewmodel, even when the `ControlTemplate` binds to the templated parent (the runtime object instance to which the template is applied).
The following XAML example shows a page that consumes a viewmodel named `PeopleViewModel`:
```xaml
<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>
```
In this example, the `BindingContext` of the page is set to a `PeopleViewModel` instance. This viewmodel exposes a `People` collection and an `ICommand` named `DeletePersonCommand`. The `StackLayout` on the page uses a bindable layout to data bind to the `People` collection, and the `ItemTemplate` of the bindable layout is set to the `PersonTemplate` resource. This `DataTemplate` specifies that each item in the `People` collection will be displayed using a `CardView` object. The visual structure of the `CardView` object is defined using a `ControlTemplate` named `CardViewControlTemplate`:
```xaml
<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>
```
In this example, the root element of the `ControlTemplate` is a `Frame` object. The `Frame` object uses the `RelativeSource` markup extension to set its `BindingContext` to the templated parent. The binding expressions of the `Frame` object and its children resolve against `CardView` properties, due to inheriting the `BindingContext` from the root `Frame` element. The following screenshot shows the page displaying the `People` collection:
:::image type="content" source="media/controltemplate/viewmodel-controltemplate.png" alt-text="Screenshot of three templated CardView objects that bind to a viewmodel.":::
While the objects in the `ControlTemplate` bind to properties on its templated parent, the `Button` within the control template binds to both its templated parent, and to the `DeletePersonCommand` in the viewmodel. This is because the `Button.Command` property redefines its binding source to be the binding context of the ancestor whose binding context type is `PeopleViewModel`, which is the `StackLayout`. The `Path` part of the binding expressions can then resolve the `DeletePersonCommand` property. However, the `Button.CommandParameter` property doesn't alter its binding source, instead inheriting it from its parent in the `ControlTemplate`. Therefore, the `CommandParameter` property binds to the `CardTitle` property of the `CardView`.
The overall effect of the `Button` bindings is that when the `Button` is tapped, the `DeletePersonCommand` in the `PeopleViewModel` class is executed, with the value of the `CardName` property being passed to the `DeletePersonCommand`. This results in the specified `CardView` being removed from the bindable layout.
For more information about relative bindings, see [Relative bindings](~/fundamentals/data-binding/relative-bindings.md).

Просмотреть файл

@ -0,0 +1,283 @@
---
title: "Data templates"
description: ".NET MAUI data templates provide the ability to define the presentation of data on supported controls. Data templates can be chosen at runtime, using a DataTemplateSelector, based on the value of a data-bound property."
ms.date: 02/21/2022
---
# Data templates
.NET Multi-platform App UI (.NET MAUI) data templates provide the ability to define the presentation of data on supported controls.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
Consider a `ListView` that displays a collection of `Person` objects. The following example shows the definition of the `Person` class:
```csharp
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Location { get; set; }
}
```
The `Person` class defines `Name`, `Age`, and `Location` properties, which can be set when a `Person` object is created. A control that displays collections, such as `ListView`, can be used to display `Person` objects:
```xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataTemplates"
x:Class=""DataTemplates.WithoutDataTemplatePage>
<StackLayout>
<ListView>
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
<local:Person Name="John" Age="37" Location="USA" />
<local:Person Name="Tom" Age="42" Location="UK" />
<local:Person Name="Lucas" Age="29" Location="Germany" />
<local:Person Name="Tariq" Age="39" Location="UK" />
<local:Person Name="Jane" Age="30" Location="USA" />
</x:Array>
</ListView.ItemsSource>
</ListView>
</StackLayout>
</ContentPage>
```
In this example, items are added to the `ListView` by initializing its `ItemsSource` property from an array of `Person` objects. `ListView` calls `ToString` when displaying the objects in the collection. However, because there is no `Person.ToString` override, `ToString` returns the type name of each object:
:::image type="content" source="media/datatemplate/no-data-template.png" alt-text="Screenshot of a ListView without a data template.":::
The `Person` object can override the `ToString` method to display meaningful data:
```csharp
public class Person
{
...
public override string ToString ()
{
return Name;
}
}
```
This results in the `ListView` displaying the `Person.Name` property value for each object in the collection:
:::image type="content" source="media/datatemplate/override-tostring.png" alt-text="Screenshot of a ListView that overrides the Person.ToString method.":::
The `Person.ToString` override could return a formatted string consisting of the `Name`, `Age`, and `Location` properties. However, this approach only offers limited control over the appearance of each item of data. For more flexibility, a `DataTemplate` can be created that defines the appearance of the data.
## Create a DataTemplate
A `DataTemplate` is used to specify the appearance of data, and typically uses data binding to display data. A common usage scenario for data templates is when displaying data from a collection of objects in a control such as a `ListView`, `CollectionView`, or `CarouselView`. For example, when a `ListView` is bound to a collection of `Person` objects, the `ListView.ItemTemplate` property can be set to a `DataTemplate` that defines the appearance of each `Person` object in the `ListView`. The `DataTemplate` will contain objects that bind to property values of each `Person` object. For more information about data binding, see [Data binding](~/fundamentals/data-binding/index.md).
A `DataTemplate` that's defined inline in a control is known as an *inline template*. Alternatively, data templates can be defined as a control-level, page-level, or app-level resource. Choosing where to define a `DataTemplate` impacts where it can be used:
- A `DataTemplate` defined at the control-level can only be applied to the control.
- A `DataTemplate` defined at the page-level can be applied to multiple controls on the page.
- A `DataTemplate` defined at the app-level can be applied to valid controls throughout the app.
> [!NOTE]
> Data templates lower in the view hierarchy take precedence over those defined higher up when they share `x:Key` attributes. For example, an app-level data template will be overridden by a page-level data template, and a page-level data template will be overridden by a control-level data template, or an inline data template.
A `DataTemplate` can be created inline, with a type, or as a resource, regardless of where it's defined.
### Create an inline DataTemplate
An inline data template, which is one that's defined inline in a control, should be used if there's no need to reuse the data template elsewhere. The objects specified in the `DataTemplate` define the appearance of each item of data. A control such as `ListView` can then set its `ItemTemplate` property to the inline `DataTemplate`:
```xaml
<ListView>
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
<local:Person Name="John" Age="37" Location="USA" />
<local:Person Name="Tom" Age="42" Location="UK" />
<local:Person Name="Lucas" Age="29" Location="Germany" />
<local:Person Name="Tariq" Age="39" Location="UK" />
<local:Person Name="Jane" Age="30" Location="USA" />
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
...
<Label Text="{Binding Name}" FontAttributes="Bold" />
<Label Grid.Column="1" Text="{Binding Age}" />
<Label Grid.Column="2" Text="{Binding Location}" HorizontalTextAlignment="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
```
In a `ListView`, the child of an inline `DataTemplate` must be of, or derive from, type `Cell`. In this example, a `ViewCell`, which derives from `Cell` is used, and layout inside the `ViewCell` is managed by a `Grid`. The `Grid` contains three `Label` objects that bind their `Text` properties to properties of each `Person` object in the collection. The following screenshot shows the resulting appearance:
:::image type="content" source="media/datatemplate/data-template-appearance.png" alt-text="Screenshot of a ListView with a data template.":::
### Create a DataTemplate with a type
A `DataTemplate` can be created with a custom cell type. The advantage of this approach is that the appearance defined by the cell type can be reused by multiple data templates throughout an app. A control such as `ListView` can then set its `ItemTemplate` property to the `DataTemplate`:
```xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataTemplates"
x:Class="DataTemplates.WithDataTemplatePageFromType">
<StackLayout>
<ListView>
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
...
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<local:PersonCell />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
```
In this example, the `ListView.ItemTemplate` property is set to a `DataTemplate` that's created from a custom type that defines the cell appearance. The custom type must derive from `ViewCell`:
```xaml
<ViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataTemplates.PersonCell">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" FontAttributes="Bold" />
<Label Grid.Column="1" Text="{Binding Age}" />
<Label Grid.Column="2" Text="{Binding Location}" HorizontalTextAlignment="End" />
</Grid>
</ViewCell>
```
In this example, layout within the `ViewCell` is managed by a `Grid`. The `Grid` contains three `Label` objects that bind their `Text` properties to properties of each `Person` object in the collection.
<!-- > [!NOTE]
> .NET MAUI also includes cell types that can be used to display simple data in `ListView` cells. For more information, see [Cell aAppearance](~/user-interface/controls/listview/customizing-cell-appearance.md). -->
### Create a DataTemplate as a resource
Data templates can be created as reusable objects in a `ResourceDictionary`. This is achieved by giving each `DataTemplate` a unique `x:Key` value, which provides it with a descriptive key in the `ResourceDictionary`. A control such as `ListView` can then set its `ItemTemplate` property to the `DataTemplate`:
```xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataTemplates"
x:Class="DataTemplates.WithDataTemplateResource">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="personTemplate">
<ViewCell>
<Grid>
...
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView ItemTemplate="{StaticResource personTemplate}">
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
...
</x:Array>
</ListView.ItemsSource>
</ListView>
</StackLayout>
</ContentPage>
```
In this example, the `DataTemplate` is assigned to the `ListView.ItemTemplate` property by using the `StaticResource` markup extension. While the `DataTemplate` is defined in the page's `ResourceDictionary`, it could also be defined at the control-level or app-level.
## Create a DataTemplateSelector
A `DataTemplateSelector` can be used to choose a `DataTemplate` at runtime based on the value of a data-bound property. This enables multiple data templates to be applied to the same type of object, to choose their appearance at runtime. A data template selector enables scenarios such as a `ListView`, `CollectionView`, or `CarouselView`, binding to a collection of objects where the appearance of each object can be chosen at runtime by the data template selector returning a specific `DataTemplate`.
A data template selector is implemented by creating a class that inherits from `DataTemplateSelector`. The `OnSelectTemplate` method should then be overridden to return a specific `DataTemplate`:
```csharp
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}
```
In this example, the `OnSelectTemplate` method returns a specific data template based on the value of the `DateOfBirth` property. The returned data template is defined by the `ValidTemplate` or `InvalidTemplate` property, which are set when consuming the data template selector.
### Limitations
`DataTemplateSelector` objects have the following limitations:
- The `DataTemplateSelector` subclass must always return the same template for the same data if queried multiple times.
- The `DataTemplateSelector` subclass must not return another `DataTemplateSelector` subclass.
- The `DataTemplateSelector` subclass must not return new instances of a `DataTemplate` on each call. Instead, the same instance must be returned. Failure to do so will create a memory leak and will disable control virtualization.
- On Android, there can be no more than 20 different data templates per `ListView`.
### Consume a DataTemplateSelector
A data template selector can be consumed by creating it as a resource and assigning its instance to .NET MAUI control properties of type `DataTemplate`, such as `ListView.ItemTemplate`.
The following example shows declaring `PersonDataTemplateSelector` as a page-level resource:
```xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:local="clr-namespace:Selector"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Selector.MainPage">
<ContentPage.Resources>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector x:Key="personDataTemplateSelector"
ValidTemplate="{StaticResource validPersonTemplate}"
InvalidTemplate="{StaticResource invalidPersonTemplate}" />
</ContentPage.Resources>
...
</ContentPage>
```
In this example, the page-level `ResourceDictionary` defines two `DataTemplate` objects and a `PersonDataTemplateSelector` object. The `PersonDataTemplateSelector` object sets its `ValidTemplate` and `InvalidTemplate` properties to the `DataTemplate` objects using the `StaticResource` markup extension. While the resources are defined in the page's `ResourceDictionary`, they could also be defined at the control-level or app-level.
The `PersonDataTemplateSelector` object can be consumed by assigning it to the `ListView.ItemTemplate` property:
```xaml
<ListView x:Name="listView"
ItemTemplate="{StaticResource personDataTemplateSelector}" />
```
At runtime, the `ListView` calls the `PersonDataTemplateSelector.OnSelectTemplate` method for each of the items in the underlying collection, with the call passing the data object as the `item` parameter. The returned `DataTemplate` is then applied to that object.
The following screenshot shows the result of the `ListView` applying the `PersonDataTemplateSelector` to each object in the underlying collection:
:::image type="content" source="media/datatemplate/data-template-selector.png" alt-text="Screenshot of a ListView with a data template selector.":::
In this example, any `Person` object that has a `DateOfBirth` property value greater than or equal to 1980 is displayed in green, with the remaining objects being displayed in red.

Двоичные данные
docs/fundamentals/media/behaviors/behavior.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.6 KiB

Двоичные данные
docs/fundamentals/media/controltemplate/controltemplate.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 23 KiB

Двоичные данные
docs/fundamentals/media/controltemplate/get-named-element.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 28 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 35 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 17 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 35 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 20 KiB

Двоичные данные
docs/fundamentals/media/datatemplate/data-template-appearance.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 11 KiB

Двоичные данные
docs/fundamentals/media/datatemplate/data-template-selector.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 19 KiB

Двоичные данные
docs/fundamentals/media/datatemplate/no-data-template.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 18 KiB

Двоичные данные
docs/fundamentals/media/datatemplate/override-tostring.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.2 KiB

Двоичные данные
docs/fundamentals/media/messagingcenter/messagingcenter.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 23 KiB

Просмотреть файл

@ -0,0 +1,95 @@
---
title: "Publish and subscribe to messages"
description: "The .NET MAUI MessagingCenter class implements the publish-subscribe pattern, allowing message-based communication between components that are inconvenient to link by object and type references."
ms.date: 02/18/2022
---
# Publish and subscribe to messages
The publish-subscribe pattern is a messaging pattern in which publishers send messages without having knowledge of any receivers, known as subscribers. Similarly, subscribers listen for specific messages, without having knowledge of any publishers.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
Events in .NET implement the publish-subscribe pattern, and are the most simple and straightforward approach for a communication layer between components if loose coupling is not required, such as a control and the page that contains it. However, the publisher and subscriber lifetimes are coupled by object references to each other, and the subscriber type must have a reference to the publisher type. This can create memory management issues, especially when there are short lived objects that subscribe to an event of a static or long-lived object. If the event handler isn't removed, the subscriber will be kept alive by the reference to it in the publisher, and this will prevent or delay the garbage collection of the subscriber.
The .NET Multi-platform App UI (.NET MAUI) `MessagingCenter` class implements the publish-subscribe pattern, allowing message-based communication between components that are inconvenient to link by object and type references. This mechanism allows publishers and subscribers to communicate without having a reference to each other, helping to reduce dependencies between them.
The `MessagingCenter` class provides multicast publish-subscribe functionality. This means that there can be multiple publishers that publish a single message, and there can be multiple subscribers listening for the same message:
:::image type="content" source="media/messagingcenter/messagingcenter.png" alt-text="Multicast publish-subscribe functionality." border="false":::
Publishers send messages using the `MessagingCenter.Send` method, while subscribers listen for messages using the `MessagingCenter.Subscribe` method. In addition, subscribers can also unsubscribe from message subscriptions, if required, with the `MessagingCenter.Unsubscribe` method.
> [!IMPORTANT]
> Internally, the `MessagingCenter` class uses weak references. This means that it will not keep objects alive, and will allow them to be garbage collected. Therefore, it should only be necessary to unsubscribe from a message when a class no longer wishes to receive the message.
## Publish a message
`MessagingCenter` messages are strings. Publishers notify subscribers of a message with one of the `MessagingCenter.Send` overloads. The following code example publishes a `Hi` message:
```csharp
MessagingCenter.Send<MainPage>(this, "Hi");
```
In this example the `Send` method specifies a generic argument that represents the sender. To receive the message, a subscriber must also specify the same generic argument, indicating that they are listening for a message from that sender. In addition, this example specifies two method arguments:
- The first argument specifies the sender instance.
- The second argument specifies the message.
Payload data can also be sent with a message:
```csharp
MessagingCenter.Send<MainPage, string>(this, "Hi", "John");
```
In this example, the `Send` method specifies two generic arguments. The first is the type that's sending the message, and the second is the type of the payload data being sent. To receive the message, a subscriber must also specify the same generic arguments. This enables multiple messages that share a message identity but send different payload data types to be received by different subscribers. In addition, this example specifies a third method argument that represents the payload data to be sent to the subscriber. In this case the payload data is a `string`.
The `Send` method will publish the message, and any payload data, using a fire-and-forget approach. Therefore, the message is sent even if there are no subscribers registered to receive the message. In this situation, the sent message is ignored.
## Subscribe to a message
Subscribers can register to receive a message using one of the `MessagingCenter.Subscribe` overloads. The following code example shows an example of this:
```csharp
MessagingCenter.Subscribe<MainPage> (this, "Hi", (sender) =>
{
// Do something whenever the "Hi" message is received
});
```
In this example, the `Subscribe` method subscribes the `this` object to `Hi` messages that are sent by the `MainPage` type, and executes a callback delegate in response to receiving the message. The callback delegate, specified as a lambda expression, could be code that updates the UI, saves some data, or triggers some other operation.
> [!NOTE]
> A subscriber might not need to handle every instance of a published message, and this can be controlled by the generic type arguments that are specified on the `Subscribe` method.
The following example shows how to subscribe to a message that contains payload data:
```csharp
MessagingCenter.Subscribe<MainPage, string>(this, "Hi", async (sender, arg) =>
{
await DisplayAlert("Message received", "arg=" + arg, "OK");
});
```
In this example, the `Subscribe` method subscribes to `Hi` messages that are sent by the `MainPage` type, whose payload data is a `string`. A callback delegate is executed in response to receiving such a message, that displays the payload data in an alert.
> [!IMPORTANT]
> The delegate that's executed by the `Subscribe` method will be executed on the same thread that publishes the message using the `Send` method.
## Unsubscribe from a message
Subscribers can unsubscribe from messages they no longer want to receive. This is achieved with one of the `MessagingCenter.Unsubscribe` overloads:
```csharp
MessagingCenter.Unsubscribe<MainPage>(this, "Hi");
```
In this example, the `Unsubscribe` method unsubscribes the `this` object from the `Hi` message sent by the `MainPage` type.
Messages containing payload data should be unsubscribed from using the `Unsubscribe` overload that specifies two generic arguments:
```csharp
MessagingCenter.Unsubscribe<MainPage, string>(this, "Hi");
```
In this example, the `Unsubscribe` method unsubscribes the `this` object from the `Hi` message sent by the `MainPage` type, whose payload data is a `string`.

Просмотреть файл

@ -0,0 +1,514 @@
---
title: "Triggers"
description: "Triggers allow you to express actions declaratively in XAML that change the appearance of controls based on events or property changes."
ms.date: 02/18/2022
---
# Triggers
.NET Multi-platform App UI (.NET MAUI) triggers allow you to express actions declaratively in XAML that change the appearance of controls based on events or data changes. In addition, state triggers, which are a specialized group of triggers, define when a `VisualState` should be applied.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
You can assign a trigger directly to a control's `Triggers` collection, or add it to a page-level or app-level resource dictionary to be applied to multiple controls.
## Property triggers
A `Trigger` represents a trigger that applies property values, or performs actions, when the specified property meets a specified condition.
The following example shows a `Trigger` that changes an `Entry` background color when it receives focus:
```xaml
<Entry Placeholder="Enter name">
<Entry.Triggers>
<Trigger TargetType="Entry"
Property="IsFocused"
Value="True">
<Setter Property="BackgroundColor"
Value="Yellow" />
<!-- Multiple Setter elements are allowed -->
</Trigger>
</Entry.Triggers>
</Entry>
```
The trigger's declaration specifies the following:
- `TargetType` - the control type that the trigger applies to.
- `Property` - the property on the control that is monitored.
- `Value` - the value, when it occurs for the monitored property, that causes the trigger to activate.
- `Setter` - a collection of `Setter` elements that are applied when the trigger condition is met.
In addition, optional `EnterActions` and `ExitActions` collections can be specified. For more information, see [EnterActions and ExitActions](#enteractions-and-exitactions).
### Apply a trigger using a style
Triggers can also be added to a `Style` declaration on a control, in a page, or an application `ResourceDictionary`. The following example declares an *implicit* style for all `Entry` controls on the page:
```xaml
<ContentPage.Resources>
<Style TargetType="Entry">
<Style.Triggers>
<Trigger TargetType="Entry"
Property="IsFocused"
Value="True">
<Setter Property="BackgroundColor"
Value="Yellow" />
<!-- Multiple Setter elements are allowed -->
</Trigger>
</Style.Triggers>
</Style>
</ContentPage.Resources>
```
## Data triggers
A `DataTrigger` represents a trigger that applies property values, or performs actions, when the bound data meets a specified condition. The `Binding` markup extension is used to monitor for the specified condition.
The following example shows a `DataTrigger` that disables a `Button` when the `Entry` is empty:
```xaml
<Entry x:Name="entry"
Text=""
Placeholder="Enter text />
<Button Text="Save">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Source={x:Reference entry},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled"
Value="False" />
<!-- Multiple Setter elements are allowed -->
</DataTrigger>
</Button.Triggers>
</Button>
```
In this example, when the length of the `Entry` is zero, the trigger is activated.
> [!TIP]
> When evaluating `Path=Text.Length` always provide a default value for the target property (eg. `Text=""`> because otherwise it will be `null` and the trigger won't work like you expect.
In addition, optional `EnterActions` and `ExitActions` collections can be specified. For more information, see [EnterActions and ExitActions](#enteractions-and-exitactions).
## Event triggers
An `EventTrigger` represents a trigger that applies a set of actions in response to an event. Unlike `Trigger`, `EventTrigger` has no concept of termination of state, so the actions will not be undone once the condition that raised the event is no longer true.
An `EventTrigger` only requires an `Event` property to be set:
```xaml
<EventTrigger Event="TextChanged">
<local:NumericValidationTriggerAction />
</EventTrigger>
```
In this example, there are no `Setter` elements. Instead, there's a `NumericalValidationTriggerAction` object.
> [!NOTE]
> Event triggers don't support `EnterActions` and `ExitActions`.
A trigger action implementation must:
- Implement the generic `TriggerAction<T>` class, with the generic parameter corresponding with the type of control the trigger will be applied to. You can use classes such as `VisualElement` to write trigger actions that work with a variety of controls, or specify a control type like `Entry`.
- Override the `Invoke` method. This method is called whenever the trigger event occurs.
- Optionally expose properties that can be set in XAML when the trigger is declared.
The following example shows the `NumericValidationTriggerAction` class:
```csharp
public class NumericValidationTriggerAction : TriggerAction<Entry>
{
protected override void Invoke(Entry entry)
{
double result;
bool isValid = Double.TryParse(entry.Text, out result);
entry.TextColor = isValid ? Colors.Black : Colors.Red;
}
}
```
> [!WARNING]
> Be careful when sharing triggers in a `ResourceDictionary`. One instance will be shared among controls so any state that is configured once will apply to them all.
## Multi-triggers
A `MultiTrigger` represents a trigger that applies property values, or performs actions, when a set of conditions are satisfied. All the conditions must be true before the `Setter` objects are applied.
The following example shows a `MultiTrigger` that binds to two `Entry` objects:
```xaml
<Entry x:Name="email"
Text="" />
<Entry x:Name="phone"
Text="" />
<Button Text="Save">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference email},
Path=Text.Length}"
Value="0" />
<BindingCondition Binding="{Binding Source={x:Reference phone},
Path=Text.Length}"
Value="0" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
<!-- multiple Setter elements are allowed -->
</MultiTrigger>
</Button.Triggers>
</Button>
```
In addition, the `MultiTrigger.Conditions` collection can also contain `PropertyCondition` objects:
```xaml
<PropertyCondition Property="Text"
Value="OK" />
```
## EnterActions and ExitActions
An alternative approach to implementing changes when a trigger occurs is by specifying `EnterActions` and `ExitActions` collections, and creating `TriggerAction<T>` implementations.
The `EnterActions` collection, of type `IList<TriggerAction>`, defines a collection that will be invoked when the trigger condition is met. The `ExitActions` collection, of type `IList<TriggerAction>`, defines a collection that will be invoked after the trigger condition is no longer met.
> [!NOTE]
> The `TriggerAction` objects defined in the `EnterActions` and `ExitActions` collections are ignored by the `EventTrigger` class.
The following example shows a property trigger that specifies an `EnterAction` and an `ExitAction`:
```xaml
<Entry Placeholder="Enter job title">
<Entry.Triggers>
<Trigger TargetType="Entry"
Property="Entry.IsFocused"
Value="True">
<Trigger.EnterActions>
<local:FadeTriggerAction StartsFrom="0" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<local:FadeTriggerAction StartsFrom="1" />
</Trigger.ExitActions>
</Trigger>
</Entry.Triggers>
</Entry>
```
A trigger action implementation must:
- Implement the generic `TriggerAction<T>` class, with the generic parameter corresponding with the type of control the trigger will be applied to. You can use classes such as `VisualElement` to write trigger actions that work with a variety of controls, or specify a control type like `Entry`.
- Override the `Invoke` method. This method is called whenever the trigger event occurs.
- Optionally expose properties that can be set in XAML when the trigger is declared.
The following example shows the `FadeTriggerAction` class:
```csharp
public class FadeTriggerAction : TriggerAction<VisualElement>
{
public int StartsFrom { get; set; }
protected override void Invoke(VisualElement sender)
{
sender.Animate("FadeTriggerAction", new Animation((d) =>
{
var val = StartsFrom == 1 ? d : 1 - d;
sender.BackgroundColor = Color.FromRgb(1, val, 1);
}),
length: 1000, // milliseconds
easing: Easing.Linear);
}
}
```
> [!NOTE]
> You can provide `EnterActions` and `ExitActions` as well as `Setter` objects in a trigger, but be aware that the `Setter` objects are called immediately (they do not wait for the `EnterAction` or `ExitAction` to complete).
## State triggers
State triggers are a specialized group of triggers that define the conditions under which a `VisualState` should be applied.
State triggers are added to the `StateTriggers` collection of a `VisualState`. This collection can contain a single state trigger, or multiple state triggers. A `VisualState` will be applied when any state triggers in the collection are active.
When using state triggers to control visual states, .NET MAUI uses the following precedence rules to determine which trigger (and corresponding `VisualState`) will be active:
1. Any trigger that derives from `StateTriggerBase`.
1. An `AdaptiveTrigger` activated due to the `MinWindowWidth` condition being met.
1. An `AdaptiveTrigger` activated due to the `MinWindowHeight` condition being met.
If multiple triggers are simultaneously active (for example, two custom triggers) then the first trigger declared in the markup takes precedence.
> [!NOTE]
> State triggers can be set in a `Style`, or directly on elements.
<!-- For more information about visual states, see [Visual State Manager](~/user-interface/visual-state-manager.md). -->
### State trigger
The `StateTrigger` class, which derives from the `StateTriggerBase` class, has an `IsActive` bindable property. A `StateTrigger` triggers a `VisualState` change when the `IsActive` property changes value.
The `StateTriggerBase` class, which is the base class for all state triggers, has an `IsActive` property and an `IsActiveChanged` event. This event fires whenever a `VisualState` change occurs. In addition, the `StateTriggerBase` class has overridable `OnAttached` and `OnDetached` methods.
> [!IMPORTANT]
> The `StateTrigger.IsActive` bindable property hides the inherited `StateTriggerBase.IsActive` property.
The following XAML example shows a `Style` that includes `StateTrigger` objects:
```xaml
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Checked">
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding IsToggled}"
IsActiveChanged="OnCheckedStateIsActiveChanged" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding IsToggled, Converter={StaticResource inverseBooleanConverter}}"
IsActiveChanged="OnUncheckedStateIsActiveChanged" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
```
In this example, the implicit `Style` targets `Grid` objects. When the `IsToggled` property of the bound object is `true`, the background color of the `Grid` is set to black. When the `IsToggled` property of the bound object becomes `false`, a `VisualState` change is triggered, and the background color of the `Grid` becomes white.
In addition, every time a `VisualState` change occurs, the `IsActiveChanged` event for the `VisualState` is fired. Each `VisualState` registers an event handler for this event:
```csharp
void OnCheckedStateIsActiveChanged(object sender, EventArgs e)
{
StateTriggerBase stateTrigger = sender as StateTriggerBase;
Console.WriteLine($"Checked state active: {stateTrigger.IsActive}");
}
void OnUncheckedStateIsActiveChanged(object sender, EventArgs e)
{
StateTriggerBase stateTrigger = sender as StateTriggerBase;
Console.WriteLine($"Unchecked state active: {stateTrigger.IsActive}");
}
```
In this example, when a handler for the `IsActiveChanged` event is fired, the handler outputs whether the `VisualState` is active or not. For example, the following messages are output to the console window when changing from the `Checked` visual state to the `Unchecked` visual state:
```
Checked state active: False
Unchecked state active: True
```
> [!NOTE]
> Custom state triggers can be created by deriving from the `StateTriggerBase` class, and overriding the `OnAttached` and `OnDetached` methods to perform any required registrations and cleanup.
### Adaptive trigger
An `AdaptiveTrigger` triggers a `VisualState` change when the window is a specified height or width. This trigger has two bindable properties:
- `MinWindowHeight`, of type `double`, which indicates the minimum window height at which the `VisualState` should be applied.
- `MinWindowWidth`, of type `double`, which indicates the minimum window width at which the `VisualState` should be applied.
> [!NOTE]
> The `AdaptiveTrigger` derives from the `StateTriggerBase` class and can therefore attach an event handler to the `IsActiveChanged` event.
The following XAML example shows a `Style` that includes `AdaptiveTrigger` objects:
```xaml
<Style TargetType="StackLayout">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Vertical">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="Orientation"
Value="Vertical" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Horizontal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="800" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="Orientation"
Value="Horizontal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
```
In this example, the implicit `Style` targets `StackLayout` objects. When the window width is between 0 and 800 device-independent units, `StackLayout` objects to which the `Style` is applied will have a vertical orientation. When the window width is >= 800 device-independent units, the `VisualState` change is triggered, and the `StackLayout` orientation changes to horizontal.
The `MinWindowHeight` and `MinWindowWidth` properties can be used independently or in conjunction with each other. The following XAML shows an example of setting both properties:
```xaml
<AdaptiveTrigger MinWindowWidth="800"
MinWindowHeight="1200"/>
```
In this example, the `AdaptiveTrigger` indicates that the corresponding `VisualState` will be applied when the current window width is >= 800 device-independent units and the current window height is >= 1200 device-independent units.
### Compare state trigger
The `CompareStateTrigger` triggers a `VisualState` change when a property is equal to a specific value. This trigger has two bindable properties:
- `Property`, of type `object`, which indicates the property being compared by the trigger.
- `Value`, of type `object`, which indicates the value at which the `VisualState` should be applied.
> [!NOTE]
> The `CompareStateTrigger` derives from the `StateTriggerBase` class and can therefore attach an event handler to the `IsActiveChanged` event.
The following XAML example shows a `Style` that includes `CompareStateTrigger` objects:
```xaml
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Checked">
<VisualState.StateTriggers>
<CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}"
Value="True" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.StateTriggers>
<CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}"
Value="False" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
...
<Grid>
<Frame BackgroundColor="White"
CornerRadius="12"
Margin="24"
HorizontalOptions="Center"
VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<CheckBox x:Name="checkBox"
VerticalOptions="Center" />
<Label Text="Check the CheckBox to modify the Grid background color."
VerticalOptions="Center" />
</StackLayout>
</Frame>
</Grid>
```
In this example, the implicit `Style` targets `Grid` objects. When the `IsChecked` property of the `CheckBox` is `false`, the background color of the `Grid` is set to white. When the `CheckBox.IsChecked` property becomes `true`, a `VisualState` change is triggered, and the background color of the `Grid` becomes black.
### Device state trigger
The `DeviceStateTrigger` triggers a `VisualState` change based on the device platform the app is running on. This trigger has a single bindable property:
- `Device`, of type `string`, which indicates the device platform on which the `VisualState` should be applied.
> [!NOTE]
> The `DeviceStateTrigger` derives from the `StateTriggerBase` class and can therefore attach an event handler to the `IsActiveChanged` event.
The following XAML example shows a `Style` that includes `DeviceStateTrigger` objects:
```xaml
<Style x:Key="DeviceStateTriggerPageStyle"
TargetType="ContentPage">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="iOS">
<VisualState.StateTriggers>
<DeviceStateTrigger Device="iOS" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Silver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Android">
<VisualState.StateTriggers>
<DeviceStateTrigger Device="Android" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="#2196F3" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
```
In this example, the explicit `Style` targets `ContentPage` objects. `ContentPage` objects that consume the style set their background color to silver on iOS, and to pale blue on Android.
### Orientation state trigger
The `OrientationStateTrigger` triggers a `VisualState` change when the orientation of the device changes. This trigger has a single bindable property:
- `Orientation`, of type `DeviceOrientation`, which indicates the orientation to which the `VisualState` should be applied.
> [!NOTE]
> The `OrientationStateTrigger` derives from the `StateTriggerBase` class and can therefore attach an event handler to the `IsActiveChanged` event.
The following XAML example shows a `Style` that includes `OrientationStateTrigger` objects:
```xaml
<Style x:Key="OrientationStateTriggerPageStyle"
TargetType="ContentPage">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Silver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Landscape">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Landscape" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
```
In this example, the explicit `Style` targets `ContentPage` objects. `ContentPage` objects that consume the style set their background color to silver when the orientation is portrait, and set their background color to white when the orientation is landscape.

Просмотреть файл

@ -60,12 +60,18 @@ landingContent:
url: fundamentals/app-lifecycle.md
- text: App startup
url: fundamentals/app-startup.md
- text: Behaviors
url: fundamentals/behaviors.md
- text: Data binding
url: fundamentals/data-binding/index.md
- text: Publish and subscribe to messages
url: fundamentals/messagingcenter.md
- text: Resource dictionaries
url: fundamentals/resource-dictionaries.md
- text: Single project
url: fundamentals/single-project.md
- text: Triggers
url: fundamentals/triggers.md
- title: User interface
linkLists:
- linkListType: concept