查看BindableObject和BindableProperty
名为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编程中最具特色的四个单词序列。
阅读(2262) | 评论(0) | 转发(0) |