Version 8.15
通知メッセージの受け方
「今回は MFC の方の最後として、通知メッセージの受け方を説明します」
『ってゆーかそれが目的だったんだよね、確か』
「そう、このあと SimpleDialog の方に戻って、 MFC を使わないでツリー
ビューコントロールを操作する方法を解説するから」
『そのための準備か今回までってことね』
「そゆこと。まずはと……ツリーのアイテムの追加はこんな感じ」
BOOL CMfcDialogDlg::OnInitDialog()
{
// 略。
// TODO: 特別な初期化を行う時はこの場所に追加してください。
HTREEITEM h1stItem
= m_cMainTree.InsertItem( "1番目のアイテム" );
m_cMainTree.InsertItem( "2番目のアイテム" );
m_cMainTree.InsertItem( "1−1番目のアイテム", h1stItem );
return TRUE;
}
『ほいほい』
「次に、ダイアログエディタで貼り付けてあるツリービューコントロール
IDC_TREE_MAIN の右クリックメニューから【イベント】を選んで」
『ボタンを押したときのメンバ関数を作るときと同じだね……』
「あ!」
『な、なに???』
「そうだ、それキャンセルして、【OK】ボタンで【イベント】選んで」
『ん? それがそのボタンを押したとき、でしょ?』
「そう、だから。今まで火美ちゃんは〈こうすればメンバ関数ができる〉
ってことでしか見てなかったけど、少し憶えたことあるからちょっと違う見
方できると思うよ」
『違う見方……あ、左側のとこに BN_CLICKED と BN_DOUBLECLICKED があ
る、これって通知メッセージだよね!』
「そう、通知メッセージ。 Version 8.10 ( No.152 ) でやったように、ボ
タンを押したときには WM_COMMAND と一緒に BN_CLICKED が通知メッセージ
として送られてきます」
『ここではいつも、 BN_CLICKED を選んで【追加と編集】してたよね』
「そうすると、 MFC を仕組みを使って、 WM_COMMAND が送られてきた時に
BN_CLICKED とボタンの ID の IDOK が送られてきたらメンバ関数を読み込
むように」
『プログラムを作ってくれる!』
「そゆこと」
『……でも、その辺の仕組みってどうなってるの? なんか裏でこそこ
そ?』
「まだ難しいからもう少し先になるけど、これはマクロ使ってます」
『マクロ?』
「 BEGIN_MESSAGE_MAP とか DECLARE_MESSAGE_MAP がコードにあるでしょ」
『うんある。これがマクロ?』
「そう。これが実は結構長いプログラムに置き換えられます」
『そっか、マクロだからそういうこともできるんだ』
「この置き換えられたプログラムにはメッセージが送られてきて、この中に
ON_BN_CLICKED とかのマクロを埋め込むと」
『ひっかかってメンバ関数が呼ばれる?』
「そゆこと。さっきのイベントのとか、 ClassWizard とか使うとこういう
マクロを VC がプログラムに書き込んでくれるんです」
『……ってことは、もうちょっと勉強すれば、この辺の仕組みも見て分かる
ようになる?』
「もちろん。その辺もちゃんと教えるからね」
『いつになるやら』
「ま、その辺は気長にね。話を戻して」
『ツリービューコントロールこのイベント?』
「そうそう、そっちの方の【イベントハンドラの新規作成】ダイアログを出
して」
『ほい。お、 NM_CLICK とかの通知メッセージと TVN_BEGINDRAG とかの通
知メッセージがある』
「 NM_ のは、 Version 8.11 ( No.153 ) で言った〈新しいコントロール〉
すべてに共通の通知メッセージ」
『マウスをクリックしたとかだね。 TVN_ はツリービューコントロールだけ
のってこと?』
「そう。これはコントロールごとに違うのがあるから。ちなみに、これが
MFC のだってことに注意」
『ん? 当たり前じゃない』
「もし MFC だけやってたら」
『あ、そういえば通知メッセージがここに出てるけど、通知メッセージのこ
とを知らなかったら……』
「 MFC は便利だけど、でもこういうふうに通知メッセージの知識は必要
だったりするから、何も分からなくても使えるってわけじゃないんだよね」
『ボタンの時は何も知らずに教えられたままに、だもんねー』
「時には MFC 使っててもこの辺の仕組みを知らないといけなかったりする
から」
『だからあたしはこういうことも教わってるわけね』
「そゆこと。じゃあ TVN_SELCHANGED を選んで【追加と編集】を選んで」
『ほい。 Sel Changed ってことは、選択してるアイテムを変えたときに送
られてくる通知メッセージ?』
「そう。他にも色々あるけど、これが分かりやすいと思って」
『ん、メンバ関数ができた。ボタンと同じね』
void CMfcDialogDlg::OnSelchangedTreeMain
(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
*pResult = 0;
}
「注、長いから改行しました」
『でー、なんか引数難しいね。ポインタっぽいけど』
「そうだね……これは結構難しいかも。っていうより、むしろ MFC 使わな
い方でやった方が分かりやすかったかも」
『なんですとー!?』
「まぁとりあえずこっちで見ておこうか。まず第1引数の NMHDR 構造体の
ポインタ。これは WM_NOTIFY の LPARAM として渡されるもので、この中に
通知メッセージの各種情報が入ってます」
『……だけど』
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
『ってなに? NMHDR って構造体が、 NM_TREEVIEW って……メッセージに
なってる?』
「これはかなり混乱の元かも。まず、 NM_TREEVIEW も構造体で、この中に
ツリービューコントロールの各種情報が入ってます」
『でも、 NMHDR と NM_TREEVIEW って構造体違うのにこんなことしていい
の?』
「これは大丈夫。 NM_TREEVIEW の最初のメンバ変数が NMHDR になってるか
ら」
『???』
「んー、これはメモリ上のイメージが必要かもね。メモリ上に NM_TREEVIEW
構造体が置かれてます」
[ NM_TREEVIEW ]
「この中には、最初に NMHDR 、次に UINT 、 TVITEM がふたつ、最後に
POINT 構造体が入ってます」
[[ NMHDR ][ UINT ][ TVITEM ][ TVITEM ][ POINT ]]
「この TVN_SELCHANGED の場合、実際には WM_NOTIFY の LPARAM には
NM_TREEVIEW へのポインタが渡されます。つまり、ウィンドウズがこの構造
体を持ってて、そのポインタだけ LPARAM に渡してくるってこと」
『それを NMHDR へのポインタにもできる……あー、つまり上のだと』
[[ NMHDR ]]
『って見るのと同じってこと? union みたく』
「そうそう、そういうこと。こういうのはホントは良くないんだけど、
WM_NOTIFY の仕組みがそうなってるから」
『これはそうしなきゃ使えないわけねー』
「まぁ、これは MFC だから LPARAM じゃなくて NMHDR へのポインタが渡さ
れちゃうからこうなってるけど、 LPARAM から NM_TREEVIEW へのポインタ
にキャストしてからなら NMHDR へはメンバ変数として使えるから」
『そっちの方が安全っぽいね』
「で、この NMHDR にはツリービューコントロールのウィンドウハンドルと
か入ってるんだけど、今回は使いません」
『なんで?』
「このメンバ関数が呼ばれたってことは、どのツリービューコントロールの
通知メッセージって分かるから」
『あー』
「だからその辺は調べずに、直接 m_cMainTree でツリービューコントロー
ルを操作すればいいだけ。というわけで実際にやってみましょう」
void CMfcDialogDlg::OnSelchangedTreeMain
(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
char pchText[256];
TVITEM stTvItem;
stTvItem.mask = TVIF_TEXT;
stTvItem.hItem = pNMTreeView->itemNew.hItem;
stTvItem.pszText = pchText;
stTvItem.cchTextMax = 255;
// 実際に受け取ります。
m_cMainTree.GetItem( &stTvItem );
TRACE( "%s\n", pchText );
*pResult = 0;
}
『あれ? どっかでみたよーな』
「 Version 8.13 ( No.155 ) の、アイテムの名前を取得するのとほとんど
同じ」
『違うのは…… TVITEM::hItem に入れてるとこだけだね』
「ここで NM_TREEVIEW の2番目の TVITEM に入ってる〈アイテムのハンド
ル〉を渡します」
『あ、おなじみの TVITEM だ』
「ふたつある TVITEM は、最初のは以前選択されてたアイテム、あとのは新
しく選択したアイテム」
『そういえば……こうやって面倒なことしてるってことは、この TVITEM の
pszText には、アイテムの文字列が入ってないんだ』
「入ってないんだよねー、残念ながら。ま、この方法自体は」
『分かってるから大丈夫だけどねー。最後の、っていうか第2引数の
pResult は?』
「 LRESULT は API の型のひとつで、戻り値に使うもの」
『 result って〈結果〉って意味だもんね。 L は?』
「 LPARAM の L と同じ」
『 LPARAM の L ってなんだっけ』
「……教えてないかも。この話は長くなっちゃうからパス。この LRESULT
のポインタを通して渡した値が、ウィンドウプロシージャの戻り値として返
されます」
「? 普通にこのメンバ関数の戻り値にすればいいんじゃないの?」
『実はイベントハンドラになるメンバ関数って戻り値は void って決まって
るんです』
「あらま。だからわざわざ引数で?」
『そゆこと。というわけで API だけのに戻ります!』
/*
Preview Next Story!
*/
『さらば MFC ……でも結構めんどいとこもあったね』
「まぁ、 API でするときの勉強も兼ねてたからね」
『全部 MFC でやったら簡単? あ、構造体とか使わないから』
「アイテムの追加とかはかなり簡単」
『ってことは次回からまた大変ね……』
「とりあえずこんな感じ」
『うわ、コードが長い!』
「というわけで次回」
< Version 8.16 API だけでツリーコントロール操作 >
『につづく!』
「長くても、細かい部分の組み合わせなんだけどね」
『そう見えるように慣れるようにしないとね』
「そゆこと」