通知公告
使用MPF事件(MPF实例教程第十三课)
日期:2016-01-14  发布人:admin  浏览量:864

    MPF所有事件的基类都是suic::EventArg,其中最重要的就是suic::RoutedEventArg,此事件具有向上或向下的路由功能,MPF里大多都是此类事件,我们叫做路由事件,路由事件有如下三种类型:
    1) suic::RoutingStrategy::Direct
       此种类型的事件表示仅在触发的元素上调用,事件不会向元素的父元素向上进行路由;同时,也不会从元素的根节点向下路由。
    2) suic::RoutingStrategy::Bubble
       此种类型的事件表示首先在触发的元素上调用元素对应的处理例程,如果事件没有处理则继续向其父元素方向处理,直到事件被处理或到达根元素为止,这种事件处理方式叫冒泡处理。
    3) suic::RoutingStrategy::Tunnel
       此种类型的事件表示首先在触发元素的根元素上调用对应的处理例程,如果事件没有处理则继续向下处理,直到事件被处理或到达开始触发的元素为止,这种事件处理方式叫隧道处理。
   上面了解了MPF中路由事件的三种类型,那么怎么样来挂接我们的处理例程到指定的路由事件上呢?
   挂接事件处理例程:
   我们继续完善我们的演示程序,我们增加一个suic::Button控件,用来动态给我们的演示程序更换外观:
    
   这个按钮的名称为:switchTheme,我们要在按钮点击时执行我们定义的处理例程,为了达到这个目的,我们需要弄清楚如下几点:
       1) 需要挂接的事件类型是什么
       2) 找到事件对应的声明(因为我们定义的方法必须和事件声明一致)
   现在我们需要挂接的是按钮的点击事件:ClickEvent,其对应的事件处理声明为


   typedef delegate<void(Element*, RoutedEventArg*)> ClickEventHandler; 


   知道了ClickEvent事件处理声明,我们就可以定义对应的处理方法,先在MainWindow.h里定义如下方法:


   void OnSwitchThemeClick(suic::Element* sender, suic::RoutedEventArg* e);


   注意,定义的处理方法必须和事件声明的一致,包括返回值、参数类型、个数以及参数的顺序都必须保持一致。
   然后在MainWindow.cpp实现OnSwitchThemeClick:


   void OnSwitchThemeClick(suic::Element* sender, suic::RoutedEventArg* e)
   {
       // 业务代码
       e->SetHandled(true); // 设置为true表示我们已经处理了这个事件,不用再往下传递了
   }
   需要挂接的方法准备好了,下面我们把OnSwitchThemeClick挂接到按钮的点击事件,使得点击按钮时调用我们的实现的方法,挂接我们的方法最好在窗口的OnInitialized里进行,因为到这里表示所有的资源都已经加载完毕,我们可以做一些初始化操作了,代码片段如下:
   void OnInitialized(suic::EventArg* e)
   {
      // 先调用基类的方法进行处理
      suic::Window::OnInitialized(e);
      // 找到名称为switchTheme的按钮
      suic::Button* pBtn = FindElem<suic::Button>(_U("switchTheme"));
      if (NULL != pBtn)
      {
         // 把我们定义的处理关联到按钮的点击事件
         pBtn->AddClick(new suic::RoutedEventHandler(this, & OnSwitchThemeClick);
      }
   }
    
   到这里,我们知道了怎么把我们自己定义的方法关联到指定的事件进行处理,在MPF里,所有路由事件都可以通过方法AddHandler完成挂接,比如上面的AddClick,其实在按钮内部是这样子调用的:

   

   AddHandler(suic::ButtonBase::ClickEvent, new suic::RoutedEventHandler(this, & OnSwitchThemeClick), false);
   

   最终其实还是调的AddHandler来完成的,对应的移除事件的方法就是RemoveHandler。
   上面讲解了已有事件以及怎么样把我们实现的方法挂接到指定的事件,那么,根据业务需求,我们怎么样来实现自己的事件呢?
   有点需要明白,在MPF中事件处理都是一种委托(delegate),比如RoutedEventHandler的定义就是:


   typedef delegate<void(DpObject*, RoutedEventArg*)> RoutedEventHandler;
   

   可以接收c函数、类成员函数以及类静态成员函数,使用上非常灵活,MPF系统预定义了多大可以带九个参数的委托。
   实现一般的事件(非路由事件)非常简单,只需要定义好事件参数类型,然后定义好和事件参数对应的事件委托就可以了,比如:


    class MyEventArg : public suic::EventArg
    {
    public:
        … // 业务实现
    }
    // 然后定义和MyEventArg对应的委托
    typedef delegate<void(Element*,MyEventArg*)> MyEventHandler;
    甚至你还可以根据业务需求灵活定义需要的委托:
    typedef delegate<bool(int errcode,String msg)> MyErrorHandler;
    这种事件(委托)的挂接非常简单,处理方法和上面的说明额步骤一样,比如我们实现了方法bool OnError(int errcode, String msg);然后在类中定义了之前的委托:

   class MyClass
   {
   public:
      MyErrorHandler errorHandler;
   };
   那么挂接如下:
   errorHandler += MyErrorHandler(target,OtherClass:: OnError);
   其中的target是OtherClass的一个实例对象。
   上面说了一般的事件委托定义和处理,接下来我们看下怎么样实现自定义的路由事件,步骤如下:
    1) 从suic::RoutedEventArg继承自己的路由事件参数类型,或则直接使用suic::RoutedEventArg也可以;
    2) 定义自己的事件处理委托;
    3) 在对应的类里静态声明我们的事件;
    4) 向系统注册我么你的事件;
    5) 触发事件

   注意:实现自己的路由事件参数类型时,必须实现CallEventHandler虚方法,否则事件无效,不过suic::RoutedEventArg里已经做好了一个模版方法:
   template<typename T, typename H, typename A>
   void InternalCall(Object* handler, Object* target)
   我们可以直接调用此方法即可。
   下面先实现路由事件:
   class MyRoutedEventArg : public suic::RoutedEventArg
   {
   public:
   
   void CallEventHandler(suic::Object handler, suic::Object* target)
   
   {
     
   InternalCall<suic::Element,MyRoutedEventHandler,MyRoutedEventArg>(handler, target);
   
   }
   };
   定义我们的事件处理委托:
   typedef delegate<void(suic::Element,MyRoutedEventArg*)> MyRoutedEventHandler;
   实现我们的业务类:
   MyControl.h
   class MyControl : public suic::Control
   {
   public:
   
   static suic::RoutedEvent* MyRoutedEvent;
   
   static bool StaticInit();
   };


   MyControl.cpp
   suic::RoutedEvent* MyControl::MyRoutedEvent;

   bool MyControl::StaticInit()
   {
   
   if (NULL == MyRoutedEvent)
      {
   
      MyRoutedEvent = suic::EventHelper::RegisterRoutedEvent(_U("MyRouted"),RoutingStrategy::Bubble, RTTIType(), RTTIType());
      }
   
   return true;
   }
   到这里已经完成整个自定义路由事件,下面的代码触发路由事件的执行:
   class MyControl : public suic::Control
   {
   public:
      static suic::RoutedEvent* MyRoutedEvent;
      static bool StaticInit();
      void OnExecuteEvent()
      {
         MyRoutedEventArg e;
         e.SetRoutedEvent(
MyRoutedEvent);
         RaiseEvent(&e);
      }

   };
   然后就可以像使用系统路由事件一样,我们可以挂接自己的处理例程来对该事件进行处理。
   注意:拥有路由事件的类必须直接或间接从suic::DpObject继承。