Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6642543
  • 博文数量: 915
  • 博客积分: 17977
  • 博客等级: 上将
  • 技术积分: 8846
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-26 09:59
个人简介

一个好老好老的老程序员了。

文章分类

全部博文(915)

文章存档

2022年(9)

2021年(13)

2020年(10)

2019年(40)

2018年(88)

2017年(130)

2015年(5)

2014年(12)

2013年(41)

2012年(36)

2011年(272)

2010年(1)

2009年(53)

2008年(65)

2007年(47)

2006年(81)

2005年(12)

分类: Android平台

2018-08-05 14:46:51

只读可绑定属性
假设您正在使用一个应用程序,在该应用程序中,可以方便地知道Label元素显示的文本中的单词数。 也许您希望将该工具直接构建到源自Label的类中。 我们称这个新类为CountedLabel。
到目前为止,您首先想到的是定义一个名为WordCount?属性的BindableProperty对象和一个名为WordCount的相应CLR属性。
但是等一下:只有从CountedLabel类中设置这个WordCount属性才有意义。 这意味着WordCount CLR属性不应具有公共集访问器。 它应该这样定义:

点击(此处)折叠或打开

  1. public int WordCount
  2. {
  3.     private set { SetValue(WordCountProperty, value); }
  4.     get { return (double)GetValue(WordCountProperty); }
  5. }
get访问器仍然是公共的,但set访问器是私有的。 那够了吗?
不完全是。 尽管CLR属性中有私有set访问器,但CountedLabel外部的代码仍然可以使用CountedLabel.WordCountProperty可绑定属性对象调用SetValue。 这种类型的财产设置也应该被禁止。 但是,如果WordCountProp?erty对象是公共的,那怎么能工作呢?
解决方案是使用BindableProperty.Create?ReadOnly方法创建只读的可绑定属性。 Xamarin.Forms API本身定义了几个只读的可绑定属性 - 例如,Width和
由VisualElement定义的高度属性。
以下是如何制作自己的:
第一步是使用与BindableProperty.Create相同的参数调用BindableProperty.CreateReadOnly。 但是,CreateReadOnly方法返回Binda?blePropertyKey的对象而不是BindableProperty。 将此对象定义为static和readonly,与BindableProperty一样,但将其设置为类的私有:

点击(此处)折叠或打开

  1. public class CountedLabel : Label
  2. {
  3.     static readonly BindablePropertyKey WordCountKey =
  4.         BindableProperty.CreateReadOnly("WordCount", //propertyName
  5.                                         typeof(int), // returnType
  6.                                         typeof(CountedLabel), // declaringType
  7.                                         0); // defaultValue
  8.     ...
  9. }
不要将此BindablePropertyKey对象视为加密密钥或类似的东西。 它更简单 - 实际上只是一个私有的对象。
第二步是使用BindablePropertyKey的BindableProperty属性创建一个公共BindableProperty对象:

点击(此处)折叠或打开

  1. public class CountedLabel : Label
  2. {
  3.     ...
  4.     public static readonly BindableProperty WordCountProperty = WordCountKey.BindableProperty;
  5.     ...
  6. }
这个BindableProperty对象是公共的,但它是一种特殊的BindableProperty:它不能用于SetValue调用。 尝试这样做会引发InvalidOperationException。
但是,SetValue方法有一个重载,它接受一个BindablePropertyKey对象。 CLR set访问器可以使用此对象调用SetValue,但是此set访问器必须是私有的,以防止在类外部设置属性:

点击(此处)折叠或打开

  1. public class CountedLabel : Label
  2. {
  3.     ...
  4.     public int WordCount
  5.     {
  6.         private set { SetValue(WordCountKey, value); }
  7.         get { return (int)GetValue(WordCountProperty); }
  8.     }
  9.     ...
  10. }
现在可以在CountedLabel类中设置WordCount属性。 但课程什么时候应该设定呢? 此CountedLabel类派生自Label,但它需要检测Text prop?erty何时更改,以便它可以对单词进行计数。
Label有TextChanged事件吗? 不,不是的。 但是,BindableObject实现了INotifyPropertyChanged接口。 这是一个非常重要的.NET接口,特别是对于实现Model-View-ViewModel(MVVM)体系结构的应用程序。 在第18章中,您将了解如何在自己的数据类中使用它。
INotifyPropertyChanged接口在System.ComponentModel命名空间中定义,如下所示:

点击(此处)折叠或打开

  1. public interface INotifyPropertyChanged
  2. {
  3.     event PropertyChangedEventHandler PropertyChanged;
  4. }
每当由BindableProperty支持的任何属性发生更改时,从BindableObject派生的每个类都会自动触发此PropertyChanged事件。 此事件附带的PropertyChangedEventArgs对象包含一个名为PropertyName的属性,其属性为string,用于标识已更改的属性。
因此,所有必要的是,CountedLabel为PropertyChanged事件附加处理程序并检查属性名称“Text”。 从那里它可以使用它想要的任何技术来计算字数。 完整的CountedLabel类在PropertyChanged事件上使用lambda函数。 处理程序调用Split将字符串分解为单词,并查看有多少部分结果。 Split方法基于空格,短划线和短划线(Unicode u2014)拆分文本:

点击(此处)折叠或打开

  1. public class CountedLabel : Label
  2. {
  3.     static readonly BindablePropertyKey WordCountKey =
  4.         BindableProperty.CreateReadOnly("WordCount", // propertyName
  5.                                         typeof(int), // returnType
  6.                                         typeof(CountedLabel), // declaringType
  7.                                         0); // defaultValue
  8.     public static readonly BindableProperty WordCountProperty = WordCountKey.BindableProperty;
  9.     public CountedLabel()
  10.     {
  11.         // Set the WordCount property when the Text property changes.
  12.         PropertyChanged += (object sender, PropertyChangedEventArgs args) =>
  13.         {
  14.             if (args.PropertyName == "Text")
  15.             {
  16.                 if (String.IsNullOrEmpty(Text))
  17.                 {
  18.                     WordCount = 0;
  19.                 }
  20.                 else
  21.                 {
  22.                     WordCount = Text.Split(' ', '-', '\u2014').Length;
  23.                 }
  24.             }
  25.         };
  26.     }
  27.     public int WordCount
  28.     {
  29.         private set { SetValue(WordCountKey, value); }
  30.         get { return (int)GetValue(WordCountProperty); }
  31.     }
  32. }
该类包含System.ComponentModel命名空间的using指令,用于处理程序的Property?ChangedEventArgs参数。 注意:Xamarin.Forms定义了一个名为Prop?ertyChangingEventArgs(现在时)的类。 这不是你想要的PropertyChanged han?dler。 你想要PropertyChangedEventArgs(过去时)。
因为Split方法的这个调用将文本分成空白字符,破折号和破折号,所以您可能会认为将使用包含一些破折号和破折号的文本演示CountedLabel。 这是真的。 BaskervillesCount程序是第3章中Baskervilles程序的变体,但是这里的文本段落用CountedLabel显示,并且包含常规Label以显示单词count:

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              xmlns:toolkit=
  4.                  "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
  5.             x:Class="BaskervillesCount.BaskervillesCountPage"
  6.             Padding="5, 0">
  7.     <StackLayout>
  8.         <toolkit:CountedLabel x:Name="countedLabel"
  9.                               VerticalOptions="CenterAndExpand"
  10.                               Text=
  11. "Mr. Sherlock Holmes, who was usually very late in
  12. the mornings, save upon those not infrequent
  13. occasions when he was up all night, was seated at
  14. the breakfast table. I stood upon the hearth-rug
  15. and picked up the stick which our visitor had left
  16. behind him the night before. It was a fine, thick
  17. piece of wood, bulbous-headed, of the sort which
  18. is known as a “Penang lawyer.” Just
  19. under the head was a broad silver band, nearly an
  20. inch across, “To James Mortimer, M.R.C.S.,
  21. from his friends of the C.C.H.,” was engraved
  22. upon it, with the date “1884.” It was
  23. just such a stick as the old-fashioned family
  24. practitioner used to carry—dignified, solid,
  25. and reassuring." />
  26.         <Label x:Name="wordCountLabel"
  27.                Text="???"
  28.                FontSize="Large"
  29.                VerticalOptions="CenterAndExpand"
  30.                HorizontalOptions="Center" />
  31.     </StackLayout>
  32. </ContentPage>
常规Label在代码隐藏文件中设置:

点击(此处)折叠或打开

  1. public partial class BaskervillesCountPage : ContentPage
  2. {
  3.     public BaskervillesCountPage()
  4.     {
  5.         InitializeComponent();
  6.         int wordCount = countedLabel.WordCount;
  7.         wordCountLabel.Text = wordCount + " words";
  8.     }
  9. }
它计算的单词计数是基于这样的假设:文本中的所有连字符分开两个单词,并且“hearth-rug”和“bulbous-headed”应该被计为每个单词两个单词。 当然,这并不总是正确的,但字数不像算法那么简单,因为这个代码可能意味着:

如果文本在程序运行时动态更改,程序将如何构建? 在这种情况下,每当CountedLabel对象的WordCount属性发生更改时,都需要更新单词计数。 您可以在Count?edLabel对象上附加PropertyChanged处理程序,并检查名为“WordCount”的属性。
但是,如果您尝试从XAML设置此类事件处理程序,请务必谨慎 - 例如,如下所示:

点击(此处)折叠或打开

  1. <toolkit:CountedLabel x:Name="countedLabel"
  2.          VerticalOptions="CenterAndExpand"
  3.          PropertyChanged="OnCountedLabelPropertyChanged"
  4.          Text=" ... " />
您可能希望在代码隐藏文件中编码事件处理程序,如下所示:

点击(此处)折叠或打开

  1. void OnCountedLabelPropertyChanged(object sender,
  2.                                    PropertyChangedEventArgs args)
  3. {
  4.     wordCountLabel.Text = countedLabel.WordCount + " words";
  5. }
当XAML解析器设置Text属性时,该处理程序将触发,但事件处理程序正在尝试设置尚未实例化的第二个Label的Text属性,这意味着wordCountLabel字段仍设置为null。 这是第15章在使用交互式控件时会再次出现的问题,但在第16章中处理数据绑定时,它将会得到很好的解决。
在AbsoluteLay的第14章中还有另一种可绑定属性的变体吗?out:这是附加的可绑定属性,它在实现某些类型的布局时非常有用,你将在第26章中发现, “自定义布局。”
同时,让我们看一下可绑定属性最重要的应用之一:样式。
阅读(1967) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~