使用 Castal DynamicProxy 简化 Silverlight 数据绑定

大家都知道, 在使用 Silverlight 数据绑定的时候, 为了使源对象的更改能够传播到目标,源必须实现 INotifyPropertyChanged 接口。INotifyPropertyChanged 具有 PropertyChanged 事件,该事件通知绑定引擎源已更改,以便绑定引擎可以更新目标值。

下面是一个典型的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class UserModel : INotifyPropertyChanged {
 
   private string _firstName;
   private string _lastName;
 
   public string FirstName {
      get {
         return this._firstName;
      }
      set {
         this._firstName = value;
         this.NotifyPropertyChanged("FirstName");
      }
   }
 
   public string LastName {
      get {
         return this._lastName;
      }
      set {
         this._lastName = value;
         this.NotifyPropertyChanged("LastName");
      }
   }
 
   public event PropertyChangedEventHandler PropertyChanged;
 
   protected void NotifyPropertyChanged(string propertyName) {
      if (this.PropertyChanged != null) {
         this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}

在这个例子中,设置 FirstName 、LastName 时,需要手工激发 PropertyChanged 事件, 通知绑定引擎,因此, 如果数据源中属性比较多的时候, 是比较烦人的, 每个属性的 Setter 都需要激发一下 PropertyChanged 事件, 而且不能使用 C#  自带的自动属性特性。 当然, 可以自己设置一个代码段 snippet  来解决, 但是,重复的激发 PropertyChanged 事件的代码依然存在, 这不是我们的目标。

前段时间看到有人在抱怨 Silverlight 的数据绑定,说必须要实现 INotifyPropertyChanged 接口, 而且还要手工调用 NotifyPropertyChanged 事件等等, 我想说的是, 借助 Castal DynamicProxy 提供的拦截技术,可以把手工调用 NotifyPropertyChanged 事件的代码省掉。

INotifyPropertyChanged 接口是 Silverlight 数据绑定必须的, 这一点我们无法改变。因此需要先创建一个 BaseModel , 并让其实现 INotifyPropertyChanged 接口,代码如下:

1
2
3
4
5
6
7
8
9
10
public class BaseModel : INotifyPropertyChanged {
 
   public event PropertyChangedEventHandler PropertyChanged;
 
   public void NotifyPropertyChanged(string propertyName) {
      if (this.PropertyChanged != null) {
         this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}

接下来为 BaseModel 写一个拦截器, 让所有继承自 BaseModel 的类在设置属性之后自动激发 NotifyPropertyChanged 事件, 拦截器代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class NotifyPropertyChangedInterceptor : StandardInterceptor {
 
   protected override void PostProceed(IInvocation invocation) {
      base.PostProceed(invocation);
      var methodName = invocation.Method.Name;
      // 这里可能不是很完善, 属性的 Setter 一般都是以 set_ 开头的,
      // 应该有更好的判断方法。
      if (methodName.StartsWith("set_")) {
         var propertyName = methodName.Substring(4);
         var target = invocation.Proxy as BaseModel;
         if (target != null) {
            target.NotifyPropertyChanged(propertyName);
         }
      }
   }
}

拦截器的代码很简单, 而且是可以扩展的, 相信都能看懂, 我们还需要一个 ModelHelper , 来方便的创建 Proxy , ModelHelper 的代码如下:

1
2
3
4
5
6
7
8
9
public static class ModelHelper {
 
   private static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator();
   private static readonly NotifyPropertyChangedInterceptor Interceptor = new NotifyPropertyChangedInterceptor();
 
   public static T CreateProxy≶T>(T obj) where T : class, INotifyPropertyChanged {
      return ProxyGenerator.CreateClassProxyWithTarget(obj, Interceptor);
   }
}

有了 ModelHelper , 可以说是万事俱备了, 我们来重写上边的 UserModel , UserModel 最终的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class UserModel : BaseModel {
 
   public virtual string FirstName {
      get;
      set;
   }
 
   public virtual string LastName {
      get;
      set;
   }
}

最后,使用 UserModel 的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
public partial class MainPage : UserControl {
 
   public MainPage() {
      InitializeComponent();
 
      // 不能直接使用 UserModel, 要通过 ModelHelper 创建一个 Proxy 才行。
      var dataContext = ModelHelper.CreateProxy(new UserModel());
      dataContext.FirstName = "Zhang";
      dataContext.LastName = "ZhiMin";
 
      this.DataContext = dataContext;
   }
}

我们不能改变环境, 但是可以改变自己, 因此,我们应该多一些思考,少一些抱怨。

本文的内容虽然是针对 Silverlight 数据绑定而写的, 对于 WPF 数据绑定也很适用。

posted @   张志敏  阅读(1697)  评论(7编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
历史上的今天:
2010-03-07 Silverlight 异步单元测试
2008-03-07 ExtJS入门之二 事件
点击右上角即可分享
微信分享提示