名前空間 | |
namespace | KSCL |
■使い方。
使うのは CEventBegin<> と CEventEnd<> 、それと各メッセージハンドラクラスです。
まず、ハンドラメンバ関数を作成したいクラスは、これらから多重継承させます。
class CTestProc : public KSCL::CEventBegin<> , public KSCL::C_WM_INITDIALOG<> , public KSCL::CEventEnd<> {};
コンストラクタでは「チェーン」を継なげます。
コピーコンストラクタに「次のハンドラクラス::RetThis() 」の戻り値を渡します。
CTestProc::CTestProc() : T_CEventBegin( T_WM_INITDIALOG::RetThis() ) , T_WM_INITDIALOG( T_CEventEnd::RetThis() ) , T_CEventEnd() {}
ホントはただ this ポインタを渡したかったんですが、初期化子の中なので 警告が出てしまうのでこのようにしています。
T_CEventBegin などは KSCL::CEventBegin<> などと同じで、 KSCL::CEventBegin<> などの中で自クラスに typedef されて います。これもコンパイルエラー回避用の方法です。
あとは各メッセージハンドラクラスのハンドラメンバ関数をオーバーライドします。
オーバーライドすべき関数はハンドラクラスに純粋仮想関数として作ってあるので それをコピーしてください。
第1引数にはメッセージや wParam などのイベントデータ、 第2引数以降には各イベント用の引数が渡されます。これは MFC のものに 似せてあります。
int CMainProc::ON_WM_INITDIALOG ( type_const_CEventData_Ref p_rcData , HWND p_hWnd ) throw() { return 0; }これで終わり。あとはウィンドウプロシージャから CEventBegin<>::ChainStart() を呼び出せば、 メッセージが各ハンドラのチェックに回されて、引っかかればオーバーライドしたハンドラ関数が 呼び出されるというわけです。
int CMainProc::DialogProc( HWND p_hDlgWnd, UINT p_uiMsg, WPARAM p_wParam, LPARAM p_lParam ) { return ChainStart( p_hDlgWnd, p_uiMsg, p_wParam, p_lParam ); }あ、このダイアログプロシージャは KSCL::ADialog などを使用して別に実装してください。
, public KSCL::C_WM_COMMAND< ID_CMD_TITLE >2:次にクラス宣言の中で typedef しておきます。
typedef KSCL::C_WM_COMMAND< ID_CMD_TITLE > T_ID_CMD_TITLE;3:チェーンはこの typedef したのを用いて行います。
, T_CEventBegin( T_ID_CMD_TITLE::RetThis() ) , T_ID_CMD_TITLE( T_CEventEnd::RetThis() ) , T_CEventEnd()4:ハンドラ関数のオーバーライド時には、「最後の引数」をこの typedef したもののポインタにします。
virtual void ON_WM_COMMAND ( type_const_CEventData_Ref p_rcData , HWND p_hCtrlWnd , WORD p_wNotifyCode , T_ID_CMD_TITLE *p_Dummy = NULL );この引数の違いでオーバーロードされることで、 ID の違いがハンドラ関数の 違いに反映されます。つまりこの系統のメンバ関数を見分けるときには最後の引数の 違いを見分けることになります。
■作り方。
見ての通り、ハンドラクラスはほとんどのメッセージのものは作ってないので、 実際には必要なメッセージのハンドラクラスを皆さんが作ることになります(爆)。
作り方は簡単、適当なハンドラクラスをコピーして、書き換えればいいだけです。
書き換えるポイントを紹介します。
// WM_INITDIALOG // まずメッセージ名。まあこれはコメントだから好きずきで。 template< class type_EventData = CEventData > class C_WM_INITDIALOG // クラス名。もちろん変更します。 : public CEventChain< type_EventData > { enum { c_iMsg = WM_INITDIALOG // これが分岐のキーになるメッセージです。 }; typedef C_WM_INITDIALOG<> type_This; // ここにクラス名が出てきます。 typedef CEventChain< type_EventData > type_Parent; public: typedef type_This T_WM_INITDIALOG; // これは派生クラスで使う自クラス型です。 // これも書き換えます。 public: //! ・コンストラクタ。 C_WM_INITDIALOG( CEventChain< type_EventData > *const p_pcNext ) throw() // ここにもクラス名。 : type_Parent( p_pcNext ) {} private: virtual long CallHandler( const type_EventData &p_rcData, bool &p_rbIsHandled ) throw() { // ここでキーを使って分岐を行います。 // 必要ならもっと複雑なチェックを行います。 if( p_rcData.GetMsg() != c_iMsg ) { return 0; } p_rbIsHandled = true; // 処理したという印です。これをしなければ、次のチェーンへとそのまま進みます。 return KTL::b2B( ON_WM_INITDIALOG( p_rcData, p_rcData.m_hWnd ) ); // ハンドラ関数を呼び出します。 // この呼び出し部分は、ハンドラ関数の引数や戻り値によって大きく変える必要があります。 // また、特殊な前処理&後処理を行うこともできます。 } protected: // この下のがハンドラ関数です。これもメッセージによって変えます。 // この引数は、自分の使いやすいように変えてしまいましょう。つまり必要な引数が渡されるように // するということです。 wParam などのキャストは危険なので、こういった所に隠蔽すべき、という // 概念に基づいています。 virtual bool ON_WM_INITDIALOG( type_const_CEventData_Ref p_rcData, HWND p_hWnd ) throw() = 0; };ハンドラ関数を特殊化できるようにするために、ハンドラクラスは存在しています。だから、 C_DRAW のようなハンドラクラスを作ることもできます。また、メンバ関数の登録制のような 他のハンドラシステムを組み込むこともできます。ハンドラクラスに面倒な部分を 隠蔽して、便利なハンドラ関数を作ってください。