Version 8.18
ディスパッチ
「前回は脱線したけど、今回は元に戻って、ツリービューコントロールの通
知メッセージについて見ていきます」
『なんか、この前はプログラミング全然しなかったし……』
「それじゃあ今回はばりばりしてみようか」
#include <Windows.h>
#include <Commctrl.H>
#include <stdio.h>
#include "resource.h"
// ダイアログの初期化。
BOOL OnInitDialog( HWND p_hDlgWnd )
{
TVINSERTSTRUCT stInsertItem;
stInsertItem.hParent = TVI_ROOT;
stInsertItem.hInsertAfter = TVI_LAST;
stInsertItem.item.mask = TVIF_TEXT;
stInsertItem.item.pszText = "1番目のアイテム";
HWND hMainTreeWnd
= GetDlgItem( p_hDlgWnd, IDC_TREE_MAIN );
HTREEITEM h1stItem
= (HTREEITEM)SendMessage
( hMainTreeWnd
, TVM_INSERTITEM
, 0
, (LPARAM)&stInsertItem
);
stInsertItem.item.pszText = "2番目のアイテム";
SendMessage
( hMainTreeWnd
, TVM_INSERTITEM
, 0
, (LPARAM)&stInsertItem
);
stInsertItem.hParent = h1stItem;
stInsertItem.item.pszText = "1−1番目のアイテム";
SendMessage
( hMainTreeWnd
, TVM_INSERTITEM
, 0
, (LPARAM)&stInsertItem
);
return TRUE;
}
// アイテムの選択が変更された時。
BOOL OnSelchangedTreeMain( NM_TREEVIEW *p_pstNmTreeView )
{
char pchText[256];
TVITEM stTvItem;
stTvItem.mask = TVIF_TEXT;
stTvItem.hItem = p_pstNmTreeView->itemNew.hItem;
stTvItem.pszText = pchText;
stTvItem.cchTextMax = 255;
SendMessage
( p_pstNmTreeView->hdr.hwndFrom
, TVM_GETITEM
, 0
, (LPARAM)&stTvItem
);
OutputDebugString( pchText );
OutputDebugString( "\n" );
return 0;
}
// OK ボタンが押されました。
BOOL OnOk( HWND p_hDlgWnd )
{
EndDialog( p_hDlgWnd, IDOK );
return TRUE;
}
// ダイアログプロシージャ。
BOOL CALLBACK DialogProc
( HWND p_hDlgWnd
, UINT p_uiMessage
, WPARAM p_wParam
, LPARAM p_lParam
)
{
if( p_uiMessage == WM_INITDIALOG )
{
return OnInitDialog( p_hDlgWnd );
}
else if( p_uiMessage == WM_NOTIFY )
{
if( p_wParam == IDC_TREE_MAIN )
{
NM_TREEVIEW* pstNmTreeView = (NM_TREEVIEW*)p_lParam;
if( pstNmTreeView->hdr.code == TVN_SELCHANGED )
{
return OnSelchangedTreeMain( pstNmTreeView );
}
}
}
else if( p_uiMessage == WM_COMMAND )
{
if( LOWORD( p_wParam ) == IDOK )
{
return OnOk( p_hDlgWnd );
}
}
return FALSE;
}
// WinMain() 。
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
DialogBox
( p_hInstance
, MAKEINTRESOURCE( IDD_MAIN )
, NULL
, DialogProc
);
return 0;
}
『……長すぎ』
「実はこれまでとほとんど変わらないんだけどね。前はダイアログプロシー
ジャに全部詰め込んでいたけど、それを他の関数に分けただけ」
『 OnOk() とかに?』
「そゆこと。実際にダイアログプロシージャを見てみると分かるけど」
『メッセージを受け取ったら、それに合わせて関数を呼び出すのね。 MFC
みたい』
「 MFC の方が高度だけどね。こういう仕組みを〈ディスパッチ〉って言い
ます」
『でぃすぱっち?』
「英語で dispatch 、受け取った各種情報を適切な場所へ送付することを言
います。あと送付する人のことを〈ディスパッチャー〉って言います」
『ってことはこのダイアログプロシージャがディスパッチャー?』
「そういうこと。ダイアログプロシージャ内に全部のコードを書くと」
『それはかなり汚いね……』
「だからそういう機能を外に出して、ダイアログプロシージャがメッセージ
を適切な関数へとディスパッチする」
『それがこれなのね』
「 MFC でハンドラ関数が呼ばれるのはこのディスパッチのシステムがしっ
かりできてるから。これって結構大きいかもね」
『たぶんあたしには作れない……』
「あ、もしかして言ってなかったかもしれないけど、これからも MFC は
使っていくからね」
『え? もう MFC って使わないんだと思った』
「 MFC のメリットは大きいからね。これまでの勉強は、 MFC を理解するた
めのものってこともあるから」
『確かに、 MFC と照らし合わせて、って多かったね』
「ま、最終的には MFC 使わない方向に行くだろうけど」
『やっぱし……』
「と、今回最後は、ちょっと難しそうな、通知メッセージの部分」
else if( p_uiMessage == WM_NOTIFY )
{
if( p_wParam == IDC_TREE_MAIN )
{
NM_TREEVIEW *pstNmTreeView = (NM_TREEVIEW*)p_lParam;
if( pstNmTreeView->hdr.code == TVN_SELCHANGED )
{
return OnSelchangedTreeMain( pstNmTreeView );
}
}
}
『これって、ツリービューコントロールの通知メッセージを受け取ってる部
分?』
「そう。まず、ツリービューコントロールなんかの〈新しい〉コントロール
は、通知メッセージが WM_COMMAND じゃなく WM_NOTIFY で送られてきま
す」
『その WPARAM に、通知メッセージを送ってきたコントロールの ID が入っ
てるのね。これは LOWORD いらないんだ」
「うん、 LPARAM まるごと使ってるから。で、この時点でツリービューコン
トロールの通知メッセージってことが分かっているので、 LPARAM に入って
る NM_TREEVIEW へのポインタを受け取ります」
『これってようするに Version 8.15 ( No.157 ) の
CMfcDialogDlg::OnSelchangedTreeMain() と同じことしてるんだよね』
「そうそう、 LPARAM から NMHDR のポインタへはこのメンバ関数が呼ばれ
る前にされちゃってるけど、してる事は同じ」
『で、変換して……?』
「 NM_TREEVIEW::hdr メンバ変数は NMHDR 型で、この中に通知メッセージ
の基本的な情報が入ってるんです」
『あ、 code って hdr のメンバ変数ね。これを見ると……通知メッセージ
そのものが入ってるんだ!』
「そゆこと。〈ツリービューコントロールの選択を変更した〉っていう意味
の TVN_SELCHANGED が入ってるかどうか確認してから、関数にディスパッチ
してるってわけ」
『そこまで調べなきゃ、間違ったメッセージをディスパッチしちゃうかもし
れないってことねー』
/*
Preview Next Story!
*/
「次回で今回のダイアログプロシージャの話も完結」
『仕組みは簡単だと思うんだけど、細かいとこ複雑だよね』
「それはこれからもそうかも」
『レベル高〜い』
「複雑なのを分解してひとつひとつ見ることが大事かもね」
『今回のディスパッチみたいに?』
「そゆこと」
『というわけで次回』
< Version 8.19 MFC を使わずにアイテムを取得! >
「につづく!」
『で、そのあとは?』
「今度は SDI !」
『 SDI ?』