delegate4
前回delegate3-C#風windowsアプリ - while( c++ );の続き。
日があいてしまったので、別エントリに書きます。
class.h
- アプリケーションで使用しているnamespace、クラス、列挙型の一覧を記述しておきます。
- shared_ptrで管理するクラスはdef_managed_classマクロで定義します。
#pragma once #include <xtl/shared_ptr.h> //自作boost風shared_ptr #include <xtl/bind.h> //自作boost風bind #include <xtl/delegate.h> //自作C#風delegate //shared_ptrで管理されるクラスの定義 #define def_managed_class( name ) class name; typedef xtl::shared_ptr< name > name##Ptr; // // アプリケーションのクラス一覧 // class System; def_managed_class( Component ); def_managed_class( Control ); def_managed_class( Form ); def_managed_class( Button ); class Event; class WindowEvent; class ActionEvent; typedef xtl::delegate< void ( Component*, WindowEvent& ) > WindowEventHandler; typedef xtl::delegate< void ( Component*, ActionEvent& ) > ActionEventHandler; #undef def_class
C#ではもっと深いクラス階層になっていますが、例によって必要なものだけ実装して行きます。
Component
class Component { public: Component(){} virtual ~Component(){} };
Control
Control.h
#pragma once #include <string> #include "Component.h" class Control : public Component { public: HWND hwnd; std::string text; int x, y; int w, h; ActionEventHandler click; bool created; public: Control(); virtual ~Control(){} public: //各コントロールの具体的な実装 virtual void Create( Control* /*parent*/ ){} public: //WindowEventを処理する virtual void ProcessActionEvent( ActionEvent& /*e*/ ){} };
Control.cpp
#include <pch.h> //プリコンパイル済みヘッダ #include "Control.h" Control::Control() : hwnd( 0 ) , text( "" ) , x( CW_USEDEFAULT ) , y( CW_USEDEFAULT ) , w( CW_USEDEFAULT ) , h( CW_USEDEFAULT ) , created( false ) { }
Form
Form.h
#include "Control.h" #include <list> class Form : public Control { friend class System; typedef std::list< ControlPtr > control_list; protected: control_list controls; //Form上に配置するコントロールリスト public: WindowEventHandler load; //Form生成時イベント WindowEventHandler closing; //Form破棄時イベント public: Form(); virtual ~Form(); public: //Formの破棄。別スレッドから呼び出すことは出来ない void Destory(); //Formの表示。Formを作るだけでは画面に表示されない void Show(); //control_listにコントロールの実体を追加する void AddControl( ControlPtr c ); private: //control_listに登録されたコントロールを実際に生成する void CreateControls(); private: //デフォルトのウィンドウプロシージャ void Default( Event& e ); //各Formのプロシージャ virtual void Proc( Event& e ); private: //WindowEventを処理する virtual void ProcessWindowEvent( WindowEvent& e ); //ActionEventを処理する virtual void ProcessActionEvent( ActionEvent& e ); private: //Formの生成 void Create( Control* parent ); };
Form.cpp
- 正直、Eventの扱いがイケてない気がする。。。
#include <pch.h> #include "Form.h" #include "WindowEvent.h" #include "ActionEvent.h" Form::Form() : Control() { } Form::~Form(void) { Destory(); } void Form::Destory() { DestroyWindow( hwnd ); } void Form::Show() { ShowWindow( hwnd, SW_SHOW ); } void Form::CreateControls() { control_list::iterator it = controls.begin(); while( it != controls.end() ) { if( !( *it )->created ) { ( *it )->Create( this ); } ++ it; } } void Form::AddControl( ControlPtr c ) { controls.push_back( c ); } void Form::Default( Event& e ) { e.ret = DefWindowProc( e.hwnd, e.msg, e.wp, e.lp ); } void Form::Proc( Event& e ) { if( e.msg == WM_CREATE || e.msg == WM_CLOSE /* || その他のWindowEvent */ ) { WindowEvent we( e ); ProcessWindowEvent( we ); e = we; }else if( e.msg == WM_COMMAND ) { ActionEvent ae( e ); ProcessActionEvent( ae ); e = ae; } } void Form::ProcessWindowEvent( WindowEvent& e ) { if( e.msg == WM_CREATE ) { if( !load.empty() ) { e.Consume(); load( this, e ); } }else if( e.msg == WM_CLOSE ) { if( !closing.empty() ) { e.Consume(); closing( this, e ); } } /* その他のWindowEvent */ } void Form::ProcessActionEvent( ActionEvent& e ) { if( e.msg == WM_COMMAND ) { control_list::iterator it = controls.begin(); while( it != controls.end() ) { ( *it )->ProcessActionEvent( e ); ++ it; } } } void Form::Create( Control* parent ) { //hwndはプロシージャ内で設定される /*hwnd = */CreateWindowEx ( 0 , "hoge" , text.c_str() , WS_OVERLAPPEDWINDOW , x , y , w , h , parent ? parent->hwnd : 0 , 0 , GetModuleHandle( 0 ) , this // プロシージャに渡す情報 ); if( hwnd )created = true; }
Button
- 普通のボタンです。
Button.h
#include "Control.h" class Button : public Control { public: Button() : Control() {} public: //ActionEventを処理する void ProcessActionEvent( ActionEvent& e ); public: //Buttonの生成 void Create( Control* parent ); };
Button.cpp
- ProcessActionEventでclickを呼び出します。
- IDは使わず、ボタンのウィンドウハンドルで識別しています。
#include <pch.h> #include "Button.h" #include "ActionEvent.h" void Button::ProcessActionEvent( ActionEvent& e ) { if( e.GetCtrl() == hwnd ) { if( !click.empty() ) { e.Consume(); click( this, e ); } } } void Button::Create( Control* parent ) { hwnd = CreateWindowEx ( 0 , "BUTTON" , text.c_str() , WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE , x , y , w , h , parent->hwnd , 0 //とりあえずIDは常に0 , GetModuleHandle( 0 ) , 0 ); if( hwnd )created = true; }
System
- プロシージャ、メッセージループなどのwindows特有の部分はpImplイディオムにしてみました。特に意味はないです。
- ウィンドウクラスの登録をもう少しうまく隠蔽したいのですが。。。
class System { public: static System& instance(); private: System(); System( System const& ); System& operator=( System const& ); public: void Release(); void Run( FormPtr Form ); void Exit( int exit_code ); public: bool DefRegisterClass(); bool RegisterClass( std::string const& class_name, WNDPROC proc ); private: class Impl; typedef xtl::shared_ptr< Impl > pImpl; pImpl impl; }; #define system System::instance()
System.cpp
#include <map> #include "System.h" #include "../Form/Form.h" #include "../Form/Event.h" /** * */ class System::Impl { private: std::map< HWND, Form* > form_map; public: void Add( HWND hwnd, Form* form ) { form->hwnd = hwnd; form_map.insert( std::make_pair( hwnd, form ) ); } void Remove( HWND hwnd ) { form_map.erase( hwnd ); } Form* Search( HWND hwnd ) { std::map< HWND, Form* >::iterator it = form_map.find( hwnd ); if( it != form_map.end() ) { return it->second; } return 0; } public: static LRESULT CALLBACK WinProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) { if( msg == WM_CREATE ) { //CreateWindowEx()の最後の引数(Form*)を受け取る LPCREATESTRUCT cs = reinterpret_cast< LPCREATESTRUCT >( lp ); Form* form = reinterpret_cast< Form* >( cs->lpCreateParams ); //ウィンドウハンドルをキーとするForm*マップに追加 if( form ) { system.impl->Add( hwnd, form ); form->CreateControls(); } } //ウィンドウハンドルをキーとしてForm*を検索 Form* form = system.impl->Search( hwnd ); if( form ) { //Formクラスが持つオーバーライド可能なプロシージャを呼び出す Event e( hwnd, msg, wp, lp, form ); form->Proc( e ); //ウィンドウが破棄された場合、Form*マップから削除 if( msg == WM_DESTROY ) { system.impl->Remove( hwnd ); } //イベントが消費されていればすぐに終了 if( !e.IsConsumed() )form->Default( e ); return e.GetResult(); } return DefWindowProc( hwnd, msg, wp, lp ); } public: bool RegisterClass( std::string const& class_name, WNDPROC proc ) { WNDCLASSEX wc = { sizeof( WNDCLASSEX ), CS_HREDRAW | CS_VREDRAW, proc, 0, 0, GetModuleHandle( 0 ), LoadIcon( 0, IDI_APPLICATION ), LoadCursor( 0, IDC_ARROW ), ( HBRUSH )GetStockObject( BLACK_BRUSH ), 0, class_name.c_str(), 0 }; if( !RegisterClassEx( &wc ) )return false; return true; } void MessageLoop() { MSG msg; while( GetMessage( &msg, 0, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } void Exit( int exit_code ) { PostQuitMessage( exit_code ); } }; /** * */ System& System::instance() { static System inst; return inst; } /** * */ System::System() : impl( new System::Impl ) {} void System::Release() { impl = pImpl(); } /** * */ bool System::DefRegisterClass() { return RegisterClass( "hoge", &Impl::WinProc ); } bool System::RegisterClass( std::string const& class_name, WNDPROC proc ) { return impl->RegisterClass( class_name, proc ); } /** * */ void System::Exit( int exit_code ) { impl->Exit( exit_code ); } /** * */ void System::Run( FormPtr Form ) { Form->Create( 0 ); Form->Show(); impl->MessageLoop(); } int main(); /** * */ int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) { #ifdef _DEBUG //メモリリークの検出 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); //コンソールの割り当て AllocConsole(); FILE* out = 0; freopen_s( &out, "CON", "w", stdout ); FILE* in = 0; freopen_s( &in, "CON", "r", stdin ); #endif //デフォルトのプロシージャを使って、ウィンドウクラスの登録。 system.DefRegisterClass(); int ret = main(); system.Release(); #ifdef _DEBUG std::cout << "Enterキーを押してください"; getchar(); //コンソール解放 fclose( out ); fclose( in ); FreeConsole(); #endif return ret; }
download
- win32_20081228.zip
- VC2008ExpressEdition用