Поведение

Browse sample. Обзор примера

Поведение пользовательского интерфейса многоплатформенного приложения .NET (.NET MAUI) позволяет добавлять функциональные возможности в элементы управления пользовательским интерфейсом без необходимости подкласса. Функциональность реализуется в классе реакции на событие и присоединяется к элементу управления так, как если бы она была его частью.

Реакции на события позволяют реализовать код, который обычно пришлось бы писать как код программной части, так как он напрямую взаимодействует с API элемента управления таким образом, что его можно быстро подключить к элементу управления и упаковать для повторного использования в нескольких приложениях. С их помощью можно реализовать полный спектр функциональных возможностей в элементах управления, например:

  • добавление проверяющего элемента управления электронной почты в Entry;
  • создание элемента управления оценкой с помощью распознавателя жестов касания;
  • управление анимацией;

.NET MAUI поддерживает три различных типа поведения:

  • Вложенные реакции на события — это классы static с одним или несколькими вложенными свойствами. Дополнительные сведения о присоединенном поведении см. в разделе "Присоединенное поведение".
  • Поведение .NET MAUI — это классы, производные от Behavior класса или Behavior<T> класса, где T тип элемента управления, к которому должно применяться поведение. Дополнительные сведения см. в статье о поведении MAUI .NET.
  • Поведение платформы — это классы, производные от PlatformBehavior<TView> класса или PlatformBehavior<TView,TPlatformView> класса. Это поведение может реагировать на произвольные условия и события в собственном элементе управления. Дополнительные сведения см. в разделе "Поведение платформы".

Присоединенное поведение

Присоединенные реакции на события — это статические классы с одним или несколькими присоединенными свойствами. Присоединенное свойство — это привязываемое свойство особого типа. Оно определяется в одном классе, но присоединяется к другим объектам и распознается в XAML как атрибут, который содержит имя класса и имя свойства, разделенные точкой. Дополнительные сведения о присоединенных свойствах см. в разделе "Присоединенные свойства".

Присоединенное свойство может определять делегат propertyChanged, который будет выполняться при изменении значения свойства, например при задании свойства для элемента управления. Когда делегат propertyChanged выполняется, ему передается ссылка на элемент управления, к которому он присоединяется, а также параметры, содержащие старое и новое значения свойства. С помощью этого делегата можно расширять функциональность элемента управления, к которому присоединяется свойство, путем манипуляций с передаваемой ссылкой.

  1. Делегат propertyChanged приводит ссылку на элемент управления, полученную как BindableObject, к типу элемента управления, который должна расширять реакция на событие.
  2. Делегат propertyChanged изменяет свойства элемента управления, вызывает его методы или регистрирует обработчики событий, предоставляемых элементом управления, для реализации базовой функциональности реакции на событие.

Предупреждение

Присоединенное поведение определяется в static классе с свойствами static и методами. Это затрудняет создание присоединенных реакций на события с состоянием.

Создание присоединенного поведения

Присоединенное поведение можно реализовать путем создания статического класса, содержащего присоединенное свойство, указывающее propertyChanged делегат.

В следующем примере показан AttachedNumericValidationBehavior класс, который выделяет значение, введенное пользователем в Entry элемент управления красным цветом, если это не так double:

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

В этом примере AttachedNumericValidationBehavior класс содержит присоединенное свойство AttachBehavior с static именем getter и setter, которое управляет добавлением или удалением поведения в элемент управления, к которому он будет присоединен. Это присоединенное свойство регистрирует метод OnAttachBehaviorChanged, который будет выполняться при изменении значения свойства. В зависимости от значения присоединенного свойства AttachBehavior этот метод регистрирует обработчик событий TextChanged или отменяет его регистрацию. Основные функциональные возможности поведения предоставляются методом OnEntryTextChanged , который анализирует значение, введенное в объекте Entry , и задает TextColor для свойства красный цвет, если значение не doubleявляется.

Использование присоединенного поведения

Присоединенное поведение можно использовать, задав присоединенное свойство в целевом элементе управления.

В следующем примере показано использование AttachedNumericValidationBehavior класса в объекте Entry путем добавления присоединенного AttachBehavior свойства в Entry:


<ContentPage ...
             xmlns:local="clr-namespace:BehaviorsDemos">
    <Entry Placeholder="Enter a System.Double" local:AttachedNumericValidationBehavior.AttachBehavior="true" />
</ContentPage>

В следующем примере кода показан эквивалентный элемент управления Entry на языке C#.

Entry entry = new Entry { Placeholder = "Enter a System.Double" };
AttachedNumericValidationBehavior.SetAttachBehavior(entry, true);

На следующем снимку экрана показана присоединенная реакция на недопустимые входные данные:

Screenshot of attached behavior responding to invalid input

Примечание.

Присоединенная реакция на событие создается для элементов управления определенного типа (или суперкласса, включающего множество разных элементов управления) и должна добавляться только к совместимым элементам управления.

Удаление присоединенного поведения

Класс AttachedNumericValidationBehavior можно удалить из элемента управления, задав присоединенное AttachBehavior свойство falseследующим:

<Entry Placeholder="Enter a System.Double" local:AttachedNumericValidationBehavior.AttachBehavior="false" />

Во время выполнения метод OnAttachBehaviorChanged выполняется, когда присоединенному свойству AttachBehavior присваивается значение false. Затем OnAttachBehaviorChanged метод отменит регистрацию обработчика событий для TextChanged события, гарантируя, что поведение не выполняется при взаимодействии с элементом управления.

Поведение .NET MAUI

Поведение .NET MAUI создается путем производных от Behavior класса или Behavior<T> класса.

Процесс создания поведения .NET MAUI выглядит следующим образом:

  1. Создайте класс, наследующий от класса Behavior или Behavior<T>, где T — это тип элемента управления, к которому должна применяться реакция на событие.
  2. Переопределите метод OnAttachedTo для выполнения требуемой настройки.
  3. Переопределите метод OnDetachingFrom для выполнения требуемой очистки.
  4. Реализуйте основные функциональные возможности реакции на событие.

Это приводит к созданию структуры, показанной в следующем примере:

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
}

Метод OnAttachedTo вызывается сразу после присоединения поведения к элементу управления. Этот метод получает ссылку на элемент управления, к которому выполнено присоединение, и может использоваться для регистрации обработчиков событий или выполнения других настроек, необходимых для поддержки функциональных возможностей реакции на событие. Например, можно подписаться на событие в элементе управления. В этом случае функциональные возможности реакции на событие будут реализованы в обработчике событий для этого события.

Метод OnDetachingFrom вызывается при удалении поведения из элемента управления. Этот метод получает ссылку на элемент управления, к которому выполнено присоединение, и используется для выполнения любой требуемой очистки. Например, вы можете отменить подписку на событие в элементе управления, чтобы предотвратить утечки памяти.

Затем поведение можно использовать, подключив его к Behaviors коллекции элемента управления.

Создание поведения .NET MAUI

Поведение .NET MAUI можно реализовать, создав класс, производный от Behavior класса или Behavior<T> класса, а также переопределяя OnAttachedTo методы и OnDetachingFrom методы.

В следующем примере показан NumericValidationBehavior класс, который выделяет значение, введенное пользователем в Entry элемент управления красным цветом, если это не так double:

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 ? Colors.Black : Colors.Red;
    }
}

В этом примере NumericValidationBehavior класс является производным от Behavior<T> класса, где T является .Entry Метод OnAttachedTo регистрирует обработчик событий для события TextChanged, при этом метод OnDetachingFrom отменяет регистрацию события TextChanged во избежание утечек памяти. Основные функциональные возможности поведения предоставляются методом OnEntryTextChanged , который анализирует значение, введенное в объекте Entry , и задает TextColor для свойства красный цвет, если значение не doubleявляется.

Важно!

.NET MAUI не задает BindingContext поведение, так как поведение можно совместно использовать и применять к нескольким элементам управления с помощью стилей.

Использование поведения .NET MAUI

Каждый элемент управления MAUI .NET содержит Behaviors коллекцию, в которую можно добавить одно или несколько действий:

<Entry Placeholder="Enter a System.Double">
    <Entry.Behaviors>
        <local:NumericValidationBehavior />
    </Entry.Behaviors>
</Entry>

В следующем примере кода показан эквивалентный элемент управления Entry на языке C#.

Entry entry = new Entry { Placeholder = "Enter a System.Double" };
entry.Behaviors.Add(new NumericValidationBehavior());

На следующем сниме экрана показано поведение .NET MAUI, реагируя на недопустимые входные данные:

Screenshot of .NET MAUI behavior responding to invalid input

Предупреждение

Поведение .NET MAUI записывается для определенного типа элемента управления (или суперкласса, который может применяться ко многим элементам управления), и их следует добавлять только в совместимый элемент управления. Попытка подключить поведение .NET MAUI к несовместимым элементу управления приведет к возникновению исключения.

Использование поведения .NET MAUI с стилем

Поведение .NET MAUI может использоваться явным или неявным стилем. Однако создание стиля, который задает свойство Behaviors элемента управления, не поддерживается, так как это свойство доступно только для чтения. Решение заключается в том, чтобы добавить присоединенное свойство в класс реакции на событие, управляющий добавлением и удалением реакции на событие. Применяется следующая обработка.

  1. Добавьте присоединенное свойство в класс поведения, который будет использоваться для управления добавлением или удалением поведения в элемент управления, к которому будет присоединено поведение. Это присоединенное свойство должно регистрировать делегат propertyChanged, который будет выполняться при изменении значения свойства.
  2. Создайте методы получения и задания static для присоединенного свойства.
  3. Реализуйте логику в делегате propertyChanged для добавления и удаления реакции на событие.

В следующем примере показан NumericValidationStyleBehavior класс, имеющий присоединенное свойство, которое управляет добавлением и удалением поведения:

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

    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 ? Colors.Black : Colors.Red;
    }
}

В этом примере NumericValidationStyleBehavior класс содержит присоединенное свойство AttachBehavior с static именем getter и setter, которое управляет добавлением или удалением поведения в элемент управления, к которому он будет присоединен. Это присоединенное свойство регистрирует метод OnAttachBehaviorChanged, который будет выполняться при изменении значения свойства. В зависимости от значения присоединенного свойства AttachBehavior, этот метод добавляет или удаляет реакцию на событие в элементе управления.

В следующем примере кода показан явный стиль для NumericValidationStyleBehavior, который использует присоединенное свойство AttachBehavior и может применяться к элементам управления Entry:

<Style x:Key="NumericValidationStyle" TargetType="Entry">
    <Style.Setters>
        <Setter Property="local:NumericValidationStyleBehavior.AttachBehavior" Value="true" />
    </Style.Setters>
</Style>

Его Style можно применить к объекту Entry , задав его Style свойство в стиле с помощью StaticResource расширения разметки:

<Entry Placeholder="Enter a System.Double" Style="{StaticResource NumericValidationStyle}">

Дополнительные сведения о стилях см. в статье Стили .

Примечание.

Хотя вы можете добавить привязываемые свойства для реакции на событие, которая задается или запрашивается в XAML, в этом случае нужно создать реакции на событие с состоянием, которое не должно совместно использоваться между элементами управления в Style в ResourceDictionary.

Удаление поведения .NET MAUI

Метод OnDetachingFrom вызывается при удалении поведения из элемента управления и используется для выполнения любой требуемой очистки, например отмены подписки из события, чтобы предотвратить утечку памяти. Однако поведение неявно удаляется из элементов управления, если коллекция элементов управления Behaviors не изменяется с помощью Remove метода или Clear метода:

Behavior toRemove = entry.Behaviors.FirstOrDefault(b => b is NumericValidationStyleBehavior);
if (toRemove != null)
{
    entry.Behaviors.Remove(toRemove);
}

Кроме того, можно очистить коллекцию элемента управления Behaviors :

entry.Behaviors.Clear();

Примечание.

Поведение .NET MAUI неявно удаляется из элементов управления при появлении страниц из стека навигации. Вместо этого их требуется явно удалить до того, как страницы окажутся вне области действия.

Поведение платформы

Поведение платформы создается путем производных от PlatformBehavior<TView> класса или PlatformBehavior<TView,TPlatformView> класса. Они реагируют на произвольные условия и события в собственном элементе управления.

Поведение платформы можно реализовать с помощью условной компиляции или частичных классов. Приведенный здесь подход заключается в использовании частичных классов, где поведение платформы обычно состоит из кроссплатформенного частичного класса, который определяет API поведения и собственный частичный класс, реализующий поведение на каждой платформе. Во время сборки мультинацеливание объединяет частичные классы для создания поведения платформы на каждой платформе.

Процесс создания поведения платформы выглядит следующим образом:

  1. Создайте кроссплатформенный частичный класс, определяющий API для поведения платформы.

  2. Создайте собственный частичный класс на каждой платформе, для которых создано приложение, которое имеет то же имя, что и кроссплатформенный частичный класс. Этот собственный частичный класс должен наследовать от PlatformBehavior<TView> класса или PlatformBehavior<TView,TPlatformView> класса, где TView является кроссплатформенный элемент управления, к которому должно применяться поведение, и TPlatformView является собственным представлением, реализующим кроссплатформенный элемент управления на определенной платформе.

    Примечание.

    Хотя для создания собственного частичного класса на каждой платформе, для каждой платформы создается приложение, не требуется реализовать функциональные возможности поведения платформы на каждой платформе. Например, можно создать поведение платформы, которое изменяет толщину границы собственного элемента управления на некоторых, но не всех платформах.

  3. В каждом собственном частичном классе, который требуется реализовать поведение платформы, необходимо:

    1. Переопределите метод для выполнения любой OnAttachedTo настройки.
    2. Переопределите метод для выполнения любой OnDetachedFrom очистки.
    3. Реализуйте основные функции поведения платформы.

Затем поведение можно использовать, подключив его к Behaviors коллекции элемента управления.

Создание поведения платформы

Чтобы создать поведение платформы, необходимо сначала создать кроссплатформенный частичный класс, который определяет API для поведения платформы:

namespace BehaviorsDemos
{
    public partial class TintColorBehavior
    {
        public static readonly BindableProperty TintColorProperty =
            BindableProperty.Create(nameof(TintColor), typeof(Color), typeof(TintColorBehavior));

        public Color TintColor
        {
            get => (Color)GetValue(TintColorProperty);
            set => SetValue(TintColorProperty, value);
        }
    }
}

Поведение платформы — это частичный класс, реализация которого будет завершена на каждой требуемой платформе с дополнительным частичным классом, использующим то же имя. В этом примере TintColorBehavior класс определяет одно привязываемое свойство, TintColorкоторое будет тонирует изображение с указанным цветом.

После создания кроссплатформенного частичного класса необходимо создать собственный частичный класс на каждой платформе, для которых вы создаете приложение. Это можно сделать, добавив частичные классы в необходимые дочерние папки папки Платформы :

Screenshot of the native partial classes for a platform behavior.

Кроме того, вы можете настроить проект для поддержки многонацеливания на основе файлов или многонацеливания на основе папок или обоих. Дополнительные сведения о многоцелевой настройке см. в разделе "Настройка нескольких целевых параметров".

Собственные частичные классы должны наследоваться от PlatformBehavior<TView> класса или PlatformBehavior<TView,TPlatformView> класса, где TView является кроссплатформенный элемент управления, к которому должно применяться поведение, и TPlatformView является собственным представлением, реализующим кроссплатформенный элемент управления на определенной платформе. В каждом собственном частичном классе, который требуется реализовать поведение платформы, необходимо переопределить OnAttachedTo метод и OnDetachedFrom метод, а также реализовать основные функциональные возможности поведения платформы.

Метод OnAttachedTo вызывается сразу после того, как поведение платформы присоединено к кроссплатформенным элементу управления. Метод получает ссылку на кроссплатформенный элемент управления, к которому он подключен, и при необходимости ссылку на собственный элемент управления, реализующий кроссплатформенный элемент управления. Этот метод можно использовать для регистрации обработчиков событий или выполнения других настроек, необходимых для поддержки функций поведения платформы. Например, можно подписаться на событие в элементе управления. В этом случае функциональные возможности реакции на событие будут реализованы в обработчике событий для этого события.

Метод OnDetachedFrom вызывается при удалении поведения из кроссплатформенного элемента управления. Метод получает ссылку на элемент управления, к которому он подключен, и при необходимости ссылку на собственный элемент управления, реализующий кроссплатформенный элемент управления. Метод должен использоваться для выполнения любой требуемой очистки. Например, вы можете отменить подписку на событие в элементе управления, чтобы предотвратить утечки памяти.

Важно!

Частичные классы должны находиться в одном пространстве имен и использовать идентичные имена.

В следующем примере показан частичный TintColorBehavior класс для Android, который тонирует изображение с указанным цветом:

using Android.Graphics;
using Android.Widget;
using Microsoft.Maui.Platform;
using Color = Microsoft.Maui.Graphics.Color;

namespace BehaviorsDemos
{
    public partial class TintColorBehavior : PlatformBehavior<Image, ImageView>
    {
        protected override void OnAttachedTo(Image bindable, ImageView platformView)
        {
            base.OnAttachedTo(bindable, platformView);

            if (bindable is null)
                return;
            if (TintColor is null)
                ClearColor(platformView);
            else
                ApplyColor(platformView, TintColor);
        }

        protected override void OnDetachedFrom(Image bindable, ImageView platformView)
        {
            base.OnDetachedFrom(bindable, platformView);

            if (bindable is null)
                return;
            ClearColor(platformView);
        }

        void ApplyColor(ImageView imageView, Color color)
        {
            imageView.SetColorFilter(new PorterDuffColorFilter(color.ToPlatform(), PorterDuff.Mode.SrcIn ?? throw new NullReferenceException()));
        }

        void ClearColor(ImageView imageView)
        {
            imageView.ClearColorFilter();
        }
    }
}

В этом примере TintColorBehavior класс является производным от PlatformBehavior<TView,TPlatformView> класса, где TView является Image и TPlatformView является .ImageView Цвет OnAttachedTo оттенка применяется к изображению, при условии, что TintColor свойство имеет значение. Метод OnDetachedFrom удаляет цвет оттенка из изображения.

Собственный частичный класс необходимо добавить на каждой платформе, для которых вы создаете приложение. Однако вы можете сделать собственный частичный класс NO-OP, если поведение платформы не требуется для определенной платформы. Это можно сделать, предоставив пустой класс:

using Microsoft.UI.Xaml;

namespace BehaviorsDemos
{
    public partial class TintColorBehavior : PlatformBehavior<Image, FrameworkElement>
    {
        // NO-OP on Windows
    }
}

Важно!

.NET MAUI не задает BindingContext поведение платформы.

Использование поведения платформы

Каждый элемент управления MAUI .NET содержит Behaviors коллекцию, в которую можно добавить одно или несколько действий платформы:

<Image Source="dotnet_bot.png"
       HeightRequest="200"
       HorizontalOptions="Center">
    <Image.Behaviors>
        <local:TintColorBehavior TintColor="Red" />
    </Image.Behaviors>
</Image>

Эквивалент Image в C# показан в следующем примере:

Image image = new Image { Source = "dotnet_bot.png", HeightRequest = 200, HorizontalOptions = LayoutOptions.Center };
image.Behaviors.Add(new TintColorBehavior());

На следующем снимка экрана показана схема поведения платформы:

Screenshot of .NET MAUI platform behavior tinting an image.

Предупреждение

Поведение платформы записывается для определенного типа элемента управления (или суперкласса, который может применяться ко многим элементам управления), и их следует добавлять только в совместимый элемент управления. Попытка подключить поведение платформы к несовместимым элементу управления приведет к возникновению исключения.