不同的SOUI控件可以产生不同的事件。SOUI系统中提供了两种事件处理方式:事件订阅 + 事件处理映射表(参见第八篇:SOUI中控件事件的响应)

事件订阅由于直接将事件及事件处理函数连接,不存在事件分发的问题,这里主要介绍使用事件映射表时的事件分发。

在回答这个问题前,首先了解一下什么是事件分发。

在大型项目中,程序逻辑可能非常复杂,如果将所有UI中控件的事件处理集中在一个消息/事件映射表里,代码的可维护性会变得非常差。解决这个问题常见的方法就是将事件进行分类(如根据来源分类),不同类别的事件采用一个独立的事件处理对象来处理,这就是事件分发的核心。

目前流行的UI通常采用Tab控件来组织UI,不同的功能放到不同的Tab页中,不同的Tab页可能互不相干的功能模块,对于类似这样的情形很自然的会想到采用事件分发机制来实现模块之间逻辑的解耦(如下图中SoTool采用的UI)。

在上面的UI中,虽然整个UI被TAB分成了6个页面,但是6个页面都存在于同一个宿主窗口中。

一般情况下,如果UI相对比较简单,我们推荐直接在宿主窗口的事件处理映射表中统一处理控件事件。

但是当出现如上图这样复杂的界面时,最好是将不同功能页的事件处理在不同的对象中分别处理。

在MFC中,一个类要处理消息,这个类通常派生自CCmdTarget(可能记错了,太久不用MFC了),主窗口收到的消息会自动路由到这个消息处理对象中。

在WTL中,WTL提供了一组消息映射宏:CHAIN_MSG_MAP,CHAIN_MSG_MAP_MEMBER等以便将消息分发到同样实现了消息映射表的任意C++对象。

SOUI的事件分发采用了WTL消息分发类似的机制,同样采用事件映射宏的方式来构造事件映射表,下面是SOUI中几个主要的和事件分发相关的宏:

#define EVENT_MAP_BEGIN()                           \
protected: \
virtual BOOL _HandleEvent(SOUI::EventArgs *pEvt)\
{ \
UINT uCode = pEvt->GetID(); \ #define EVENT_MAP_DECLEAR() \
protected: \
virtual BOOL _HandleEvent(SOUI::EventArgs *pEvt);\ #define EVENT_MAP_BEGIN2(classname) \
BOOL classname::_HandleEvent(SOUI::EventArgs *pEvt)\
{ \
UINT uCode = pEvt->GetID(); \ #define EVENT_MAP_END() \
return __super::_HandleEvent(pEvt); \
} \ #define EVENT_MAP_BREAK() \
return FALSE; \
} \ #define CHAIN_EVENT_MAP(ChainClass) \
if(ChainClass::_HandleEvent(pEvt)) \
return TRUE; \ #define CHAIN_EVENT_MAP_MEMBER(theChainMember) \
{ \
if(theChainMember._HandleEvent(pEvt)) \
return TRUE; \
} #define EVENT_CHECK_SENDER_ROOT(pRoot) \
{ \
SWindow *pWnd = sobj_cast<SWindow>(pEvt->sender);\
if(!pWnd->IsDescendant(pRoot)) \
return FALSE; \
} // void OnEvent(EventArgs *pEvt)
#define EVENT_HANDLER(cd, func) \
if(cd == uCode) \
{ \
func(pEvt); return TRUE; \
}

下面是SoTool中的MainDlg中的事件处理:

    //soui消息
EVENT_MAP_BEGIN()
EVENT_NAME_COMMAND(L"btn_close", OnClose)
EVENT_NAME_COMMAND(L"btn_min", OnMinimize)
EVENT_NAME_COMMAND(L"btn_max", OnMaximize)
EVENT_NAME_COMMAND(L"btn_restore", OnRestore)
CHAIN_EVENT_MAP_MEMBER(m_imgMergerHandler)
CHAIN_EVENT_MAP_MEMBER(m_codeLineCounter)
CHAIN_EVENT_MAP_MEMBER(m_2UnicodeHandler)
CHAIN_EVENT_MAP_MEMBER(m_folderScanHandler)
CHAIN_EVENT_MAP_MEMBER(m_calcMd5Handler)
EVENT_MAP_END()

上面代码中,EVENT_MAP_BEGIN()和EVENT_MAP_END()这两个宏构造出一个空的事件处理函数,该函数自动将未处理的事件交给基类的事件处理函数处理。

如果基类中没有事件处理函数,显然这个事件映射表编译不能通过,此时SOUI提供了另一个EVENT_MAP_BREAK()来代替。

上面的事件分发表中,我使用CHAIN_EVENT_MAP_MEMBER宏将来自不同页面的控件事件传递到不同的事件处理对象中。

下面代码是m_imgMergerHandler对象头文件。

class CImageMergerHandler : public IFileDropHandler
{
friend class CMainDlg;
public:
CImageMergerHandler(void);
~CImageMergerHandler(void); void OnInit(SWindow *pRoot); void AddFile(LPCWSTR pszFileName);
protected:
virtual void OnFileDropdown(HDROP hDrop); void OnSave();
void OnClear();
void OnModeHorz();
void OnModeVert(); EVENT_MAP_BEGIN()
EVENT_CHECK_SENDER_ROOT(m_pPageRoot)
EVENT_NAME_COMMAND(L"btn_save", OnSave)
EVENT_NAME_COMMAND(L"btn_clear", OnClear)
EVENT_NAME_COMMAND(L"radio_horz", OnModeHorz)
EVENT_NAME_COMMAND(L"radio_vert", OnModeVert)
EVENT_MAP_BREAK() SWindow *m_pPageRoot;
SImgCanvas *m_pImgCanvas;
};

可以看到这里的事件映射表使用了EVENT_MAP_BREAK来结束。

在SOUI中推荐使用控件的name属性来标识一个控件(name属性是一个wchar*的字符串,使用name虽然在事件分发时采用字符串比较,较基于整数id属性的比较效率低一点,好处在于代码的可读性好),不同的页面中的控件如果出现相同的name该如何识别呢?

在SOUI中使用了一点小技巧:在事件处理对象中实现一个oninit函数,该函数在maindlg中处理WM_INITDIALOG时被调用,在oninit中保存了一个页面根节点的指针:

SWindow *m_pPageRoot;

在事件映射表的开始,我们采用EVENT_CHECK_SENDER_ROOT(m_pPageRoot)这个宏来识别那些来自本页面的事件。如果事件是来自其它页面则不处理。

第二十五篇:在SOUI中做事件分发处理的更多相关文章

  1. 第二十八篇:SOUI中自定义控件开发过程

    在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件. 学习一个新东西,最简单的办法就是依葫芦画瓢.事实上在SOUI系统中内置控件 ...

  2. 第二十五篇 -- C++宝典中的图书管理系统

    此篇文章是基于C++宝典写的图书管理系统,本人对其中的部分做了相应修改,并且以现有格式替代原有格式,使程序更加清晰明了.此程序运行在VS2017上. 系统设计 图书管理系统分为四个模块:图书管理模块. ...

  3. Egret入门学习日记 --- 第十五篇(书中 6.1~6.9节 内容)

    第十五篇(书中 6.1~6.9节 内容) 好的,昨天完成了第五章. 今天来看第六章. 总结重点: 1.如何对组件进行分组? 跟着做: 重点1:如何对组件进行分组? 首先,选中你想要组合的组件. 然后点 ...

  4. SpringBoot非官方教程 | 第二十五篇:2小时学会springboot

    转载请标明出处: http://blog.csdn.net/forezp/article/details/61472783 本文出自方志朋的博客 一.什么是spring boot Takes an o ...

  5. Python之路(第二十五篇) 面向对象初级:反射、内置方法

    [TOC] 一.反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它 ...

  6. flask第二十五篇——控制语句

    有兴趣的请加船长公众号:自动化测试实战 先和大家强调一个发邮件的问题 # coding: utf-8 import smtplib from email.mime.text import MIMETe ...

  7. 第二十五篇:使用 sigaction 函数实现可靠信号

    前言 在前文中,讲述了一个可靠信号的示例.它分成几个步骤组成( 请参考前文 ).在 Linux 系统编程中,有个方法可以将这些步骤给集成起来,让我们使用起来更加的方便. 那就是调用 sigaction ...

  8. 第二十五篇 hashlib模块(* *)

    用于加密相关的操作,Python 3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法. 加密复杂程度: SHA1  ...

  9. SpringBoot第二十五篇:SpringBoot与AOP

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/11457867.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言   作者在实际 ...

随机推荐

  1. 按钮点击事件,打开新的Activity

    按钮点击事件,打开新Activity, 打开网页 findViewById(R.id.btnStartBAty).setOnClickListener(new View.OnClickListener ...

  2. Largest BST Subtree

    Given a binary tree, find the largest subtree which is a Binary Search Tree (BST), where largest mea ...

  3. POJ 1321

    http://poj.org/problem?id=1321 一道深搜的题目,和那个POJ3740有点相类似. 也是到了现在我才知道原来深搜也有几种套路的,以前我的都是用队列来做,那个是不需要记住什么 ...

  4. js控制精度的加减乘除:js浮点数计算问题

    //加法函数 function accAdd(arg1, arg2) { var r1, r2, m; try { r1 = arg1.toString().split(".")[ ...

  5. effective c++ resources

    http://www.cnblogs.com/littlethank/archive/2011/12/15/2288787.html http://www.cnblogs.com/liao-xiao- ...

  6. 解读Unity中的CG编写Shader系列八(多光源漫反射)

    转自http://www.itnose.net/detail/6117338.html 前文中完成最简单的漫反射shader只是单个光源下的漫反射,而往往场景中不仅仅只有一个光源,那么多个光源的情况下 ...

  7. iOS9 UI Tests探索笔记

    UI Tests是什么? UI Tests是一个自动测试UI与交互的Testing组件 UI Tests有什么用? 它可以通过编写代码.或者是记录开发者的操作过程并代码化,来实现自动点击某个按钮.视图 ...

  8. assign() 方法

    assign() 方法可加载一个新的文档. 语法 location.assign(URL) <html> <head> <script type="text/j ...

  9. Android环境搭建中遇到的小问题

    有一认识的同学做Android,结果他们搭建环境出现问题,最后卡在了一关,因为听说自己学过Java,所以就... 最后,自己试了一下,结果将遇到的解决问题记下来了:(看到小绿人后自己也被Android ...

  10. 【python】入门学习(二)

    键盘读取字符串: name = input('What is your first name?').strip()print("Hello " + name.capitalize( ...