Version 18.09
イベントごとにメンバ関数を分ける
「前回は、ダイアログプロシージャを純粋仮想関数にした理由や、どういう
仕組みで動いているのか、ということを説明しました」
『ってゆーか複雑!! なんだかパズルみたい』
「実際そういうところもあるかもね。 API や Windows システムの制約、
C++ 言語の仕様をうまく使わないとできないだろうから」
『むー』
「まぁいきなり書くのはかなり大変だろうから、色々な例を見て、それを
理解して、真似してみるところから始めてみるといいかな」
『まねしちゃっていいの?』
「もちろん。というか、僕が紹介しているものだって真似だし」
『げ、そうなの?』
「というわけで、今回は前回の DialogProc() メンバ関数を、 MFC を真似
てメンバ関数に分けてみます」
『おー』
「まずはプログラムから。 NewCalcDialog.h はこうなります」
// NewCalcDialog.h
// CNewCalcDialog クラス。
class CNewCalcDialog : public CDialog
{
// このダイアログのウィンドウハンドル。
HWND m_hWnd;
public:
// ダイアログプロシージャ。
BOOL DialogProc
( HWND p_hDlgWnd
, UINT p_uiMessage
, WPARAM p_wParam
, LPARAM p_lParam
);
// OK ボタンが押された時のイベントハンドラ。
void OnOk();
// = ボタンが押された時のイベントハンドラ。
void OnEqual();
};
『 m_hWnd メンバ変数が追加されたね。これって MFC の CWnd みたいな感
じ?』
「そう、当たり前だけどウィンドウハンドルはウィンドウやダイアログごと
に振られるから、引数で渡すよりもメンバ変数で持っておいた方がいいか
な」
『こんなところから MFC の真似なのね。あと OnOk() メンバ関数と
OnEqual() メンバ関数が増えたね』
「この 2つのメンバ関数に、 DialogProc() メンバ関数の機能を分割しま
す」
// NewCalcDialog.cpp
#include <Windows.h>
#include <stdio.h>
#include "resource.h"
#include "Dialog.h"
#include "NewCalcDialog.h"
// ダイアログプロシージャ。
BOOL CNewCalcDialog::DialogProc
( HWND p_hDlgWnd
, UINT p_uiMessage
, WPARAM p_wParam
, LPARAM p_lParam
)
{
// ウィンドウハンドルをメンバ変数に保存します。
m_hWnd = p_hDlgWnd;
if( p_uiMessage == WM_COMMAND )
{
if( LOWORD( p_wParam ) == IDOK )
{
// OK ボタンが押されました。
OnOk();
return TRUE;
}
else if( LOWORD( p_wParam ) == IDC_B_EQUAL )
{
// = ボタンが押されました。
OnEqual();
return TRUE;
}
}
return FALSE;
}
// OK ボタンが押された時のイベントハンドラ。
void CNewCalcDialog::OnOk()
{
// OK ボタンが押されました。
EndDialog( m_hWnd, IDOK );
}
// = ボタンが押された時のイベントハンドラ。
void CNewCalcDialog::OnEqual()
{
// 各エディットボックスのウィンドウハンドルを取得します。
HWND hLeftWnd = GetDlgItem( m_hWnd, IDC_E_LEFT );
HWND hRightWnd = GetDlgItem( m_hWnd, IDC_E_RIGHT );
HWND hAnswerWnd = GetDlgItem( m_hWnd, IDC_E_ANSWER );
// 各エディットボックス用文字列を用意します。
char pchLeft[256];
char pchRight[256];
char pchAnswer[256];
// IDC_E_LEFT と IDC_E_RIGHT の文字列を取得します。
GetWindowText( hLeftWnd, pchLeft, 255 );
GetWindowText( hRightWnd, pchRight, 255 );
// それぞれ int 型に変換します。
int iLeft = atoi( pchLeft );
int iRight = atoi( pchRight );
// 足した結果を pchAnswer に文字列変換します。
sprintf( pchAnswer, "%d", iLeft + iRight );
// それを IDC_E_ANSWER にセットします。
SetWindowText( hAnswerWnd, pchAnswer );
}
『つまり、 DialogProc() メンバ関数の機能は、ウィンドウハンドルの保存
と、メッセージのチェック、で、各メンバ関数の呼び出しだけして、残りは
各メンバ関数でしてるわけね』
「そういうこと。 DialogProc() メンバ関数では、まず m_hWnd メンバ変数
に引数で渡されたウィンドウハンドルを保存します」
// ウィンドウハンドルをメンバ変数に保存します。
m_hWnd = p_hDlgWnd;
『? これって毎回保存しなきゃいけないの?』
「ううん、実際には WM_INITDIALOG メッセージが送られてきたときに一度
保存すればいいかな。でもこうして毎回保存してもほとんど問題はないか
ら。当然だけど毎回同じウィンドウハンドルだし」
『その後は、メッセージとボタン ID をチェックして、ボタンごとに
メンバ関数を呼び分ける、ってするわけね』
if( p_uiMessage == WM_COMMAND )
{
if( LOWORD( p_wParam ) == IDOK )
{
// OK ボタンが押されました。
OnOk();
return TRUE;
}
else if( LOWORD( p_wParam ) == IDC_B_EQUAL )
{
// = ボタンが押されました。
OnEqual();
return TRUE;
}
}
「ここはこの前の DialogProc() メンバ関数と同じだから。あと
OnOk() メンバ関数と OnEqual() メンバ関数の中身もほぼ同じ。あ、ただ
ウィンドウハンドルは m_hWnd メンバ変数を渡すようにしているから」
// OK ボタンが押された時のイベントハンドラ。
BOOL CNewCalcDialog::OnOk()
{
// OK ボタンが押されました。
EndDialog( m_hWnd, IDOK ); ← m_hWnd になってます。
return TRUE;
}
『 p_hDlgWnd が m_hWnd になったわけね』
「ウィンドウハンドルはどのメンバ関数でも使うから、引数で渡すよりこう
してメンバ変数にした方がいいと思うよ」
『んー、 DialogProc() メンバ関数の他の引数、つかメッセージとか WPARAM
とか LPARAM は渡さなくていいの?』
「メッセージは必要ないでしょ」
『あ、そか。メンバ関数呼ばれたらそのときにもうメッセージは分かってる
んだ。 OnOk() メンバ関数が呼ばれたら、 OK ボタンが押された、ってわけ
だもんね』
「 WPARAM や LPARAM はイベントによって、かな。ボタンが押されたときに
は必要ないけど、たとえば Version 10.20 ( No.198 ) で説明した
WM_CHAR メッセージとかは」
『そか、 WPARAM にキーが入ってるんだよね、そういうのは渡した方がいい
んだ』
「ただ、この部分は自分で変えられるから、 WPARAM を直接渡してもいい
し、 char 型にキャストしてから渡してもいいかな。ちなみに MFC は
キャストしてから渡すものがほとんどだけど」
『んー、どっちがいいの?』
「その辺は好みの問題かな、今の段階だと DialogProc() メンバ関数も自分
で手を加えなきゃいけないし」
『どゆこと?』
「 MFC って、 DialogProc() メンバ関数に当たる部分は見えてないでしょ」
『うん、そういえば追加されたイベントハンドラ使うだけだね』
「 MFC の場合、 DialogProc() メンバ関数に当たる部分がうまく隠されて
いて、ここに手を加えなくてもイベントハンドラが呼び出される仕組みに
なっているんです」
『そ、そんなことできるの!? ボタンって好きなのを好きなだけ追加で
きるのに!』
「たとえば MFC でダイアログアプリケーションを作ると、こういうのが
xxxDlg.cpp にあるでしょ」
BEGIN_MESSAGE_MAP(CMfcDialogDlg, CDialog)
//{{AFX_MSG_MAP(CMfcDialogDlg)
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
『うん、あるある。……もしかしてここでその処理をしてるの?』
「そういうこと。 MFC の場合、 ClassWizard や AppWizard が自動的にこ
ういうマクロを追加してくれるから」
『そか、マクロってプログラム置き換えてくれるんだもんね』
「この仕組みのおかげでメッセージの分岐をしなくてよかったんだけど、
そういうツールがないから、イベントハンドラを追加するときには
DialogProc() メンバ関数に自分で追加していくしかないかな」
『やっぱ MFC 使わないと不便なとこは結構あるんだね……』
/*
Preview Next Story!
*/
『んー、やっぱり MFC 使った方が楽そう』
「便利なところはあるからね。でも API だけの方が自由度は高いから」
『でも作る所多いし、 CString とかもあるし』
「あ、そういうクラスなら MFC じゃなくてもあるよ」
『というわけで次回』
< Version 18.10 iostream と std::string >
「につづく!」
『って、なんだかどっかで聞いたようなクラスが!』
「復習かも」
『ええ〜?』