行为

Browse sample.浏览示例

.NET Multi-platform App UI (.NET MAUI) 行为允许向用户界面控件添加功能,而无需将其子类化。 功能是在行为类中实现的,并附加到控件上,就像它本身就是控件的一部分。

行为使开发人员可以实现那些通常必须以代码隐藏形式编写的代码,因为它直接与控件的 API 进行交互,这样便可简洁地将其附加到控件,并打包以便跨多个应用程序重用。 它们可以用于为控件提供一系列完整的功能,例如:

  • Entry 添加电子邮件验证程序。
  • 使用点击手势识别器创建评分控件。
  • 控制动画。

.NET MAUI 支持三种不同类型的行为:

附加行为

“附加行为是具有一个或多个附加属性的静态类。 附加属性是特殊的可绑定属性。 附加属性在一个类中定义,但可附加到其他对象,并且在 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 类包含带有 static getter 和 setter 且名为 AttachBehavior 的附加属性,该属性控制向将被附加行为的控件添加或删除行为。 该附加属性注册属性值更改时执行的 OnAttachBehaviorChanged 方法。 此方法根据 AttachBehavior 附加属性的值为 TextChanged 事件注册或注销事件处理程序。 该行为的核心功能由 OnEntryTextChanged 方法提供,该方法解析在 Entry 中输入的值,如果该值不是 double,则将 TextColor 属性设置为红色。

使用附加行为

在目标控件上设置附加属性即可使用附加行为。

以下示例演示如何通过将 AttachBehavior 附加属性添加到 Entry 以在 Entry 上使用 AttachedNumericValidationBehavior 类:


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

下面的代码示例介绍了 C# 中的等效 Entry

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

以下屏幕截图显示了响应无效输入的附加行为:

Screenshot of attached behavior responding to invalid input

注意

附加行为是为特定的控件类型(或者可以应用于许多控件的超类)编写的,它们只应添加到可兼容的控件中。

移除附加行为

AttachBehavior 附加属性设置为 false 即可从控件中移除 AttachedNumericValidationBehavior 类:

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

在运行时,如果 AttachBehavior 附加属性的值设置为 false 时,则将执行 OnAttachBehaviorChanged 方法。 然后,OnAttachBehaviorChanged 方法将注销 TextChanged 事件的事件处理程序,以确保与控件交互时不执行该行为。

.NET MAUI 行为

.NET MAUI 行为是通过派生自 BehaviorBehavior<T> 类创建的。

创建 .NET MAUI 行为的过程如下:

  1. 创建一个继承自 BehaviorBehavior<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 行为

创建派生自 BehaviorBehavior<T> 类的类并重写 OnAttachedToOnDetachingFrom 方法即可实现 .NET MAUI 行为。

以下示例显示 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> 类,其中 TEntryOnAttachedTo 方法注册 TextChanged 事件的事件处理程序,并使用 OnDetachingFrom 方法注销 TextChanged 事件以防止内存泄漏。 该行为的核心功能由 OnEntryTextChanged 方法提供,该方法解析在 Entry 中输入的值,如果该值不是 double,则将 TextColor 属性设置为红色。

重要

.NET MAUI 不会设置行为的 BindingContext,因为可以通过样式共享行为并将其应用于多个控件。

使用 .NET MAUI 行为

每个 .NET MAUI 控件都有一个 Behaviors 集合,可以向其中添加一个或多个行为:

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

下面的代码示例介绍了 C# 中的等效 Entry

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 getter 和 setter。
  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 类包含带有 static getter 和 setter 且名为 AttachBehavior 的附加属性,该属性控制向将被附加行为的控件添加或移除行为。 该附加属性注册属性值更改时执行的 OnAttachBehaviorChanged 方法。 该方法根据 AttachBehavior 附加属性的值向控件添加或移除行为。

下面的代码示例显示使用 AttachBehavior 附加属性的 NumericValidationStyleBehavior 的显式样式,该样式可应用于 Entry 控件

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

可以通过使用 StaticResource 标记扩展将 Style 属性设置为样式,将 Style 应用到 Entry

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

有关样式的详细信息,请参阅 样式

注意

虽然可以向 XAML 中设置或查询的行为添加可绑定属性,但如果你确实创建了有状态的行为,则这些行为不应该在 ResourceDictionaryStyle 中的控件之间共享。

移除 .NET MAUI 行为

从控件中移除行为时将调用 OnDetachingFrom 方法,用于执行任何所需清理(例如取消订阅事件以防止内存泄漏)。 但是,除非 RemoveClear 方法修改控件的 Behaviors 集合,否则不会从控件中隐式移除行为:

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,将使用指定颜色对图像着色。

创建跨平台分部类后,应在应用适配的每个平台上创建本机分部类。 可以通过将分部类添加到 Platforms 文件夹的必需子文件夹来达成此目的:

Screenshot of the native partial classes for a platform behavior.

或者,可以将项目配置为支持基于文件名的多目标或基于文件夹的多目标,或者同时支持这两者。 有关多目标的详细信息,请参阅配置多目标

本机分部类应继承自 PlatformBehavior<TView> 类或 PlatformBehavior<TView,TPlatformView> 类,其中 TView 是行为所应该应用到的跨平台控件,TPlatformView 是在特定平台上实现跨平台控件的本机视图。 在实现平台行为所需的每个本机分部类中,应重写 OnAttachedTo 方法和 OnDetachedFrom 方法,并实现平台行为的核心功能。

将平台行为附加到跨平台控件后将立即调用 OnAttachedTo 方法。 该方法接收对其附加的跨平台控件的引用,并可接收对实现跨平台控件的本机控件的引用(可选)。 该方法可用于注册事件处理程序或执行支持平台行为功能所需的其他设置。 例如,你可以订阅控件上的事件。 然后,行为功能将在事件的事件处理程序中实现。

当行为从跨平台控件中移除时,将调用 OnDetachedFrom 方法。 该方法接收对其附加的控件的引用,并可接收对实现跨平台控件的本机控件的引用(可选)。 该方法应当用于执行任何所需的清理。 例如,可以取消订阅控件上的事件,以防止内存泄漏。

重要

分部类必须位于同一命名空间中并使用相同的名称。

以下示例显示适用于 Android 的 TintColorBehavior 分部类,该分部类使用指定颜色对图像着色:

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> 类,其中 TViewImageTPlatformViewImageView。 如果 TintColor 属性具有值,则 OnAttachedTo 将对图像着色。 OnDetachedFrom 方法从图像中移除着色。

必须在应用所适配的每个平台上添加本机分部类。 但是,如果特定平台上不需要平台行为,则可以使本机分部类 NO-OP。 这可以通过提供空类来实现:

using Microsoft.UI.Xaml;

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

重要

.NET MAUI 不会设置平台行为的 BindingContext

使用平台行为

每个 .NET MAUI 控件都有一个 Behaviors 集合,可以向其添加一个或多个平台行为:

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

下列示例介绍了 C# 中的等效 Image

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.

警告

平台行为是为特定的控件类型(或者可以应用于许多控件的超类)编写的,只应将它们添加到兼容的控件中。 尝试将行为附加到不兼容控件会导致触发异常。