第十一章:可绑定的基础结构(三)

简介:

定义可绑定属性

假设您想要一个增强的Label类,它允许您以点为单位指定字体大小。 让我们将这个类AltLabel称为“替代标签”。它派生自Label并包含一个名为PointSize的新属性。
PointSize应该由可绑定属性支持吗? 当然! (虽然这样做的真正好处直到下一章才会得到证明。)
仅代码的AltLabel类包含在Xamarin.FormsBook.Toolkit库中,因此可以访问多个应用程序。 新的PointSize属性是使用名为PointSizeProperty的BindableProp?erty对象和引用PointSizeProperty的名为PointSize的CLR属性实现的:

public class AltLabel : Label
{
    public static readonly BindableProperty PointSizeProperty … ;
 
    public double PointSize
    {
        set { SetValue(PointSizeProperty, value); }
        get { return (double)GetValue(PointSizeProperty); }
    }
 
}

字段和属性定义都必须是公共的。
因为PointSizeProperty被定义为static和readonly,所以必须在静态构造函数中或在字段定义中将其赋值,之后才能更改它。 通常,通过使用静态BindableProperty.Create方法在字段定义中指定BindableProperty对象。 需要四个参数(此处显示参数名称):

  • propertyName属性的文本名称(在本例中为“PointSize”)
  • returnType属性的类型(本例中为double)
  • declaringType定义属性的类的类型(AltLabel)
  • defaultValue默认值(比方说8分)

第二个和第三个参数通常使用typeof表达式定义。 这是赋值语句,将这四个参数传递给BindableProperty.Create:

public class AltLabel : Label
{
    public static readonly BindableProperty PointSizeProperty =
        BindableProperty.Create("PointSize", // propertyName
                                typeof(double), // returnType
                                typeof(AltLabel), // declaringType
                                8.0, // defaultValue
                                );
 
}

请注意,默认值指定为8.0而不是8.因为BindableProp?erty.Create旨在处理任何类型的属性,所以defaultValue参数定义为object。 当C#编译器只遇到8作为该参数时,它将假定8是一个int并将int传递给该方法。 直到运行时才会显示该问题,但是,当BindableProperty.Create方法期望默认值为double类型并通过引发TypeInitializationException进行响应时。
您必须明确指定要指定为默认值的值的类型。 不这样做是定义可绑定属性的一个非常常见的错误。 一个非常常见的错误。
BindableProperty.Create还有六个可选参数。 这里他们是参数名称及其目的:

  • defaultBindingMode与数据绑定结合使用
  • validifyValue用于检查有效值的回调
  • propertyChanged一个回调,指示属性何时发生更改
  • propertyChanging一个回调,指示属性何时将要更改
  • coerceValue将设置值强制转换为另一个值的回调(例如,将值限制为某个范围)
  • defaultValueCreator用于创建默认值的回调。 这通常用于实例化一个不能在类的所有实例之间共享的默认对象; 例如,一个集合对象,如List或Dictionary。

不要在CLR属性中执行任何验证,强制或属性更改处理。 CLR属性应限制为SetValue和GetValue调用。 其他所有内容都应该在可绑定属性基础结构提供的回调中完成。
对BindableProperty.Create的特定调用很少需要所有这些可选参数。 因此,这些可选参数通常用C#4.0中引入的命名争论功能表示。 要指定特定的可选参数,请使用参数名称后跟冒号。 例如:

public class AltLabel : Label
{
    public static readonly BindableProperty PointSizeProperty =
        BindableProperty.Create("PointSize", // propertyName
                                typeof(double), // returnType
                                typeof(AltLabel), // declaringType
                                8.0, // defaultValue
                                propertyChanged: OnPointSizeChanged);

}

毫无疑问,propertyChanged是可选参数中最重要的参数,因为当属性发生更改时,类会使用此回调通知,直接来自对Set?Value的调用或通过CLR属性。
在此示例中,属性更改的处理程序称为OnPointSizeChanged。 只有在属性真正发生变化时才会调用它,而不是仅在它被设置为相同值时调用。 但是,因为OnPointSizeChanged是从静态字段引用的,所以方法本身也必须是静态的。 这是它的样子:

public class AltLabel : Label
{
 
    static void OnPointSizeChanged(BindableObject bindable, object oldValue, object newValue)
    {

    }
 
}

这看起来有点奇怪。 我们可能在程序中有多个AltLabel实例,但只要PointSize属性在这些实例中的任何一个中发生更改,就会调用相同的静态方法。 该方法如何确切地知道哪个AltLabel实例已更改?
该方法可以判断哪个实例的属性已更改,因为该实例始终是属性更改处理程序的第一个参数。 虽然第一个参数被定义为BindableObject,但在这种情况下,它实际上是AltLabel类型,并指示哪个AltLabel实例的属性已更改。 这意味着您可以安全地将第一个参数强制转换为AltLabel实例:

static void OnPointSizeChanged(BindableObject bindable, object oldValue, object newValue)
{
    AltLabel altLabel = (AltLabel)bindable;
 
}

然后,您可以在属性已更改的AltLabel的特定实例中引用任何内容。 对于此示例,第二个和第三个参数实际上是double类型,并指示先前的值和新值。
通常,这个静态方法通过将参数转换为实际类型来调用实例方法通常很方便:

public class AltLabel : Label
{
 
    static void OnPointSizeChanged(BindableObject bindable, object oldValue, object newValue)
    {
        ((AltLabel)bindable).OnPointSizeChanged((double)oldValue, (double)newValue);
    }
    void OnPointSizeChanged(double oldValue, double newValue)
    {
 
    }
}

然后,实例方法可以像通常那样使用底层基类的任何实例属性或方法。
对于此类,此OnPointSizeChanged方法需要根据新的磅值和转换因子设置FontSize属性。 此外,构造函数需要根据默认的PointSize值初始化FontSize属性。 这是通过一个简单的SetLabelFontSize方法完成的。 这是最后的完整课程:

public class AltLabel : Label
{
    public static readonly BindableProperty PointSizeProperty =
        BindableProperty.Create("PointSize", // propertyName
                                typeof(double), // returnType
                                typeof(AltLabel), // declaringType
                                8.0, // defaultValue
                                propertyChanged: OnPointSizeChanged);
    public AltLabel()
    {
        SetLabelFontSize((double)PointSizeProperty.DefaultValue);
    }
    public double PointSize
    {
        set { SetValue(PointSizeProperty, value); }
        get { return (double)GetValue(PointSizeProperty); }
    }
    static void OnPointSizeChanged(BindableObject bindable, object oldValue, object newValue)
    {
        ((AltLabel)bindable).OnPointSizeChanged((double)oldValue, (double)newValue);
    }
    void OnPointSizeChanged(double oldValue, double newValue)
    {
        SetLabelFontSize(newValue);
    }
    void SetLabelFontSize(double pointSize)    
    {
        FontSize = 160 * pointSize / 72;
    }
}

实例OnPointSizeChanged属性也可以直接访问PointSize属性,而不是使用newValue。 在调用属性更改的处理程序时,基础属性值已经更改。 但是,您没有直接访问该基础值,就像私有字段支持CLR属性时一样。 该基础值是BindableObject的私有值,只能通过GetValue调用访问。
当然,没有什么能阻止使用AltLabel设置FontSize属性并覆盖PointSize设置的代码,但是我们希望这些代码能够意识到这一点。 这里有一些代码 - 一个名为PointSizedText的程序,它使用AltLabel显示4到12之间的点大小:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit=
                  "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="PointSizedText.PointSizedTextPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="5, 20, 0, 0"
                    Android="5, 0, 0, 0"
                    WinPhone="5, 0, 0, 0" />
    </ContentPage.Padding>
    <StackLayout x:Name="stackLayout">
        <toolkit:AltLabel Text="Text of 4 points" PointSize="4" />
        <toolkit:AltLabel Text="Text of 5 points" PointSize="5" />
        <toolkit:AltLabel Text="Text of 6 points" PointSize="6" />
        <toolkit:AltLabel Text="Text of 7 points" PointSize="7" />
        <toolkit:AltLabel Text="Text of 8 points" PointSize="8" />
        <toolkit:AltLabel Text="Text of 9 points" PointSize="9" />
        <toolkit:AltLabel Text="Text of 10 points" PointSize="10" />
        <toolkit:AltLabel Text="Text of 11 points" PointSize="11" />
        <toolkit:AltLabel Text="Text of 12 points" PointSize="12" />
    </StackLayout>
</ContentPage>

以下是截图:
201808032214310361

目录
相关文章
|
数据库
主题域、概念、逻辑、物理四种模型有什么区别与联系?
主题域、概念、逻辑、物理四种模型有什么区别与联系?
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
大模型的特点、重要概念及工作方式详解
大模型是具有大量参数和复杂结构的深度学习模型,通过处理大量数据实现高效任务解决。其特点包括参数规模庞大、深层网络结构、预训练与微调、多任务学习和自适应能力。重要概念有注意力机制、Transformer架构、迁移学习和分布式训练。大模型的工作方式包括输入处理、特征提取、预测与损失计算、反向传播与优化,以及评估与微调。这些特性使其在自然语言处理、计算机视觉等领域取得显著进展。
297 0
|
6月前
软件的质量特性及其子特性快速记忆表
软件的质量特性及其子特性快速记忆表
81 0
程序三大结构-系统学习一
编程从三大结构考虑问题,这样的思考方式、学习方式也有了更加深刻的认识与理解
|
存储 XML 编译器
【C#基础】C# 程序通用结构
编程语言C# 程序结构的介绍 。
240 0
【C#基础】C# 程序通用结构
关于图和实例的学习之相关概念个人理解
关于图和实例的学习之相关概念个人理解
124 0
关于图和实例的学习之相关概念个人理解
|
存储 C++
面向对象实验 ——(三)数据的保护与共享
面向对象实验 ——(三)数据的保护与共享
105 0
面向对象实验 ——(三)数据的保护与共享
|
编译器 C++
C++把类的设计看成类型设计
C++把类的设计看成类型设计
113 0
【自然框架】内部类库、控件的引用关系(最新整理,基本稳定)
  和以前相比,减少了一个项目,把Control_Interface合并到CommonFunction里面。这样引用关系就简单多了。   基本上分为三个层次:类库、自定义控件、页面基类。其中的 MetaData 负责元数据的定义和加载。
650 0