cexer

cexer
posts - 12, comments - 334, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

自己写的一个GUI框架的消息机制

Posted on 2008-08-06 17:54 cexer 阅读(6962) 评论(39)  编辑 收藏 引用 所属分类: GUI

转帖请注明出处 http://www.cppblog.com/cexer/archive/2008/08/06/58169.html

  我看过一些开源 GUI 框架的源代码,包括声句显赫的 WTL,win32gui 和 SmartWin,还有一些不知名但很优秀的,包括 jlib2( java AWT 在 C++ 上的移植  ),FLTK (比较小跨平台),甚至还曾鼓起勇气去看过 QT 那 n 万行的代码(当然没看明白)。

  其中我个人觉得最好的并不是 win32gui 或者 SmartWin,WTL 这些“名”库,而是那个名不见经传的 AWT c++ 移植版本的 jlib2--AWT 的设计相当地优雅。SmartWin 则多少有点名不附实,里面的尖括号(模板)多到让人难以忍受的程度,实际上很多是不需要的,我在自己的框架当中只用了少量的模板就完成了 SmartWin 要完成的一部分功能。win32gui 是标准库风格的代码风格,看起来舒服,可是那接口也太古怪。

  我想 SmartWin 和 win32gui,WTL 这些库那么出名,更多地是因为它们将一些非常有创意的手法运用到了 GUI 框架当中,想必以后的 GUI 框架都会多少受其影响。

  看这些库都是为了“师夷长技“,因为我自己非常喜欢写 GUI 框架,没完没了反反复复地写。

  CPPBLOG 上关于 GUI 框架的东西不是很多,自己写框架的少之又少。GUI 框架是如此大而复杂的一个车轮,除了少数像我这样的偏执狂,谁会愿意累得七窍流血去制作一个可能一开动就会车毁人亡的车轮呢?  

  写大一些的框架,是一种锻炼同时也是挑战。GUI 框架更是如此,要写一个好的 GUI 框架,就必须深入到很多的领域 ,比如线程,内存管理,内核对象,图形处理等等,这些都是在程序设计当中不容易驾驭的东西,如果还要完成诸如 序列化,脚本接口 这些东西,GUI 框架涉及的领域可以说非常地深广了。在与这些东西打交道的过程当中,我觉得自己慢慢地成长了不少。

  不过写了数个版本,从来没有自己觉得满意的,想起以前的一个很牛的朋友说过:如果一个人看到自己很久以前写的代码仍然觉得满意,那么这个人要么很牛,要么很蹉,不过可以想像,大多有这样感觉的人都属于很蹉的那一部分,可见自己觉得不满意并不是坏事。一直写,就能一直进步,总有一天会以牛人的身分回头看看自己的代码,而仍能觉得很满意。

  我写日志的一大问题就是,每次都是废话说完一大堆,仍不知道怎么自然地进入正题,这一次让我要以强硬的态度开门见山地进入。

 

进入正题:

  介绍一下自己在写的一个 GUI 框架。

  这个框架是以前几个版本的进化版,它的消息处理机制比较有意思。下面是这个框架的消息映射部分的一个测试代码,从这个代码里可以看出来这个框架在消息处理上的一些创新。

   1: // cexer
   2: #include "../../cexer/include/GUI/GUI.h"
   3: #include "../../cexer/include/GUI/window.h"
   4: #include "../../cexer/include/GUI/message.h"
   5:  
   6: using namespace cexer;
   7: using namespace cexer::gui;
   8:  
   9: // cexer LIB
  10: #include "../cexerLIB.h"
  11:  
  12: // c++ std
  13: #include <iostream>
  14: using namespace std;
  15:  
  16:  
  17: class TestWindow:public Window
  18: {
  19:     CEXER_GUI_CLASS( TestWindow,Window );
  20:  
  21: public:
  22:  
  23:     TestWindow( ParentWidget* parent=NULL,LPCTSTR name=_T("") )
  24:         :Window( parent,name )
  25:     {
  26:  
  27:     }
  28:  
  29:     LRESULT onMessage( WidgetMessage<WM_DESTROY>& message )
  30:     {
  31:         ::PostQuitMessage(0);
  32:         wcout<<L"window destroyed"<<endl;
  33:         return message.handled(this);
  34:     }
  35:  
  36:     LRESULT onMessage( WidgetMessage<WM_CREATE>& message )
  37:     {
  38:         wcout<<L"window created"<<endl;
  39:         return message.handled(this);
  40:     }
  41:  
  42:     LRESULT onMessage( WidgetMessage<WM_SIZE>& message )
  43:     {
  44:         long clientWidth  = message.cx;
  45:         long clientHeight = message.cy;
  46:  
  47:         wcout<<L"window sized"<<endl;
  48:         wcout<<L"client width  is: "<<clientWidth<<endl;
  49:         wcout<<L"client height is: "<<clientHeight<<endl;
  50:  
  51:         return message.handled(this);
  52:     }
  53:  
  54:     LRESULT onCommand( SystemCommand<SC_CLOSE>& command )
  55:     {
  56:         wcout<<L"system command SC_CLOSE"<<endl;
  57:         return command.handled(this,0,false);
  58:     }
  59:  
  60:     LRESULT onCommand( SystemCommand<SC_MAXIMIZE>& command )
  61:     {
  62:         wcout<<L"system command SC_MAXMIZE"<<endl;
  63:         return command.handled(this);
  64:     }
  65: };
  66:  
  67:  
  68: int _tmain( int argc,TCHAR** argv )
  69: {
  70:     CEXER_GUI_THREAD();
  71:  
  72:     wcout.imbue( std::locale("") );
  73:  
  74:     TestWindow* window = new TestWindow();
  75:     if ( !window->create() )
  76:     {
  77:         wcout<<L"window creating failed"<<endl;
  78:         delete window;
  79:     }
  80:  
  81:     TestWindow child( window,_T("child") );
  82:     child.create();
  83:  
  84:  
  85:     return runGUIthread();
  86: }


消息自动映射

  不像 MFC,ATL 那样的 BEGIN_MESSAGE_MAP ,MESSAGE_HANDLER 之类的一大堆宏来凑成一个巨大函数,也没有像 QT 那样的 connect(xxxx,xxxx) 的显示连接信号和槽的代码,你在上面的测试代码里找不到任何的映射痕迹,但是它的确工作得非常好。在这个 GUI 框架当中,你只需要定义消息处理函数,框架就能自动地帮你映射。例如上面定义的函数:

   1: LRESULT onMessage( WidgetMessage<WM_DESTROY>& message )
   2: {
   3:     ::PostQuitMessage(0);
   4:     wcout<<L"window destroyed"<<endl;
   5:     return message.handled(this);
   6: }


  框架在遇到 WM_DESTROY 消息的时候,会自动地调用这个函数。

  框架当中的 WidgetMessageBase 和 WidgetCommandBase ,SystemCommandBase 等模板以基类的形式,给派生类提供消息自动映射的能力。WidgetMessage,WidgetCommand,SystemCommand 等模板则提供了所有消息的一般实现。利用这些模板,只需要很简单的步骤即能完成消息映射和处理。

  比如说要实现对消息 WM_CREATE 的处理,只需定义一个函数:

   1: LRESULT onMessage( WidgetMessage<WM_CREATE>& message )
   2: {
   3:     // do something
   4:     // ...
   5:  
   6:     return message.handled(this);
   7: }

  
  要实现对 ID 为 IDOK 的按钮的点击事件的处理,只需定义一个函数:

   1: LRESULT onCommand( WidgetCommand<IDOK,BN_CLICKED>& okClicked )
   2: {
   3:     // do something here
   4:     // ...
   5:  
   6:     return clicked.handled(this);
   7: }


  要实现系统菜单的关闭命令的处理,只需要定义一个函数:

   1: LRESULT onCommand( SystemCommand<SC_CLOSE>& command )
   2: {
   3:    // do something 
   4:     // ...  
   5:  
   6:     return command.handled(this);
   7: }


  对于命令消息( WM_COMMAND),可以进一步地利用派生进行命令消息的分类处理,例如可以定义一个针对所有按钮点击事件的消息模板:

   1: template<UINT t_id>
   2: struct ButtonClicked:public WidgetCommandBase<ButtonClicked<t_id,BN_CLICKED>,t_id,BN_CLICKED>
   3: {
   4:     Button* button;
   5:  
   6:     template<typename TWidget>
   7:     ButtonClicked( TWidget* widget,UINT mess,WPARAM wpar,LPARAM lpar )
   8:         :_BaseCommand( mess,wpar,lpar )
   9:         ,button( widget->child(reinterpret_cast<HWND>(lpar)) )
  10:     {
  11:  
  12:     }
  13: };


  用这个模板同样可以完成上面那个 ID 为 IDOK 的按钮的点击事件的处理,并且进一步地从消息当中分解出了按钮对象的指针:

   1: LRESULT onCommand( ButtonClicked<IDOK>& okClicked )
   2: {
   3:     Button* button = okClicked.button;
   4:  
   5:     // do something 
   6:     // ...
   7:  
   8:     return okClicked.handled(this);
   9: }


  这些消息函数的名字并不是一定得命名为 onMessage 和 onCommand。它们可以是任何合法的 C++ 函数名。例如还是那个按钮点击事件的处理函数可以写成:

   1: LRESULT buttonClicked( ButtonClicked<IDOK>& okClicked )
   2: {
   3:     Button* button = okClicked.button;
   4:  
   5:     // do something 
   6:     // ...
   7:  
   8:     return okClicked.handledBy(this,&Self::buttonClicked);
   9: }


消息的手动映射:

  在有的环境当中(例如以 XML 为模板运行过程当中的某个时刻动态创建界面),控件是动态创建的,显然这时候动态生成的控件 ID 不是一个编译期常数,因为这样的 ID 不能用于模板参数,所以无法使用消息的自动映射(自动映射需要控件的 ID 作为模板参数),这种情况下框架必须要提供手动映射的接口。

  这个框架同时对控件的命令消息(WM_COMMAND)和通知消息(WM_NOTIFY )提供了手动映射的接口。例如对按钮提供了 onClicked,onDoubleClicked,对编辑框提供了 onChange,onUpdate 等这些用于消息映射的函数对象,客户通过调用这些函数对象来实现手动映射(像onClicked 的这种函数对象其实本身并不处理消息,只是执行一次消息映射的动作)

  例如要处理名为 button1 和 button2  的按钮的点击事件:

   1: int buttonClicked( Button* button )
   2: {
   3:     // do something 
   4:     // ...
   5:  
   6:     return 0;
   7: }
   8:  
   9: LRESULT onMessage( WidgetMessage<WM_CREATE>& message )
  10: {
  11:     buttonChild("button1")->onClicked( this,&Self::buttonClicked );
  12:     buttonChild("button2")->onClicked( this,&Self::buttonClicked );
  13:  
  14:     return message.handled(this);
  15: }


  手动映射对消息处理函数的类型要求比较宽松,例如上面的函数 buttonClicked 的参数不局限为 Button* ,也可以是 Widget* 或 MessageListener*,或其派生路径上任何一种类型的指针。返回值则可以为任何类型。


消息参数分解

  这个框架除了消息自动映射,还能够很轻松地针对不同消息分解出消息携带的信息。例如上面的函数当中:

   1: LRESULT onMessage( WidgetMessage<WM_SIZE>& message )
   2: {
   3:     long clientWidth  = message.cx;
   4:     long clientHeight = message.cy;
   5:  
   6:     wcout<<L"window sized"<<endl;
   7:     wcout<<L"client width  is: "<<clientWidth<<endl;
   8:     wcout<<L"client height is: "<<clientHeight<<endl;
   9:  
  10:     return message.handled(this);
  11: }


  针对 WM_SIZE 消息进行了消息的分解,因此在消息处理函数当中,不用再为了获得 WM_SIZE 携带的信息(客户区的宽度和高度)而一遍一遍地写 LOWORD 和 HIOWRD ,而是直接就能从 WM_SIZE 消息参数的丙个成员当中获取到它们:

   1: WidgetMessage<WM_SIZE>::cx 
   2: WidgetMessage<WM_SIZE>::cy 

  有两种方法可以针对不同的消息以不同的方式分解参数,一个模板的特化。在这个例子当中,WidgetMessage<WM_SIZE> 实际上就是是对模板 WidgetMessage<UINT t_message> 的特化

  WidgetMessage<UINT t_message>模板的定义如下:

   1: template<UINT t_mess>
   2: class WidgetMessage:public WidgetMessageBase<WidgetMessage<t_mess>,t_mess>
   3: {
   4: public:
   5:     WidgetMessage( UINT mess,WPARAM wpar,LPARAM lpar )
   6:         :_BaseMessage(mess,wpar,lpar)
   7:     {
   8:  
   9:     }
  10: };


  其中的 WidgetMessageBase 模板是一个提供消息自动映射的基类,所有需要自动映射的消息均从此模板派生,并且WidgetMessageBase 保存了三个未经加工的原始参数:

   1:   UINT      message
   2:   WPARAM    wparam
   3:   LPARAM    lparam


  因此所有的 WidgetMessage 模板的非特化版本所携带的参数都是这样三个参数。可以通过特化的方式来从这三个参数当中进一步分解出想要的部分,测试代码当中针对 WM_SIZE 来进行特化的代码如下:

   1: template<>
   2: class WidgetMessage<WM_SIZE>:public WidgetMessageBase<WidgetMessage<WM_SIZE>,WM_SIZE>
   3: {
   4: public:
   5:     long cx;
   6:     long cy;
   7:  
   8:     template<typename TWidget>
   9:     WidgetMessage( TWidget* widget,UINT mess,WPARAM wpar,LPARAM lpar )
  10:         :_BaseMessage( mess,wpar,lpar )
  11:         ,cx( LOWORD(lpar) )
  12:         ,cy( HIWORD(lpar) )
  13:     {
  14:  
  15:     }
  16: };


  再举例针对 WM_PAINT 消息利用特化来分解参数:

   1: template<>
   2: class WidgetMessage<WM_PAINT>:public WidgetMessageBase<WidgetMessage<WM_PAINT>,WM_PAINT>
   3: {
   4: public:
   5:     Widget*     widget;
   6:     Canvas      canvas;
   7:     PAINTSTRUCT paintStruct;
   8:  
   9:     template<typename TWidget>
  10:     WidgetMessage( TWidget* w,UINT mess,WPARAM wpar,LPARAM lpar )
  11:         :_BaseMessage( mess,wpar,lpar )
  12:         ,widget(w)
  13:         ,canvas( ::BeginPaint(w->handle(),&paintStruct) )
  14:     {
  15:  
  16:     }
  17:  
  18:     ~WidgetMessage()
  19:     {
  20:         ::EndPaint(widget->handle(),&paintStruct);
  21:     }
  22: };


  这样就从 WM_PAINT 当中分解出了需要的参数。然后在消息处理函数当中可以这样使用:

   1: LRESULT onMessage( WidgetMessage<WM_PAINT>& message )
   2: {
   3:     message.canvas.drawText(100,100,_T("hello world"));
   4:  
   5:     return message.handled(this);
   6: }


  虽然看起来特化模板很麻烦,但是实际上这是“do it for once,use for ever“(麻烦一次,方便永远)的事。

  还有一个分解参数的方法,就是直接从模板 WidgetMessageBase 派生。比如利用派生的方式分解 WM_CREATE 的参数:

   1: struct WindowCreating:public WidgetMessageBase<WindowCreating,WM_CREATE>
   2: {
   3:     CREATESTRUCT* createStruct;
   4:  
   5:     WindowCreating( UINT m,WPARAM w,LPARAM l)
   6:         :_BaseMessage(m,w,l)
   7:         ,createStruct( reinterpret_cast<CREATESTRUCT*>(l) )
   8:     {
   9:  
  10:     }
  11: };


  现在 WM_CREATE 消息处理函数的样子是这样:

   1: LRESULT onMessage( WindowCreating& message )
   2: {
   3:     CREATESTRUCT* createStruct = message.createStruct;
   4:  
   5:     return message.handled(this);
   6: }


总结:

  世上没有完美的东西,自己写的东西在刚完成它的时候总觉得已经完美没有再优化的可能了,但是每一次回头再看,都会发现许多的不足。这个框架提供的消息处理机制很灵活和方便了,不过等过几天再来看它,一定会发现能改进的地方。

Feedback

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-06 22:52 by 陈梓瀚(vczh)
原来你是这么做的。我花了很大的功夫,把delphi的方法搬到了C++里面,不惜成本让『使用』方便。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-06 22:53 by 陈梓瀚(vczh)
当然其中一个目标就是可以在也不管消息。你的方法在工程上是很好的,不过用起来还是会囧的。譬如将你的方法用在ListView上的时候。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-06 23:04 by Sil
很不错啊,非常期待博主放出全部框架。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-06 23:15 by cexer
@陈梓瀚(vczh)
不知道你说的很囧具体是什么?这个框架的上一个版本封装了许多的控件,不过没有封装 ListView,所以不知道是否存在你说的问题,因为是实验性质的东西,还没有在大的工程上测试过。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-06 23:20 by cexer
@Sil
谢谢支持,不过还在写作当中。

# re: 自己写的一个GUI框架的消息机制[未登录]  回复  更多评论   

2008-08-06 23:43 by eXile
最成熟的 GUI 库当然是Qt, 你的设计还不错,不过感觉还是native win32 的思维方式,没有呈现出更高介的抽象,

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 08:44 by Strive
以前就见他们写GUI,一直没欣赏到代码,现在好了,有人给出做法了。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 09:17 by Kevin Lynx
@eXile
我亦觉得,楼主局限于native win32的思维方式。在对消息的处理上,遵从的是WIN32的消息机制。

不过从代码看来(LRESULT之类),这个框架可能没有考虑跨平台特性。所以停留在WINDOWS上也没什么。

个人拙见。:)

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 09:19 by cexer
@eXile
你说它没有更高阶的抽象,大概是因为你在代码里看到了一些 windows 上的类型。不过类型都是可以 typedef 的,消息值是可以 define 的。这个框架虽然是针对 windows 平台的,写这个框架的目的是尝试一种新的方式,对所有消息驱动的系统都是适用的,包括非 GUI 系统,或非 windows 平台。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 12:27 by Touchsoft
@eXile
LRESULT,这些只是typedef的,我觉得不会影响其适用性。
我倒对楼主的事件处理机制感兴趣:)

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 14:40 by RISE
博主什么时间开放代码?

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 16:15 by 0517

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 19:18 by megax
LPCTSTR, LRESULT这些都是可以定义的,一个文件就搞定了。问题是你做出来的东西的入门容易度,我以前也写过一些封装,在消息操作系统中,其实就是消息的封装。封装消息还是有很高的技巧性的。MFC单纯用代码和宏来做,觉得也挺经典的

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-07 19:42 by cexer
@megax
这种方式的消息处理,要比 MFC 简单直接吧。你看到那么多的模板参数,最终的使用者是不会接触到的,都是框架用于扩展自身的。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-08 08:40 by yhm
没看明白,楼主的消息机制是怎么整的。

# re: 自己写的一个GUI框架的消息机制[未登录]  回复  更多评论   

2008-08-08 11:25 by eXile
@cexer
呵呵,也许我没有表达清楚,我说的“局限于native win32的思维方式”,是指局限于win32本身的GUI组件设计,而没有利用OOP技术和模式进行更好的组件与消息封装策略,也就不能消除win32 GUI 设计本身的复杂性。这种设计到顶,也不过是一个改良版的WTL(可以参考 http://www.winxgui.cn/)。当然,它可以适用在性能要求较高的场合,这个,取决于楼主的目标了。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-08 13:07 by cexer
@eXile
这倒是的.你说的这个问题,JLIB2(一个 C++ 版的 AWT )就解决得很好,因为设计方式都是 java 的,所以和 WTL,MFC 之类的大大的不同.不过它大量地使用虚函数,存在性能问题.WinX 这个库的代码我也看过,它在 GUI 上几乎没有任何的创新,不能说它是一个独立的 GUI 框架.只能说是对 WTL 的小修补.

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-09 02:50 by 陈梓瀚(vczh)
WinX好像官方也说就是对WTL进行扩展的吧。我目前也在封一个GUI,想把它变成现在RAD们的外观。当然我只care外观而已。譬如要求event可以bind和unbind全局函数或者成员函数等,这个已经做了。vista底下的控件有31个WindowClass。为什么我不想用消息呢,因为控件所有的私有事件全都是通过WM_COMMAND和WM_NOTIFY发送给parent control的,所以到时候用起来会特别恶心。

不过我的GUI原本的目的是给我自己用的,到时候也把代码开了吧。不过还要一段时间,现在才搞定了1/5的控件,封这个很惨的。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-09 11:52 by cexer
@陈梓瀚(vczh)
将event bind 到其它类的成员函数,或者全局函数,以前都实现过,不过不是很实用,干脆去掉了.对于全局函数,如果不是无参数的函数,那么给它提供运行时的参数是一件麻烦的事.因为设计框架的时候,并不知道用户会提供些什么类型的全局函数,如果把这件事给用户提供,用起来比较麻烦,接口看起来不简单明了.如果只允许提供特定的参数数目的类型,倒是很简单,不过就不实用了,所以后来就去掉了.
WM_COMMAND 和 WM_NOTIFY ,或者是其它消息的细节,框架都应该封装起来,不让最终的用户接触.

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-09 18:53 by 陈梓瀚(vczh)
不理解为什么『如果不是无参数的函数,那么给它提供运行时的参数……』event handler是拥有多种类型的,不是一个单独的类。就像函数指针一样,需要什么参数就声明什么参数的event handler。我把c#的方法搬到了c++来主要目的是为了将来GUI Editor好写。

不过我后来用宏来声明event handler了,虽然样子并不好看。我把listview的代码贴出来了,你看一下应该会了解我是怎么弄的吧。一切为了GUI Editor,囧……

制造轮子其实跟受罪没啥区别,可惜不得不做捏……

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-09 19:35 by cexer
@陈梓瀚(vczh)
能够亲手制造一个漂亮性感用起来爽的轮子,再苦再累也值得啊.
你的意思是一个针对每一个event,都写一个handler类?
多谢提供代码,等我的框架有个成熟稳定了,也打算开放代码.

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-10 00:11 by 陈梓瀚(vczh)
每一种不同参数的event都写一个类,但是我写了一对宏来高度简化了这个过程,工作量已经可以忽略不计了。

# re: 自己写的一个GUI框架的消息机制[未登录]  回复  更多评论   

2008-08-11 10:45 by c++
对这个话题非常感兴趣!
我是个mfc程序员,对C++ GUI framework认识仅限在mfc,但是觉得这个里面有很多技巧可言,而且现在可以做什么事情!

希望看到楼主的更多文章,还有vczh

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-14 22:09 by jxfwinter
关键问题你没有提到,窗口消息处理函数生成和注册.
ATL/WTL是MESSAGE一系列的宏来生成消息处理函数,通过thrunk机制把预先注册的函数替换成真正的消息处理函数.
MFC较为霸道,通过消息钩子来处理.
SMARTWIN则是通过嵌入一个this指针然后在消息处理函数中强制函数类型转换来处理的,虽说比较符合c++规范,但却是比较低效的.

你的子类中没有生成窗口处理函数,猜测应该和SMARTWIN的做法一样.

还是ATL/WTL的方法最为高效.而且我觉得WTL的代码和STL的风格一样,虽说是一个框架(尽管WTL之父说不是),但却非常灵活,易于扩展.

目前我所看到的GUI库(QT没看过),WTL个人认为是最好的.

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-16 00:45 by cexer
@jxfwinter
你没有看明白?
这个框架,函数定义即注册,只需要定义函数即可,已经用不着像 MESSAGE_HANDLER 宏,或者类似功能的东西了。这也是与一般的框架最大的不同。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-16 03:38 by jxfwinter
不是我没看明白,是你没懂我说的话,定义即注册是因为你在基类中已经预先注册过了,在窗口中嵌入一个this指针和子类的方法联系起来,每次消息来了都取出嵌入的指针,达到调用子类方法的目的,我猜你的做法是这样,也就和SmartWin做法一样.
你上面所放出的代码只是给出了你如何进行消息解码,和ON_MESSAGE之类的宏的作用是一样的,不过你是用模板实现.

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-16 16:40 by cexer
@jxfwinter
这个东西解释起来比较麻烦,我也不知道到底你是不是理解的和我想的一样。和 SmartWin 不大一样。定义即注册有两个意思,一是如果定义了函数则进行注册,二是如果没有定义函数就不注册,并不是像你说的在基类里预先注册过了。想像一下几千个消息,要一个个注册多麻烦。而且预先注册的方法,将会限制消息处理函数的名字。有时间我会把这个消息机制写几篇日志出来分析一下实现原理。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-19 09:12 by minji
每个函数都是不同的类型,楼主是怎样实现映射和分派的?

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-19 09:30 by cexer
@minji
我会把我的心得和方法写几篇日志出来,到时你看看吧。三言两语也不能说清楚。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-25 21:44 by proguru
cexer,把你的消息分派原理分析一下吧,把关键代码贴出来看看。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-08-26 10:23 by cexer
@proguru
最近就会写的。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2008-12-04 17:33 by 温辉敏
很棒的尝试,GUI的消息机制其实也可用于非GUI的模块间进行消息的传递,做得好可以让各个模块都不知道其它模块的存在,每个模块是个独立的软件模块。只知道自己要抛出什么消息,要处理外界抛出的什么消息,达到这种程度也可以做到类似插件那样可以很方便的加入新的模块,就像winamp等可以灵活加入新的插件一样。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2009-03-17 12:47 by dulton@gmail.com
很欣赏你用模板特化的方法实现了windows消息的参数分解这种手法。

不过我感觉你的框架跟很多框架一样。都是实现了观察者模式。注册后等待消息。特色就在于用模板实现了框架。而没有用继承。

没有贴出消息路由代码来。所以对于消息路由不敢妄加评论。消息如果没有一个合适的路由的话是搞不成成熟的GUI的。

拙见。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2009-09-14 19:54 by Loaden
我认为你的方法如果不是在基类中已经定义,那是无法实现的!
能否帖出核心代码?

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2009-11-02 15:43 by 大车
呵呵 我也在造车轮,我打算造一个跨平台的,跨语言的,纯粹是逻辑上的。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2009-11-03 17:36 by 溪流
这种使用方式看上去还是很爽的
同求消息机制

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2010-02-22 20:12 by lxw
这个方法我曾想过,总算碰到同仁了。不过有个悲剧的事实,windows有成百上千个消息,而模板是静态的,意味你要有成百上千个switch,每个switch对应一个消息,像Message<message>这样是无法编译的,就算你那样用,编译这么多的模版感觉要蛮久的时间。。

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2010-06-03 09:36 by ccsdu2009
GUI 框架是如此大而复杂的一个车轮,除了少数像我这样的偏执狂,谁会愿意累得七窍流血去制作一个可能一开动就会车毁人亡的车轮呢?  
比喻形象生动啊

# re: 自己写的一个GUI框架的消息机制  回复  更多评论   

2013-08-10 22:10 by sinian
有源码么呵呵

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理