代码改变世界

Prism V2之旅(6)

2009-01-13 15:14  Clingingboy  阅读(7238)  评论(4编辑  收藏  举报

     这篇来讲事件.事件主要用来交互.

监听事件

 

订阅了一些blog的rss,如果我订阅的blog发布了新的文章的话,系统(就是抓虾)就会帮我抓取新的rss信息

image

很好理解,一方订阅(Subscribe),一方发布(Publish).

prism的事件

 

prism的抽象类EventBase实现了事件的订阅和发布的操作.CompositePresentationEvent类继承自EventBase做了进一步封装,其是一个泛型类,我们可以通过CompositePresentationEvent来传递一个参数.

image

 

下面是一个简单的示例,记得先调用Subscribe方法订阅事件,然后调用Publish方法来发布,同时也可以调用Unsubscribe方法来取消订阅

private void SubscribeAndRaiseEvent()
{
    CompositePresentationEvent<string> compositePresentationEvent = new CompositePresentationEvent<string>();
    var action = new Action<string>((str) =>
    {
        System.Windows.MessageBox.Show(str);
    });
    compositePresentationEvent.Subscribe(action);
    compositePresentationEvent.Publish("hello");
    compositePresentationEvent.Unsubscribe(action);
    compositePresentationEvent.Publish("hello");
}

 

多重订阅,可以订阅多个事件

 

private void MultipleSubscribersAndRaiseCustomEvent()
{
    CompositePresentationEvent<string> compositePresentationEvent = new CompositePresentationEvent<string>();
    var actionOne = new Action<string>((str) =>
    {
        System.Windows.MessageBox.Show(str);
    });
    var actionTwo = new Action<string>((str) =>
    {
        System.Windows.MessageBox.Show(str);
    });
    compositePresentationEvent.Subscribe(actionOne);
    compositePresentationEvent.Publish("hello");
    compositePresentationEvent.Unsubscribe(actionTwo);
    compositePresentationEvent.Publish("world");
}

 

事件聚合模块交互

 

上面代码为示例,效果与.net内置的事件相似,只是做法不同而已,这样的话没多大意义,如果不同模块之间需要交互,那么事件就起作用了.

所以就需要一个容器来保存事件的状态,prism的IEventAggregator接口便是这样设计的

当事件被订阅的事件,IEventAggregator的GetEvent方法,该方法是一个泛型方法,传入的参数必须继承自EventBase,

该方法会先实例化这个类,所以我们不可以出现这样的代码

private void CustomEventWithEventAggregator()
{
    eventAggregator.GetEvent<CompositePresentationEvent<string>>();
}

 

正确的做法是从CompositePresentationEvent派生一个类,如

private void CustomEventWithEventAggregator()
{
    var action = new Action<string>((str) =>
    {
        System.Windows.MessageBox.Show(str);
    });
    eventAggregator.GetEvent<CustomEvent>().Subscribe(action);
    eventAggregator.GetEvent<CustomEvent>().Publish("hello");
}

public class CustomEvent : CompositePresentationEvent<string>
{
}

 

以上代码为演示,你只需要明确定义Event的类型,就可以在不同模块交互.两个模块之间就不要相互引用,降低了耦合度.

事件的回调方式

 

当事件回调时(即事件被触发时),有三种方式.

1.同步线程 该怎么处理就怎么处理,默认情况下是以这种方式来处理的

2.在UI线程上触发,即调用了wpf Dispatcher的BeginInvoke方法

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, method, arg);

3.在后台线程上异步调用,即通过BackgroundWorker类来异步操作

public override void InvokeAction(Action<TPayload> action, TPayload argument)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += ((sender, e) => action((TPayload)e.Argument));
    //handle worker.RunWorkerCompleted and log exceptions?
    worker.RunWorkerAsync(argument);
}

 

这三种方式是由ThreadOption枚举来设定的

image

这便是CompositePresentationEvent类扩展的功能之一,如下代码

public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter)
{
    IDelegateReference actionReference = new DelegateReference(action, keepSubscriberReferenceAlive);
    IDelegateReference filterReference;
    if (filter != null)
    {
        filterReference = new DelegateReference(filter, keepSubscriberReferenceAlive);
    }
    else
    {
        filterReference = new DelegateReference(new Predicate<TPayload>(delegate { return true; }), true);
    }
    EventSubscription<TPayload> subscription;
    switch (threadOption)
    {
        case ThreadOption.PublisherThread:
            subscription = new EventSubscription<TPayload>(actionReference, filterReference);
            break;
        case ThreadOption.BackgroundThread:
            subscription = new BackgroundEventSubscription<TPayload>(actionReference, filterReference);
            break;
        case ThreadOption.UIThread:
            subscription = new DispatcherEventSubscription<TPayload>(actionReference, filterReference, UIDispatcher);
            break;
        default:
            subscription = new EventSubscription<TPayload>(actionReference, filterReference);
            break;
    }


    return base.InternalSubscribe(subscription);
}

 

弱引用还是强引用?


通过上面的代码,我们看到该方法还有一个参数keepSubscriberReferenceAlive,默认值是false,就是弱引用了,如果你设置成强引用,记得在不需要事件的时候,取消事件的订阅.

事件过滤


Subscribe方法最后一个方法是filter事件过滤器,

举个例子我订阅了某某技术牛人的rss,平时他都写一些技术文章,可他也喜欢写了一些与技术无关的文章,我不想看,并不是他发布什么内容我都接受的,我是要有所选择的,我要把这些内容过滤掉.这个功能比较好.平时看报纸就没这个功能:).

上面的解释就是事件过滤器的功能.

 

事件在v2的改动不是很大,大家也可以参考这篇,有重复了,这篇就到这里