Version 5.25
ボタンを作ろう!
「さて、前回はコントロールもウィンドウって所まで見ていきました」
『そーそー。 CreateWindow() でボタンが作れるってゆーし』
「では実際に作ってみましょう!」
『ええっ!?』
// いつものメンバ関数。
void CFileTestDlg::OnBtnShow()
{
HWND hWnd
= ::CreateWindow
( "BUTTON"
, "てすと"
, WS_CHILD | BS_PUSHBUTTON | BS_TEXT
, 10, 10, 100, 30
, GetSafeHwnd()
, (HMENU)1050
, AfxGetInstanceHandle()
, NULL );
::ShowWindow( hWnd, SW_SHOW );
return;
// あとは取っておきましょう。
『ビルドして実行! うわ、ホントにダイアログにボタンができた! こん
なふうにしてボタンが作れるなんてなんか感動……』
「他の OS はこういうふうに作ること多いかな。ウィンドウズはダイアログ
エディタで簡単にできるようになってるんだけどね」
『なんかこの関数って呼ぶのめんどそうだし……』
「でも今の火美ちゃんの知識でも全然大丈夫だと思うよ。まず最初の引数」
『これはウィンドウクラス名だね。これが BUTTON だから』
「ボタンが作られたってわけ。ちなみにウィンドウクラス名は大文字小文字
区別しないから。たとえば今作ったボタンを Spy++ で見ると……」
『 Button になってる』
「そういうこと。2番目は〈キャプション〉」
『これはボタンに出る文字だね』
「ちなみに、普通のウィンドウだとタイトルバーの名前になります」
『ん、なんか複雑?』
「3番目はウィンドウスタイル。 WS_CHILD は〈子ウィンドウ〉って意味」
『子ウィンドウ? あ、 Ver 5.23 ( No.088 ) でやったね』
「ウィンドウには親子関係とかが作られてるから。それはちょっと飛ばして」
『 BS_PUSHBUTTON は CreateWindow() のリファレンスに書いてあるね。ボ
タンのスタイルなんだ』
「押しボタン式で、文字列を表示するってもの。たとえば BS_PUSHBUTTON
を BS_CHECKBOX に変えると」
『×ボタンのになった!! へー、ってことはこれはボタンの一種なんだ』
「そゆこと。こういうの知らないと、別のコントロールだと思っちゃうよね」
『うん、そうだと思ってた』
「第4〜第7引数はボタンの位置とサイズ」
『これで位置決めするのってめんどいよね。ダイアログエディタがあれば簡
単なのに』
「あと、ダイアログの左上からってことに注意」
『あ、ホントだ。画面全体の左上じゃないんだね』
「ウィンドウでこういうのの操作をするときには、その辺に注意した方がい
いかな」
『第8引数は CFileTestDlg::GetSafeHwnd() を呼んでるってことは……ダ
イアログのウィンドウハンドルだね』
「そう、ここではボタンの〈親ウィンドウ〉を指定してます」
『あ、ここでしっかり指定しないといけないんだ。自動だと思ってた』
「 API の世界に自動って言葉はないって思った方がいいかも。ここで親ウィ
ンドウを指定しているから、ボタンの位置がその親ウィンドウの左上からに
なるわけ」
『じゃーこれを他のウィンドウにしたら?』
「そのウィンドウの左上から」
『じゃあそれをリストボックスのにしたら?』
「リストボックスの左上から」
『なんか変……』
「ま、その辺が柔軟だから色々できるんだけどね」
『第9引数はなんか変だね。 HMENU にキャストしてる』
「普通のウィンドウだと、ここにはメニューのハンドルを指定します」
『だから HMENU ね』
「でもコントロールなんかの子ウィンドウの場合には ID を指定します」
『 ID って、あー IDC_BTN_SHOW とかだ!』
「そゆこと。それも整数値だったでしょ」
『そうそう、 resource.h の中で書かれてるんだよね』
「この ID もそれと同じ。ホントは」
『 #define とか const int とかでってゆーんでしょ?』
「そゆこと。第9引数はちょっと難しいけど、〈インスタンスハンドル〉っ
ていうのを渡してます」
『前にやったね、 Ver 3.6 ( No.031 ) とかで』
「これは一応、アプリのハンドルって考えればいいかな。これは
AfxGetInstanceHandle() で取得できるからそれを渡せばOK」
『第10引数は NULL だね』
「これは…… MSDN の CWnd::OnCreate() を見てみて」
『ほい見たよ』
「んーと、ちょっと戻って、 OnBtnShow() はボタンが押されたときに呼ば
れるメンバ関数でしょ」
『うんそだよ。 CWnd::OnCreate() もそういうの?』
「そういうの。これはウィンドウが作られる時に呼ばれるメンバ関数。で、
そのとき渡される CREATESTRUCT のページを」
『ほい』
「その第1引数は」
『 LPVOID 型の lpCreateParams ってゆーの』
「実は、これが CreateWindow() の第10引数そのものなんです」
『ええっ!? そーいえば同じ LPVOID 型だし……でも CreateWindow() の
リファレンス見るとなんか違くない? CREATESTRUCT のポインタを渡すっ
て読めるけど』
「げ! ……あー、これは訳が間違ってるんじゃないかな。 CreateWindow()
の英文リファレンスを見れば」
『 CREATESTRUCT を通して渡される、って読めるね』
「このポインタは、あるクラスへのポインタをこの第10引数から渡して、
CWnd::OnCreate() で受け取って初期化に使う、ってふうに使います」
『そっか、そうすればクラスの大きさとか関係なしに送れるんだね』
「この仕組みは API じゃよく使われてるから」
『でも、キャストし間違えたら……』
「 API はそういう部分多いからねぇ、使う時は気を付けないと」
『ここまででウィンドウ作りは終わりね。 ShowWindow() は、まんまウィン
ドウを表示させるのってことでいいの?』
「そう、ま、ホントはこの API はウィンドウを消したり最大化したりもで
きるんだけどね」
『やってみたい!』
「んじゃやってみましょう」
void CFileTestDlg::OnBtnShow()
{
ShowWindow( SW_SHOWMAXIMIZED );
return;
// あとは取っとく。
『あれ、最初のと引数が違うよ?』
「最初のは API 、これは MFC の CWnd::ShowWindow() メンバ関数」
『そーいえば、 :: が最初に付いてるってことは API だからなんだよね』
「そう。 ShowWindow() も CreateWindow() も同名のメンバ関数が CWnd に
あるからね。逆に、 CWnd のってことを明示するなら」
CWnd::ShowWindow( SW_SHOWMAXIMIZED );
『そっか、 ::ShowWindow() ってゆーのは〈どのクラスにも属してない〉っ
て意味なんだ!』
「そういうこと。じゃ、ビルドして」
『実行! うわ、ダイアログが画面いっぱいに!』
「これができるのも、ダイアログが基本的にはフツーのウィンドウだから」
『そーゆーの、結構融通きくんだねー』
「と、話を戻して、普通にウィンドウを表示するなら SW_SHOW を渡せばO
K」
『 MFC で思ったんだけど、 MFC のなんかのクラスでもこーゆーボタン作る
ことできるの?』
「もちろんできるよ。 MFC には CButton っていうクラスがあって、これを
使うとかなり簡単に作れるから」
CButton g_cButton;
void CFileTestDlg::OnBtnShow()
{
CRect cRect( 10, 10, 110, 40 );
g_cButton.Create
( "てすと"
, BS_PUSHBUTTON | BS_TEXT
, cRect
, this
, 1050 );
g_cButton.ShowWindow( SW_SHOW );
return;
// あとは取っておきましょう。
『あ、関数の外に変数がある』
「グローバル変数って言って、他の関数とかからも使える変数」
『 API とかランタイムとかの関数ってそうだよね』
「そう、その変数版。だーけーどー、ホントは使っちゃダメ」
『なんで?』
「いつの間にか書き換えちゃったりする可能性があるから。できるだけ書き
換えられる範囲は狭い方がいいからね」
『それって const ポインタ使うのと同じ考えだよね』
「そういうこと! const を使うのと同じように、グローバル変数は使わ
ない方が」
『書き換えられる範囲が狭まるってことね。これが悪い例だとすると、いい
例って?』
「 CFileTestDlg のメンバ変数にすること」
『 m_cDataLstBox なんかとおなじよーに?』
「そう、まさにそう」
『でもメンバ変数って CFileTestDlg のどのメンバ変数からでも使えるんだ
から、五十歩百歩じゃない?』
「う”……それはかなり難しい話になってくるからまた今度」
『あたしもそーゆー話ができるようになったのねー』
「子供の方が核心突くのうまいってゆーし」
『なんかゆった?』
「なんでも」
『……あとふたつ分かんないとこがあるんだけど。まず CRect のとこ』
「 CRect は MFC のクラスで、左上の x と y 、右下の x と y を格納する
クラス。【コンストラクタ】って憶えてる?」
『 Ver 5.17 ( No.082 ) でやったね。変数作るときに呼ぶ関数だよね』
「そう。ここで cRect( 10, 10, 110, 40 ) ってやってる部分は、そのコン
ストラクタを呼び出して、4つのメンバ変数にいっぺんに値を入れてるんで
す」
『そーゆー便利なのがあるのね。あと this って? 色が変わってるから
C++ 言語のなんかなんだろうけど』
「 this は……今呼ばれてるメンバ関数は?」
『 CFileTestDlg::OnBtnShow() だけど』
「これもメンバ関数なんだから、当然 ????.OnBtnShow(); って感じに呼ば
れてるはずでしょ。その ???? のアドレス、つまり &???? が this」
『????? どーゆー意味?』
「前にやったでしょ、どんなクラスも、ちゃんと変数として作られてて、そ
のメンバ関数もその変数を通して呼ばれてるんだって」
『うん憶えてるよ。えーっと、 Ver 3.3 ( No.028 ) とかでやったよね、こ
れだと、 FileTest.cpp の CFileTestApp::InitInstance() で作られてる、
CFileTestDlg dlg; の dlg がその変数なんだよね』
「そう、だから基本的には dlg.OnBtnShow() って呼ばれてるってこと」
『うんうん』
「で、その dlg.OnBtnShow() の中で dlg そのものを取得したいときは?」
『そのとき this を使う……アドレスってことは、 this は &dlg になるっ
てことなんだ!』
「そういうこと!」
/*
Preview Next Story!
*/
『はっきし言って、自分でボタン作れるのって、感動!』
「自分で操作できるのって楽しいからね」
『ホント、この前も言ったけど、色々できてくると楽しい!』
「でも、ある意味今が一番辛いかも……」
『というわけで次回』
< Version 5.26 コントロールをゲットしよう! >
「につづく!」
『げ、なんでエラー消えないの? なんで変なダイアログ出るのぉ!?』
「ほらね」