WPF:把Task, Lazy和INotifyPropertyChanged组装成一个超级属性

WPF:把Task, Lazy和INotifyPropertyChanged组装成一个超级属性

这三个元素,Task代表着异步多线程,Lazy<T>代表着延迟初始化,INotifyPropertyChanged则代表着属性改变通知(多用于MVVM模式中)。那么如果把这三个元素组合起来就是一个异步延迟初始化的INotifyPropertyChanged属性。

 

实现起来是这样,因为Task和Lazy<T>只针对方法,而INotifyPropertyChanged只针对属性。所以异步延迟初始化的INotifyPropertyChanged属性还需要一个属性的初始化值,整个过程很简单。当第一次尝试获取属性值之后,初始值会被立刻返回,接着Task运行,注意此时无论多少个线程同时获取属性值,只有一个Task会运行(这是Lazy<T>的功劳),最终等这个Task运行结束后,属性值改变,通过INotifyPropertyChanged接口的执行来反馈给需要属性地方(比如界面上的数据绑定)。

 

这里我先对其他两两组合的情况讨论一下,当然相比把这三个一起组合起来,两两组合实现起来更简单些:

Task Lazy<T> 可以参考.NET Parallel Programming博客中的AsyncLazy<T>实现
INotifyPropertyChanged Task 和没有TPL时的原始多线程操作一样,只不过需要注意操作结束后要在当前SynchronizationContext中执行属性的设置操作(因为属性改变会诱发UI改变,而UI改变必须在UI线程内)。 
这里推荐用C# 5.0的async/await。
Lazy<T> INotifyPropertyChanged 没有太大意义,因为Lazy<T>的值是不可改变的。如果界面绑定的话,直接绑定Lazy<T>的Value属性。

 

下面讲我们这个三个组合的,运行示例程序可以看到,起初界面绑定的都是初始值(此时背后的Task已经开始运行),同时整个属性还提供是否加载完毕和有错误的信息(这些属性也是INotifyPropertyChanged执行的),在最下方有两个按钮又绑定了同样的属性值,但实际上异步延迟初始化只进行一次:

image

几秒后,Task运行完毕,界面发生了变化,属性1返回结果,属性2抛出异常,界面上会显示异常信息同时属性2的值保持初始值:

image

整个过程界面都是响应的,也就是说操作是异步的。

 

属性的类型名称是AsyncProperty,是在AsyncLazy<T>的基础上,加入了初始值和INotifyPropertyChanged的支持:

image

 

不过AsyncProperty<T>没有像AsyncLazy<T>那样以Lazy<Task<T>>为父类,而是把Lazy<Task<T>>作为一个字段,这样在初始化时更好被处理。用户可以通过Func<T>或者返回Task的Func<Task<T>>来初始化AsyncProperty类型。

AsyncValue属性是AsyncProperty的值,当第一次获取AsyncValue属性,它会立刻返回初始值,接着运行背后Task,当Task结束后,AsyncValue的属性会再次改变。

Error属性是错误信息,当Task中的代码发生异常后,Error会返回异常也就是Exception的Message属性。

HasValue和HasError代表是否又值和是否有错误。当HasError为True后HasValue也会是True,这里HasValue有点像是否完成初始化的意思。

(上述属性全是执行INotifyPropertyChanged接口)

 

AsyncProperty的使用就是这样很简单,那么示例程序中的ViewModel就是这样初始化AsyncProperty的:

public class ViewModel
{
    public AsyncProperty<string> Data1 { get; private set; }
    public AsyncProperty<int> Data2 { get; private set; }

    public ViewModel()
    {
        //注意是返回Task的委托,而不是直接运行的Task
        var task1 = new Func<Task<string>>(() => Task.Run(() =>
            {
                Task.Delay(3000).Wait();
                return "Mgen";
            }));
        var task2 = new Func<Task<int>>(() => Task.Run(() =>
            {
                Task.Delay(2000).Wait();
                throw new Exception("测试错误");
                return 7;
            }));

        //初始化AsyncProperty,参数一是返回Task的委托,参数二是属性的初始值
        Data1 = new AsyncProperty<string>(task1, "None");
        Data2 = new AsyncProperty<int>(task2, -1);
    }
}

 

OK,最后在界面上就直接绑定相应的属性就可以了。这里就不需要在贴代码了。读者自行参考示例程序和源代码。

 

源代码下载 
mgen_lazyinpc.zip
源代码环境:Microsoft Visual Studio Express 2012 for Windows Desktop

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值