メインページ   名前空間一覧   クラス階層   アルファベット順一覧   構成   ファイル一覧   名前空間メンバ   構成メンバ   ファイルメンバ  

EventHandler.h の解説


名前空間

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 などを使用して別に実装してください。
 また、 C_WM_DESTROY のようにデフォルトの動作がついているものもあります。 デフォルトで十分なら、オーバーライドする必要はありません。

■コマンド系の使い方。
 C_WM_COMMAND や C_BN_CLICKED はアプリによって違う ID コマンドを処理します。
 そのため、他のメッセージとは使い方がちょっと違います。
1:まず継承時に ID をテンプレート引数に渡します。
	, 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 のようなハンドラクラスを作ることもできます。また、メンバ関数の登録制のような 他のハンドラシステムを組み込むこともできます。ハンドラクラスに面倒な部分を 隠蔽して、便利なハンドラ関数を作ってください。

■しくみ。
 基本は「チェーン」です。各ハンドラクラスはポインタで継ながっていて、 次々と CallHandler() を呼び出します。 CallHandler() でのチェックに 引っかかれば、オーバーライドされたハンドラ関数を呼び出すことになります。
  CallHandler() の呼び出しは基底クラスである CEventChain< type_EventData > の CallNext() が行います。この関数で処理することで、 CallHandler() は チェーンのことを気にする必要がなくなるというわけです。

 見ての通り、メッセージが送られてくるたびに各仮想関数を呼びだし if でチェックするという、とてもべたな方法で処理しています。これは switch によるものと比べればかなりの無駄があります。そのため、ゲーム などの早さ優先では問題となる場合があります。
 ですが、通常のアプリケーションで、メッセージが異常に多くない限りは、 まずストレスを体感できない早さは実現できるはずです。そして、べたな 方法を採用した見返りとして、とても見通しが良く、拡張性が高く、 使いやすいハンドラシステムを実現することができることでしょう。


KSCL(KAB-studioClassLibrary)に対してThu Jun 12 09:54:21 2003に生成されました。 doxygen1.2.15