MVC

こちらOpenGL GUI Applicationを参考にMVCを使ったwindowsアプリに修正します。
まずは、OpenGLを使わずに単純なアプリで実験します。

ボタンを押した回数をカウントするだけのアプリ

MVC

  • Model
    • アプリケーションのデータを動作を定義
    • View、Controllerから完全に独立している。
  • View
    • Modelからデータを取り出して、画面に出力。
  • Controller
    • ユーザからのイベントに応答し、データの修正、画面の更新要求を行う。

※一般的なMVCとは異なります。

Model

  • アプリケーションに必要なメンバとメソッドを記述します。
class HogeModel
    : public Model
{
private:
    int count;

public:
    HogeModel()
        : count( 0 )
    {}

    void inc()
    {
        count ++;
    }

    int get()const{ return count; }
};

View

ボタンとラベル、Modelからデータを取得するためのModel*を持たせます。
Modelは参照カウンタを持っているので、ctor/dtorでaddref/releaseを行います。
コントロール(ボタン、ラベル)の初期化はinit_controlsで行います。
Controllerからの更新要求に対して、Modelからデータを取得しラベルを更新します。

class HogeView
    : public windows::View
{
private:
    HogeModel* model;
    windows::Button button;
    windows::Label label;

public:
    HogeView( HogeModel* model )
        : model( model )
    {
        model->addref();
    }

    ~HogeView()
    {
        model->release();
    }

public:
    void init_controls( HWND handle )
    {
        button.create( handle,
            _T( "button" ),            //title
            1000,                      //ボタンのid
            recti( 10, 10, 10 + 100, 10 + 30 )    //ボタンの矩形
            );
        label.create( handle, _T( "label" ), recti( 120, 10, 120 + 100, 10 + 30 ) );
    }
public:
    void update()
    {
        tstringstream ss;
        ss << model->get();
        label.set_text( ss.str() );
    }

};
  • tstringstream
typedef std::basic_stringstream< TCHAR > tstringstream;
  • recti
typedef rect< int > recti;

Controller

メッセージに対応するメソッドを実装します。

  • create
  • command
    • ボタン押下時の処理
class HogeController
    : public windows::Controller
{
private:
    HogeModel* model;
    HogeView* view;

public:
    HogeController( HogeModel* model, HogeView* view )
        : model( model )
        , view( view )
    {
        model->addref();
        view->addref();
    }

    ~HogeController()
    {
        model->release();
        view->release();
    }

public:
    int create()
    {
        view->init_controls( handle );
        return 0;
    }

    int command( int id, int /*event*/ )
    {
        if( id == 1000 )
        {
            model->inc();
            view->update();
        }
        return 0;
    }
};

WinMain

int APIENTRY _tWinMain(
    HINSTANCE /*hInstance*/,
    HINSTANCE /*hPrevInstance*/,
    LPTSTR /*lpCmdLine*/,
    int /*nCmdShow*/
    )
{
    //メモリリーク時にアウトプットウィンドウに情報を出力する
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

    //MVC生成
    HogeModel* model = new HogeModel();
    HogeView* view = new HogeView( model );
    HogeController* controller = new HogeController( model, view );

    //ウィンドウ生成
    windows::CreationParam param = 
    {
        400, 300,                    //width, height
        _T( "クリック回数をカウント" ), _T( "hoge" ),	//title, class_name
    };
    if( windows::Window* window = windows::CreateDefaultWindow( param, controller ) )
    {
        windows::MessageLoop();

        window->release();
    }

    //MVC破棄
    controller->release();
    view->release();
    model->release();

    return 0;
}

プロシージャ

CreateWindowExの最後の引数にControllerを渡して、SetProp、GetProp、RemovePropでウィンドウに関連付けています。

    LRESULT CALLBACK Window::WndProc( HWND handle, UINT message, WPARAM wp, LPARAM lp )
    {
        if( message == WM_CREATE )
        {
            Controller* controller = reinterpret_cast< Controller* >( ( ( LPCREATESTRUCT )lp )->lpCreateParams );
            if( controller )
            {
                controller->set_handle( handle );
                LRESULT ret = controller->create();
                SetProp( handle, _T( "controller" ), controller );
                return ret;
            }
        }

        Controller* controller = ( Controller* )GetProp( handle, _T( "controller" ) );
        if( controller )
        {
            LRESULT ret = 0;
            switch( message )
            {
            case WM_COMMAND:
                ret = controller->command( LOWORD( wp ), HIWORD( wp ) );
                break;

            case WM_DESTROY:
                ret = controller->destroy();
                RemoveProp( handle, _T( "controller" ) );
                break;

            default:
                ret = DefWindowProc( handle, message, wp, lp );
            }
            return ret;
        }

        return DefWindowProc( handle, message, wp, lp );
    }


ということで、今回はMVCを扱ってみました。
MVCの詳細はネット上にたくさんあるので、そちらを参考にしてください。


次回はこのサンプルにOpenGLを追加していこうと思います。
OpenGLでシェーダを使いたかったのですが、随分と寄り道していますねw

今後はC#/Direct3DC++/OpenGLのセットで3D系を扱って行く予定です。