本文共 7079 字,大约阅读时间需要 23 分钟。
名为BindableObject和BindableProperty的类的存在最初可能有点令人困惑。请记住,BindableObject与Object非常相似,因为它充当了Xamarin.Forms API的一大块基类,特别是Element和VisualElement。
BindableObject为BindableProperty类型的对象提供支持。 BindableProperty对象扩展CLR属性。关于可绑定属性的最佳见解来自于您创建自己的一些内容 - 正如您将在本章结束之前所做的那样 - 但您也可以通过探索现有的可绑定属性来收集一些内容。在第7章“XAML与代码”的开头,创建了两个具有许多相同属性设置的按钮,只是使用C#3.0对象初始化语法和另一个按钮在代码中设置了一个按钮的属性。在XAML中实例化并初始化。这是一个名为PropertySettings的类似(但仅限代码)程序,它还以两种不同的方式创建和初始化两个按钮。第一个Label的属性以旧式方式设置,而第二个Label的属性使用更详细的技术设置:public class PropertySettingsPage : ContentPage{ public PropertySettingsPage() { Label label1 = new Label(); label1.Text = "Text with CLR properties"; label1.IsVisible = true; label1.Opacity = 0.75; label1.HorizontalTextAlignment = TextAlignment.Center; label1.VerticalOptions = LayoutOptions.CenterAndExpand; label1.TextColor = Color.Blue; label1.BackgroundColor = Color.FromRgb(255, 128, 128); label1.FontSize = Device.GetNamedSize(NamedSize.Medium, new Label()); label1.FontAttributes = FontAttributes.Bold | FontAttributes.Italic; Label label2 = new Label(); label2.SetValue(Label.TextProperty, "Text with bindable properties"); label2.SetValue(Label.IsVisibleProperty, true); label2.SetValue(Label.OpacityProperty, 0.75); label2.SetValue(Label.HorizontalTextAlignmentProperty, TextAlignment.Center); label2.SetValue(Label.VerticalOptionsProperty, LayoutOptions.CenterAndExpand); label2.SetValue(Label.TextColorProperty, Color.Blue); label2.SetValue(Label.BackgroundColorProperty, Color.FromRgb(255, 128, 128)); label2.SetValue(Label.FontSizeProperty, Device.GetNamedSize(NamedSize.Medium, new Label())); label2.SetValue(Label.FontAttributesProperty, FontAttributes.Bold | FontAttributes.Italic); Content = new StackLayout { Children = { label1, label2 } }; }}
这两种设置属性的方法完全一致:
然而,替代语法似乎很奇怪。 例如:label2.SetValue(Label.TextProperty, "Text with bindable properties");
什么是SetValue方法? SetValue由BindableObject定义,每个可视对象都派生自该对象。 BindableObject还定义了一个GetValue方法。
SetValue的第一个参数名称为Label.TextProperty,表示该名称TextProperty是静态的,但尽管它的名字,它根本不是一个属性。 它是Label类的静态字段。 TextProperty也是只读的,它在Label类中的定义如下:public static readonly BindableProperty TextProperty;
这是BindableProperty类型的对象。 当然,将一个字段命名为TextProperty似乎有点令人不安,但确实如此。 但是,因为它是静态的,所以它独立于任何可能存在或可能不存在的Label对象。
如果查看Label类的文档,您将看到它定义了10个属性,包括Text,TextColor,FontSize,FontAttributes等。 您还将看到10个具有名称TextProperty,TextCol?orProperty,FontSizeProperty,FontAttributesProperty等名称的BindableProperty类型的公共静态只读字段。这些属性和字段密切相关。 实际上,在Label类的内部,Text CLR属性被定义为这样引用相应的TextProperty对象:public string Text{ set { SetValue(Label.TextProperty, value); } get { return (string)GetValue(Label.TextProperty); }}
所以你明白为什么你的应用程序使用Label.TextProperty参数调用SetValue完全等同于直接设置Text属性,并且可能只是更快一点!
Label中Text属性的内部定义不是机密信息。 这是标准代码。虽然任何类都可以定义BindableProperty对象,但只有从Binda?bleObject派生的类才能调用实际实现类中属性的SetValue和GetValue方法。 GetValue方法需要进行强制转换,因为它被定义为返回对象。维护Text属性所涉及的所有实际工作都在那些SetValue和GetValue调用中进行。 BindableObject和BindableProperty对象有效地扩展了标准CLR属性的功能,以提供系统化的方法:名为Text的属性与名为TextProp的BindableProperty的紧密关系反映在程序员谈论这些属性的方式中:有时程序员说Text属性由名为TextProperty的BindableProperty“支持”,因为TextProperty提供基础结构支持 文本。 但一个常见的捷径就是说Text本身就是一个“可绑定的属性”,通常没有人会被混淆。
并非每个Xamarin.Forms属性都是可绑定属性。 ContentPage的Content属性和Layout 的Children属性都不是可绑定属性。在VisualElement定义的28个属性中,26个由可绑定属性支持,但Bounds属性和Resources属性不支持。与FormattedString结合使用的Span类不是从BindableOb?ject派生的。因此,Span不会继承SetValue和GetValue方法,也无法实现BindableProperty对象。这意味着Label的Text属性由可绑定属性支持,但Span的Text prop不是。这有什么不同吗?当然它有所作为!如果您回忆上一章中的DynamicVsStatic程序,您会发现DynamicResource处理Label的Text属性,但不处理Span的Text属性。是不是DynamicResource只能使用可绑定属性?这个假设通过Element定义的以下公共方法的定义得到了很好的证实:public void SetDynamicResource(BindableProperty property, string key);
当属性是DynamicResource标记扩展的目标时,这就是字典键与元素的特定属性相关联的方式。
此SetDynamicResource方法还允许您在代码中的属性上设置动态资源链接。这是来自DynamicVsStatic的仅代码版本的页面类,名为DynamicVsStaticCode。排除使用FormattedString和Span对象有点简化,但其他方面它非常准确地模仿了解析前一个XAML文件的方式,特别是如何通过XAML解析器设置Label元素的Text属性:public class DynamicVsStaticCodePage : ContentPage{ public DynamicVsStaticCodePage() { Padding = new Thickness(5, 0); // Create resource dictionary and add item. Resources = new ResourceDictionary { { "currentDateTime", "Not actually a DateTime" } }; Content = new StackLayout { Children = { new Label { Text = "StaticResource on Label.Text:", VerticalOptions = LayoutOptions.EndAndExpand, FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) }, new Label { Text = (string)Resources["currentDateTime"], VerticalOptions = LayoutOptions.StartAndExpand, HorizontalTextAlignment = TextAlignment.Center, FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) }, new Label { Text = "DynamicResource on Label.Text:", VerticalOptions = LayoutOptions.EndAndExpand, FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) } } }; // Create the final label with the dynamic resource. Label label = new Label { VerticalOptions = LayoutOptions.StartAndExpand, HorizontalTextAlignment = TextAlignment.Center, FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)) }; label.SetDynamicResource(Label.TextProperty, "currentDateTime"); ((StackLayout)Content).Children.Add(label); // Start the timer going. Device.StartTimer(TimeSpan.FromSeconds(1), () => { Resources["currentDateTime"] = DateTime.Now.ToString(); return true; }); }}
第二个Label的Text属性直接从字典条目设置,并且在此上下文中使用字典似乎有点无意义。 但是,最后一个Label的Text属性通过调用SetDynamicResource绑定到字典键,这允许在字典内容更改时使属性更新:
考虑一下:如果它不能使用BindableProperty对象引用属性,那么这个SetDynamicResource方法的签名是什么?在方法调用中引用属性值很容易,但不是属性本身。有两种方法,例如System.Reflection命名空间中的PropertyInfo类或LINQ Expression对象。但是BindableProperty对象是专门为此目的而设计的,以及处理属性和字典键之间的底层链接的基本工作。类似地,当我们在下一章中探索样式时,您将遇到一个用于与样式连接的Setter类。 Setter定义了一个名为Property的类型为BindableProperty的属性,该属性要求样式所针对的任何属性必须由可绑定属性支持。这允许在样式所针对的元素之前定义样式。同样,对于数据绑定。 BindableObject类定义一个SetBinding方法,该方法与Element上定义的SetDynamicResource方法非常相似:public void SetBinding(BindableProperty targetProperty, BindingBase binding);
再次注意第一个参数的类型。 数据绑定所针对的任何属性都必须由可绑定属性支持。
出于这些原因,无论何时创建自定义视图并需要定义公共属性,您的默认倾向应该是将它们定义为可绑定属性。 只有在经过仔细考虑后,您才会得出结论:如果您撤消并定义普通的CLR属性,则该属性不是必需或适合于由样式或数据绑定作为目标。所以每当你创建一个派生自BindableObject的类时,你应该在该类中输入的第一段代码之一就是“public static readonly BindableProperty” - 也许是所有Xamarin.Forms编程中最具特色的四个单词序列。转载地址:http://nplja.baihongyu.com/