可绑定属性

.NET Multi-platform App UI (.NET MAUI) 可绑定属性通过支持具有 BindableProperty 类型的属性而非字段来扩展公共语言运行时 (CLR) 属性功能。 可绑定属性的目的是提供一个属性系统,它支持通过父子关系设置的数据绑定、样式、模板和值。 此外,可绑定属性可以提供默认值、属性值验证以及监控属性更改的回调。

在 .NET MAUI 应用中,属性应实现为可绑定属性,以支持以下一个或多个功能:

  • 充当数据绑定的有效目标属性。 有关目标属性的详细信息,请参阅基本绑定
  • 通过样式设置属性。
  • 提供与属性类型的默认值不同的默认属性值。
  • 验证属性的值。
  • 监控属性更改。

.NET MAUI 可绑定属性的示例包括 Label.TextButton.BorderRadiusStackLayout.Orientation。 每个可绑定属性都有一个在相同类上公开的相应 public static readonly 类型的 BindableProperty 字段,该字段是可绑定属性的标识符。 例如,Label.Text 属性的相应可绑定属性标识符为 Label.TextProperty

创建可绑定属性

创建可绑定属性的过程如下所示:

  1. 使用其中一个 BindableProperty.Create 方法重载创建 BindableProperty 实例。
  2. 定义 BindableProperty 实例的属性访问器。

必须在 UI 线程上创建所有 BindableProperty 实例。 这意味着,只有 UI 线程上运行的代码才能获取或设置可绑定属性的值。 但是,通过封送到 UI 线程,可以从其他线程访问 BindableProperty 实例。 有关详细信息,请参阅在 UI 线程上运行代码

创建属性

若要创建 BindableProperty 实例,包含类必须派生自 BindableObject 类。 但是,BindableObject 类在类层次结构中很高,因此用于 UI 功能的大多数类支持可绑定属性。

可以通过声明 BindableProperty 类型的 public static readonly 属性来创建可绑定属性。 可绑定属性应设置为 BindableProperty.Create 方法重载之一的返回值。 声明应位于 BindableObject 派生类的正文中,但不属于任何成员定义。

创建 BindableProperty 时,必须至少指定一个标识符以及以下参数:

  • BindableProperty 的名称。
  • 属性类型。
  • 所属对象的类型。
  • 属性的默认值。 这可以确保属性在未设置时始终返回特定的默认值,并且它可以与该属性类型的默认值不同。 在可绑定属性上调用 ClearValue 方法时,将还原默认值。

重要

可绑定属性的命名约定是,可绑定属性标识符必须与 Create 方法中指定的属性名称匹配,并将“属性”追加到该方法中。

以下代码展示了可绑定属性的示例,其中包含四个必需参数的标识符和值:

public static readonly BindableProperty IsExpandedProperty =
  BindableProperty.Create ("IsExpanded", typeof(bool), typeof(Expander), false);

这将创建名为 IsExpandedPropertyBindableProperty 实例,类型为 bool。 此属性归 Expander 类所有,其默认值为 false

注意

Expander 是 .NET MAUI 社区工具包中的一个控件。 有关详细信息,请参阅扩展器

创建 BindableProperty 实例时,也可以选择指定以下参数:

  • 绑定模式。 这用于指定属性值更改的传播方向。 在默认绑定模式下,更改将从源传播到目标。 有关详细信息,请参阅基本绑定
  • 设置属性值时将调用的验证委托。 有关详细信息,请参阅验证回调
  • 属性值发生更改时将调用的属性已更改委托。 有关详细信息,请参阅检测属性更改
  • 属性值即将更改时将调用的属性即将更改委托。 此委托与属性更改的委托具有相同的签名。
  • 属性值发生更改时将调用的强制值委托。 有关详细信息,请参阅强制值回调
  • 用于初始化默认属性值的 Func。 有关详细信息,请参阅使用 Func 创建默认值

创建访问器

属性访问器需要使用属性语法才能访问可绑定属性。 Get 访问器应返回相应可绑定属性中包含的值。 可以通过调用 GetValue 方法,传入要获取值的可绑定属性标识符,然后将结果强制转换为所需的类型来达成此目的。 Set 访问器应设置相应可绑定属性的值。 可以通过调用 SetValue 方法、传入要设置值的可绑定属性标识符和要设置的值达成此目的。

以下代码示例演示 IsExpanded 可绑定属性的访问器:

public bool IsExpanded
{
    get => (bool)GetValue(IsExpandedProperty);
    set => SetValue(IsExpandedProperty, value);
}

使用可绑定属性

创建可绑定属性后,可以从 XAML 或代码使用它。 在 XAML 中,可通过声明带有前缀的命名空间、指示 CLR 命名空间名称的命名空间声明,以及(可选)程序集名称来达成此目的。 有关详细信息,请参阅 XAML 命名空间

以下代码示例演示包含可绑定属性的自定义类型的 XAML 命名空间,该属性在与引用自定义类型的应用程序代码相同的程序集中定义:

<ContentPage ... xmlns:local="clr-namespace:DataBindingDemos" ...>
  ...
</ContentPage>

设置 IsExpanded 可绑定属性时将使用命名空间声明,如以下 XAML 代码示例所示:

<Expander IsExpanded="true">
    ...
</Expander>

以下代码示例显示相应的 C# 代码:

Expander expander = new Expander
{
    IsExpanded = true
};

高级方案

创建 BindableProperty 实例时,可以设置许多可选参数来启用高级可绑定属性应用场景。 本部分将探讨这些应用场景。

检测属性更改

可通过为 BindableProperty.Create 方法指定 propertyChanged 参数,向可绑定属性注册 static 属性更改的回调方法。 当可绑定属性的值发生更改时,将调用指定的回调方法。

以下代码示例演示 IsExpanded 可绑定属性如何将 OnIsExpandedChanged 方法注册为属性更改的回调方法:

public static readonly BindableProperty IsExpandedProperty =
    BindableProperty.Create(nameof(IsExpanded), typeof(bool), typeof(Expander), false, propertyChanged: OnIsExpandedChanged);
...

static void OnIsExpandedChanged (BindableObject bindable, object oldValue, object newValue)
{
  // Property changed implementation goes here
}

在属性更改的回调方法中,BindableObject 参数用于表示所属类的哪个实例报告了更改,两个 object 参数的值分别表示可绑定属性的新旧值。

验证回调

可通过为 BindableProperty.Create 方法指定 validateValue 参数,通过可绑定属性注册 static 验证回调方法。 设置可绑定属性的值时,将调用指定的回调方法。

以下代码示例演示 Angle 可绑定属性如何将 IsValidValue 方法注册为验证回调方法:

public static readonly BindableProperty AngleProperty =
    BindableProperty.Create("Angle", typeof(double), typeof(MainPage), 0.0, validateValue: IsValidValue);
...

static bool IsValidValue(BindableObject view, object value)
{
    double result;
    double.TryParse(value.ToString(), out result);
    return (result >= 0 && result <= 360);
}

验证回调随值一起提供,如果该值对属性有效,则应返回 true,否则返回 false。 如果验证回调返回 false,则会引发异常,应该处理该异常。 验证回调方法的典型用法是在设置可绑定属性时约束整数值或双精度值。 例如,IsValidValue 方法将检查属性值是否为介于 0 到 360 范围之间的 double

强制值回调

可通过为 BindableProperty.Create 方法指定 coerceValue 参数,向可绑定属性注册 static 强制值回调方法。 当可绑定属性的值即将更改时,将调用指定的回调方法,以便在应用新值之前对其进行调整。

重要

除了由可绑定属性引擎触发之外,还可以从代码中调用强制值回调。 BindableObject 类型包含 CoerceValue 方法,可以通过调用其强制值回调来强制重新计算其 BindableProperty 参数的值。

强制值回调用于在属性值即将更改时强制重新计算可绑定属性。 例如,可使用强制值回调确保一个可绑定属性的值不大于另一个可绑定属性的值。

以下代码示例演示 Angle 可绑定属性如何将 CoerceAngle 方法注册为强制值回调方法:

public static readonly BindableProperty AngleProperty =
    BindableProperty.Create("Angle", typeof(double), typeof(MainPage), 0.0, coerceValue: CoerceAngle);
public static readonly BindableProperty MaximumAngleProperty =
    BindableProperty.Create("MaximumAngle", typeof(double), typeof(MainPage), 360.0, propertyChanged: ForceCoerceValue);
...

static object CoerceAngle(BindableObject bindable, object value)
{
    MainPage page = bindable as MainPage;
    double input = (double)value;

    if (input > page.MaximumAngle)
    {
        input = page.MaximumAngle;
    }

    return input;
}

static void ForceCoerceValue(BindableObject bindable, object oldValue, object newValue)
{
    bindable.CoerceValue(AngleProperty);
}

CoerceAngle 方法检查 MaximumAngle 属性的值,并且如果 Angle 属性值大于该值,则将该值强制转换为 MaximumAngle 属性值。 此外,当 MaximumAngle 属性更改时,通过调用 CoerceValue 方法,对 Angle 属性调用强制值回调。

使用 Func 创建默认值

Func 可用于初始化可绑定属性的默认值,如以下示例所示:

public static readonly BindableProperty DateProperty =
    BindableProperty.Create ("Date", typeof(DateTime), typeof(MyPage), default(DateTime), BindingMode.TwoWay, defaultValueCreator: bindable => DateTime.Today);

defaultValueCreator 参数设置为 Func,后者返回表示当前日期的 DateTime