MAUI新生3.1-深入理解XAML:可绑定属性BindableProperty

可绑定属性BindableProperty是MAUI框架的基石之一,一方面它是UI控件的数据载体(控件模板和数据模板是UI的外观载体),另一方面提供了数据绑定的通道接口。可绑定属性相对于一般的对象属性,提供了XAML特有的功能,如:①可以作为数据绑定的目标或源、②可以通过Style设置、③可以设置默认值、④可以验证属性值、⑤可以监视属性更改。大多数时候,很少会接触它,但如果要自定义控件、自定义行为等更深入功能的使用,就必须掌握可绑定属性和附加属性。可绑定属性的定义和使用,其实不难,比较难的是底层原理,当然,对于底层原理,了解即可。

 

 

一、创建和使用可绑定属性的简单案例

1、第一步:可绑定对象和可绑定属性的定义

//MAUI内置的控件,均是可绑定对象,属性均是可绑定属性。View是MAUI比较顶层的控件,Expander派生自View,继承了可绑定对象的特性及功能。
public class Expander:View
{
    //定义可绑定属性IsExpandedProperty
    //属性必须是公共、静态、只读的
    //通过BindableProperty.Create方法创建BindableProperty对象
    public static readonly BindableProperty IsExpandedProperty =
        BindableProperty.Create(nameof(IsExpanded),typeof(bool),typeof(Expander),false);

    //定义可绑定属性的访问属性(包装属性),通过GetValue方法读取属性值,通过SetValue方法设置属性值
    //访问属性是暴露给外部的访问端口,外部通过IsExpanded访问属性,而不是IsExpandedProperty
    public bool IsExpanded
    {
        get => (bool)GetValue(IsExpandedProperty);
        set => SetValue(IsExpandedProperty,value);
    }
}

 

2、第二步:在XAML中使用可绑定对象,并给可绑定属性赋值(注:本案例未设置控件的外观,所以在UI层并不能看到控件)

<ContentPage
    x:Class="MauiApp10.MainPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:control="clr-namespace:MauiApp10.Controls">

    <VerticalStackLayout>
        <control:Expander IsExpanded="True"/>
    </VerticalStackLayout>

</ContentPage>

 

3、BindableProperty.Create方法参数的进一步说明

1)必填参数(4个)

①CLR属性名:即访问属性的名称,可以使用字符串格式,也可以使用nameof(),如【nameof(IsExpanded)】 或者【“IsExpanded”】。

②属性的类型:即可绑定属性的类型,使用typeof()定义,如【typeof(bool)】

③宿主类的类型:即可绑定属性所在类的类型,使用typeof()定义,如【typeof(Expander)】

④属性的默认值:如【true】等

2)选填参数(5个)

①默认绑定模式:设置默认的数据绑定模式,值为BindingMode.OneWay、BindingMode.TwoWay等。

②属性监视回调函数:可以在属性更改时调用一个回调函数,回调函数的实参为宿主类对象、属性旧值和属性新值。如下所示: 

public static readonly BindableProperty IsExpandedProperty =
    BindableProperty.Create(..., propertyChanged: OnIsExpandedChanged);

static void OnIsExpandedChanged (BindableObject bindable, object oldValue, object newValue)
{
  //属性值发生改变时,执行以下逻辑
  //参数为宿主类对象、旧值、新值
}

 

③属性验证回调函数:可以在属性赋值时调用一个回调函数,对属性值进行验证,并返回true或false。如果返回false,将发出异常。如下所示: 

public static readonly BindableProperty AngleProperty =
    BindableProperty.Create(..., validateValue: IsValidValue);

static bool IsValidValue(BindableObject view, object value)
{
    //如果属性值介于0-360之间,返回true,属性值更改成功
    //如果属性值大于360或小于0,返回false,发出异常
    double result;
    double.TryParse(value.ToString(), out result);
    return (result >= 0 && result <= 360);
}

 

 

④属性强制值回调函数:可以在属性赋值时调用一个回调函数,对属性值进行判断,并根据判断结果返回给定值给属性。如下所示: 

public static readonly BindableProperty AngleProperty =
    BindableProperty.Create(..., coerceValue: CoerceAngle);

static object CoerceAngle(BindableObject bindable, object value)
{
    //如果属性值大于页面的MaximumAngle,则返回MaximumAngle,否则返回输入值
    MainPage page = bindable as MainPage;
    double input = (double)value;
    if (input > page.MaximumAngle)
    {
        input = page.MaximumAngle;
    }
    return input;
}

 

 

⑤使用委托设置默认值:普通的默认值只能设置一个具体数值,而使用委托设置默认值,可以进行复杂的逻辑计算。如下所示: 

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

 

 

 

二、可绑定属性的原理说明(以下原理说明参考WPF的依赖属性,不一定对,但大致相同。有能力的时候,再看源码)

1、可绑定属性的值保存在哪里?

  • IsExpandedProperty是一个静态字段,静态成员属于类,这个类创建的所有实例对象,都共享这个字段值,所以值不可能保存在IsExpandedProperty上。IsExpanded属性的get和set,分别调用了GetValue和SetValue方法,没有back字段,所以值也不在它身上。
  • MAUI应用运行时,会创建和维护一个全局的HashTable。BindableProperty.Create方法,返回BindableProperty对象,并用CLR属性名和宿主类名生成HashCode(全局唯一),最后将HashCode和BindableProperty对象,以键值对的方式,保存到全局的HashTable中。为保证BindableProtperty的稳定性,使用readonly来描述。
  • 每个BindableProperty指向或拥有一个数组,可以理解为一排可以随时扩建的小房间,而我们调用GetValue或SetValue时,就是从小房间里存取值,而小房间的索引编号,指向具体的对象,以保证实例对象的属性唯一。GetValue和SetValue检索的过程,先检查HashCode找到BindableProperty对象,再检索小房间编号,找到数据存取的地方。

 

2、为什么需要可绑定属性?

  • 我们知道,类的属性并不保存数据,真正保存数据的是背后的字段。如果我们像普通类一样,为控件的所有属性都定义一个字段,有些控件有一两百个属性,每创建一个对象,将会消耗大量内存,但实际上其中的大多数属性并未用到。而使用可绑定属性,只是提供了一个可以检索到的、可以随时扩建的小房间,需要的时候,才会扩充这些小房间,可以大大的节省内存。
  • 由于控件对象的属性值统一保存在一张HashTable中,检索速度快,可以带来存取性能的提升。同时,如果某个可绑定属性的数据源来源于其它属性,则这个可绑定属性都不需要保存具体数据,仅要保存一个数据源的地址即可,可以进一步的节约内存。

 

3、图例说明(大概的意思,不一定对)

 

posted @ 2022-12-03 21:45  functionMC  阅读(697)  评论(0编辑  收藏  举报