• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            cexer

            cexer
            posts - 12, comments - 334, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
            轉(zhuǎn)帖請注明出處 http://www.shnenglu.com/cexer/archive/2008/08/07/58262.html


              有時候在界面上的一系列相關(guān)控件,它們作為一組相互協(xié)作提供一個功能,則在事件處理的時候,給這一組的控件僅提供一個事件處理程序,要比給每一個單獨的控件都提供一個事件處理程序要簡單得多,邏輯也更清楚。一個簡單的例子就是 windows 自帶的計算器程序界面上的按鈕 1,2,……,9。

              未命名

              一般來說,這樣的控件的 ID 都定義是連續(xù)的,如果是這樣,那么 GUI 框架就有可能提供這樣一個接口,客戶端只需要對這個接口提供控件組的開始 ID 和 結(jié)束 ID (以及通知消息的 ID),GUI 框架就能自動地把這一組控件的消息映射到某一個消息處理函數(shù)。

            MFC:

              MFC 提供了這樣一個接口,就是 COMMAND_RANGE_HANDLER 宏。使用方法很簡單,用計算器的按鈕作為例子,假設(shè)計算器的數(shù)字按鈕從 0 到 9 的 ID 分別定義為從 IDB_0 到 IDB_9的十個常數(shù),并且它們連續(xù),遞增的。先定義一個消息處理函數(shù):

               1: void numberClicked( UINT id,UINT code )
               2: {
               3:     int number = id - IDB_0;
               4:  
               5:     // do something else
               6:     // ....
               7: }


              然后就以用宏來將數(shù)據(jù)按鈕的消息映射到這個函數(shù)了:

               1: COMMAND_RANGE_HANDLER( IDB_0,IDB_9,numberClicked )

             

            Win32GUI:

              Win32Gui 也提供了一這樣的接口,使用方法如下(不過這個消息處理函數(shù)好像無法知道到底是誰被點擊了):

               1: handle_event on_number_clicked() 
               2: {
               3:     // don't know who's been clicked
               4:     // do something 
               5:     // ...
               6:  
               7:     return command_range<IDB_0,IDB_9,BN_CLICKED>().HANDLED_BY(&me::on_number_clicked);
               8: }


            SmartWin++:

              SmartWin++ 沒有提供這樣的接口,所以如果要處理那些數(shù)字按鈕的點擊事件就比較麻煩,必須得這樣:

               1: void nubmerClicked( WidgetButtonPtr button )
               2: {
               3:     int number = button->getID() - IDB_0;
               4:  
               5:     // do something else
               6:     // ....
               7: }
               8:  
               9: void initAndCreate()
              10: {
              11:     button0->onClicked( &This::numberClicked );
              12:     button1->onClicked( &This::numberClicked );
              13:     //.....
              14:     button9->onClicked( &This::numberClicked );
              15: }


              如果按鈕數(shù)目比較多,比如說程序同時需要能輸入字母和數(shù)字,那么按鈕就至少有三十多個,這個情況下,除非是CTRL+C,CTRL+V 的愛好者,否則都不會愿意這樣一個個地手動映射。不過因為 ID 是連續(xù)的,當(dāng)數(shù)目太多的時候,可以利用循環(huán)來映射:

               1: void initAndCreate()
               2: {
               3:     for ( UINT i=IDB_0; i<=IDB_9; ++i )
               4:     {
               5:         WidgetButtonPtr button = this->subclassButton( i );
               6:         button->onClicked( &This::numberClicked );
               7:     }
               8: }

             

            QT:
              QT 類似 SmartWin,我沒有找到這樣的接口,用 QT 來實現(xiàn)將這些數(shù)字按鈕的點擊消息映射到消息處理函數(shù)的代碼類似這樣(似乎對于按鈕的 clicked() 信號,這樣多個按鈕的點擊事件用一個函數(shù)處理,在函數(shù)當(dāng)中也無法得知究竟是誰被點擊了,不過其它帶參數(shù)的信號大概有些是可以的):

               1: void numberClicked()
               2: {
               3:     // don't known who's been clicked
               4:     // do something
               5:     // ....
               6: }
               7:  
               8:  
               9: QPushButton* button0;
              10: QPushButton* button1;
              11: //....
              12: QPushButton* button9;
              13:  
              14:  
              15: connect( button0,SIGNAL(clicked()),this,SLOT(numberClicked()) );
              16: connect( button1,SIGNAL(clicked()),this,SLOT(numberClicked()) );
              17: //....
              18: connect( button9,SIGNAL(clicked()),this,SLOT(numberClicked()) );


            自己寫的 GUI 框架:

              今天下午我在用自己的 GUI 框架寫一個類似計算器測試程序的時候,發(fā)現(xiàn)了這種 Range 消息映射功能的重要性,于是看了看各框架對于這個功能的支持。

              自己的 GUI 框架不支持,不過研究了一下,寫了一個模板:

               1: template<typename TCommand,WORD t_firstId,WORD t_lastId,WORD t_code>
               2: class RangeCommandBase
               3: {
               4:  
               5: protected:
               6:  
               7:     typedef RangeCommandBase<TCommand,t_firstId,t_lastId,t_code> _BaseRangeCommand;
               8:  
               9: public:
              10:  
              11:     UINT    mess;
              12:     WPARAM    wpar;
              13:     LPARAM    lpar;
              14:     WORD    id;
              15:     WORD    code;
              16:     DWORD    data;
              17:  
              18:   // .....   
              19: }


              如果需要將一組控件(ID 從 t_firstId 到 t_lastId )的某個通知消息作統(tǒng)一處理(即使用一個特定的消息處理函數(shù)),只需要將消息類從這個模板派生即可,然后我寫了一個模板作為一般的“范圍 ID 的命令消息”類的實現(xiàn):

               1: template<WORD t_firstId,WORD t_lastId,WORD t_code>
               2: class RangeCommand:public RangeCommandBase<RangeCommand<t_firstId,t_lastId,t_code>,t_firstId,t_lastId,t_code>
               3: {
               4: public:
               5:     template<typename TWidget>
               6:     RangeCommand( TWidget*,UINT mess,WPARAM wpar,LPARAM lpar )
               7:         :_BaseRangeCommand( mess,wpar,lpar )
               8:     {
               9:  
              10:     }
              11:  
              12: };


              利用這個模板來實現(xiàn)用計算器程序當(dāng)中 IDB_0 到 IDB_9 的按鈕的點擊事件處理的代碼像這樣:

               1: LRESULT onCommand( RangeCommand<IDB_0,IDB_9,BN_CLICKED>& numberClicked )
               2: {
               3:     int number = numberClicked.id - IDB_0;
               4:  
               5:     // do something else
               6:     // ......
               7:  
               8:     return numberClicked.handled(this);
               9: }


              如果不想每次都寫 BN_CLICKED,那么可以從 RangeCommandBase 的基礎(chǔ)上派生,派生的時候提供給該模板 BN_CLICKED 作為 t_code 參數(shù)即可,比如:

               1: template<WORD t_firstId,WORD t_lastId>
               2: class RangeButtonClicked:public RangeCommandBase<RangeButtonClicked<t_firstId,t_lastId>,t_firstId,t_lastId,BN_CLICKED>
               3: {
               4: public:
               5:     template<typename TWidget>
               6:     RangeButtonClicked( TWidget*,UINT mess,WPARAM wpar,LPARAM lpar )
               7:         :_BaseRangeCommand( mess,wpar,lpar )
               8:     {
               9:  
              10:     }
              11:  
              12: };


              利用這個模板來實現(xiàn)用計算器程序當(dāng)中 IDB_0 到 IDB_9 的按鈕的點擊事件處理的代碼像這樣:

               1: LRESULT onCommand( RangeButtonClicked<IDB_0,IDB_9>& numberClicked )
               2: {
               3:     int number = numberClicked.id - IDB_0;
               4:  
               5:     // do something else
               6:     // ...
               7:  
               8:     return numberClicked.handled(this);
               9: }

            Feedback

            # re: 各 GUI 框架的 COMMAND_RANGE_HANDLER(范圍 ID 的命令消息統(tǒng)一處理)[未登錄]  回復(fù)  更多評論   

            2008-08-08 11:50 by eXile
            Qt中可以采用 QSignalMapper.

            void numberClicked(int id) {}

            QPushButton* button0;
            QPushButton* button1;

            QSignalMapper* map = new QSignalMapper(this);
            connect(button0, SIGNAL(clicked()), signalMapper, SLOT(map()));
            connect(button1, SIGNAL(clicked()), signalMapper, SLOT(map()));
            map->setMapping(button0, 0);
            map->setMapping(button1, 1);

            connect(map, SIGNAL(mapped(int)), this, SIGNAL(numberClicked(id)));

            # re: 各 GUI 框架的 COMMAND_RANGE_HANDLER(范圍 ID 的命令消息統(tǒng)一處理)[未登錄]  回復(fù)  更多評論   

            2008-08-08 11:55 by eXile
            上面有些錯誤:

            QSignalMapper* map = new QSignalMapper(this);
            connect(button0, SIGNAL(clicked()), map, SLOT(map()));
            connect(button1, SIGNAL(clicked()), map, SLOT(map()));
            map->setMapping(button0, 0);
            map->setMapping(button1, 1);

            connect(map, SIGNAL(mapped(int)), this, SIGNAL(numberClicked(int)));

            # re: 各 GUI 框架的 COMMAND_RANGE_HANDLER(范圍 ID 的命令消息統(tǒng)一處理)  回復(fù)  更多評論   

            2008-08-08 12:56 by cexer
            @eXile
            多謝,我對QT用得不多,我會把你這個方法更新到日志上去.

            # re: 各 GUI 框架的 COMMAND_RANGE_HANDLER(范圍 ID 的命令消息統(tǒng)一處理)  回復(fù)  更多評論   

            2008-08-09 02:52 by 陳梓瀚(vczh)
            凡是做GUI最終都還是要做GUI Editor的。讓他去生成代碼,所有問題都不是問題。

            # re: 各 GUI 框架的 COMMAND_RANGE_HANDLER(范圍 ID 的命令消息統(tǒng)一處理)  回復(fù)  更多評論   

            2008-08-09 11:56 by cexer
            @陳梓瀚(vczh)
            GUI 太復(fù)雜了,GUI Editor 不可能完成所有的工作.而且有了這些接口,GUI Editor 的相關(guān)部分要容易實現(xiàn)得多.

            # re: 各 GUI 框架的 COMMAND_RANGE_HANDLER(范圍 ID 的命令消息統(tǒng)一處理)  回復(fù)  更多評論   

            2008-08-09 18:50 by 陳梓瀚(vczh)
            事實上非編譯時期綁定的事件處理函數(shù)更加易于給GUI Editor提供便利。
            www.久久精品| 99久久国产主播综合精品| 亚洲国产日韩欧美综合久久| 久久国产精品免费一区| 久久天天婷婷五月俺也去| 亚洲国产精品久久久天堂| 久久99热只有频精品8| 久久伊人精品青青草原日本| 久久久久亚洲AV无码专区首JN| 久久精品九九亚洲精品| 久久国产V一级毛多内射| 久久精品国产亚洲AV不卡| 中文字幕久久欲求不满| 亚洲第一极品精品无码久久| 久久99久久成人免费播放| 人妻精品久久无码区| 久久综合九色欧美综合狠狠| 国产精品福利一区二区久久| 久久综合视频网| 欧美午夜A∨大片久久 | 久久av高潮av无码av喷吹| 99精品久久久久久久婷婷 | 亚洲精品97久久中文字幕无码| 久久亚洲AV成人出白浆无码国产| 久久久久综合国产欧美一区二区| 久久久久99精品成人片直播| 久久久精品人妻一区二区三区蜜桃| 久久99精品国产麻豆婷婷| 亚洲国产精品婷婷久久| 99久久免费国产精品热| 亚洲狠狠婷婷综合久久蜜芽 | 久久无码国产专区精品| 久久伊人中文无码| 久久青青草原精品国产不卡| 国产精品九九久久免费视频| 国产成人综合久久久久久| 91精品观看91久久久久久| 国产精品99久久久久久猫咪| 99久久综合狠狠综合久久| 国产精品丝袜久久久久久不卡| 国产ww久久久久久久久久|