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

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

文章分类

全部博文(891)

文章存档

2020年(8)

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平台

2019-11-25 13:19:31

简化ViewModel
INotifyPropertyChanged的典型实现为类定义的每个公共属性都有一个私有支持字段,例如:

点击(此处)折叠或打开

  1. double number;
它还有一个OnPropertyChanged方法,负责触发PropertyChanged事件:

点击(此处)折叠或打开

  1. protected void OnPropertyChanged(string propertyName)
  2. {
  3.     PropertyChangedEventHandler handler = PropertyChanged;
  4.     if (handler != null)
  5.     {
  6.         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  7.     }
  8. }
典型的属性定义如下所示:

点击(此处)折叠或打开

  1. public double Number
  2. {
  3.     set
  4.     {
  5.         if (number != value)
  6.         {
  7.             number = value;
  8.             OnPropertyChanged("Number");
  9.             // Do something with the new value.
  10.         }
  11.     }
  12.     get
  13.     {
  14.         return number;
  15.     }
  16. }
潜在问题涉及传递给OnPropertyChanged方法的文本字符串。 如果拼错它,您将不会收到任何类型的错误消息,但涉及该属性的绑定将不起作用。 此外,支持字段在此单个属性中出现三次。 如果您有几个类似的属性并通过复制和粘贴操作定义它们,则可以省略重新命名后备字段的三个外观之一,并且该错误可能很难追踪。
您可以使用C#5.0中引入的功能解决第一个问题。 CallerMemberNameAttribute类允许您使用调用方法或属性的名称替换可选方法参数。
您可以通过重新定义OnPropertyChanged方法来使用此功能。 通过为它赋值null并在其前面加上方括号中的CallerMemberName属性,使参数可选。 您还需要System.Runtime.CompilerServices的using指令:

点击(此处)折叠或打开

  1. protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
  2. {
  3.     PropertyChangedEventHandler handler = PropertyChanged;
  4.     if (handler != null)
  5.     {
  6.         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  7.     }
  8. }
现在,Number属性可以调用OnPropertyChanged方法,而不使用指示属性名称的参数。 该参数将自动设置为属性名称“Number”,因为这是对OnPropertyChanged的调用所在的位置:

点击(此处)折叠或打开

  1. public double Number
  2. {
  3.     set
  4.     {
  5.         if (number != value)
  6.         {
  7.             number = value;
  8.             OnPropertyChanged();
  9.             // Do something with the new value.
  10.         }
  11.     }
  12.     get
  13.     {
  14.         return number;
  15.     }
  16. }
此方法避免了拼写错误的文本属性名称,并且还允许在程序开发期间更改属性名称,而无需担心还会更改文本字符串。 实际上,发明CallerMemberName属性的主要原因之一是简化实现INotifyPropertyChanged的类。
但是,仅当从值正在更改的属性调用OnPropertyChanged时,此方法才有效。 在早期的ColorViewModel中,除了对OnPropertyChanged的一个调用之外,在所有调用中仍然需要显式属性名称。
可以更进一步简化set访问器逻辑:您需要定义一个通用方法,可能名为SetProperty或类似的东西。 此SetProperty方法也使用CallerMemberName属性定义:

点击(此处)折叠或打开

  1. bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
  2. {
  3.     if (Object.Equals(storage, value))
  4.         return false;
  5.     storage = value;
  6.     OnPropertyChanged(propertyName);
  7.     return true;
  8. }
  9. protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
  10. {
  11.     PropertyChangedEventHandler handler = PropertyChanged;
  12.     if (handler != null)
  13.     {
  14.         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  15.     }
  16. }
SetProperty的第一个参数是对支持字段的引用,第二个参数是设置为该属性的值。 SetProperty自动检查和设置支持字段。 请注意,它在调用OnPropertyChanged时显式包含propertyName参数。 (否则propertyName参数将成为字符串“SetProperty”!)如果属性已更改,则该方法返回true。 您可以使用此返回值来使用新值执行其他处理。
现在Number属性如下所示:

点击(此处)折叠或打开

  1. public double Number
  2. {
  3.     set
  4.     {
  5.         if (SetProperty(ref number, value))
  6.         {
  7.             // Do something with the new value.
  8.         }
  9.     }
  10.     get
  11.     {
  12.         return number;
  13.     }
  14. }
虽然SetProperty是一个通用方法,但C#编译器可以从参数中推断出类型。 如果您不需要对属性集访问器中的新值执行任何操作,则甚至可以将两个访问器减少为单行而不会模糊操作:

点击(此处)折叠或打开

  1. public double Number
  2. {
  3.     set { SetProperty(ref number, value); }
  4.     get { return number; }
  5. }
您可能希望这样简化以至于您希望将SetProperty和OnPropertyChanged方法放在它们自己的类中,并在创建自己的ViewModel时从该类派生。 这样一个名为ViewModelBase的类已经存在于Xamarin.FormsBook.Toolkit库中:

点击(此处)折叠或打开

  1. using System;
  2. using System.ComponentModel;
  3. using System.Runtime.CompilerServices;
  4. namespace Xamarin.FormsBook.Toolkit
  5. {
  6.     public class ViewModelBase : INotifyPropertyChanged
  7.     {
  8.         public event PropertyChangedEventHandler PropertyChanged;
  9.         protected bool SetProperty(ref T storage, T value,
  10.                                       [CallerMemberName] string propertyName = null)
  11.         {
  12.             if (Object.Equals(storage, value))
  13.                 return false;
  14.             storage = value;
  15.             OnPropertyChanged(propertyName);
  16.             return true;
  17.         }
  18.         protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
  19.         {
  20.             PropertyChangedEventHandler handler = PropertyChanged;
  21.             if (handler != null)
  22.             {
  23.                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  24.             }
  25.         }
  26.     }
  27. }
本章将在本章的其余两个示例中使用。


阅读(97) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~