ソースを変更してみる

OKボタンとキャンセルと書いたボタンが置いてあります。
ややこしいのでこれは取ってしまいましょう。
OKボタンの上でマウスを右クリックをして出たメニューの一番上の切り取りを押します。
OKボタンが消えましたか?
同じ方法でキャンセルボタンも消します。

ではさっきと同じように実行して見てください。
同じようにダイアログが表示されてボタンがなくなっていれば成功です。

これも終わらせましょう。
右上のばってん印で閉じます。

では次にダイアログの上に新しいボタンをつけてみます。
右上にコントロールと書いた小さなウィンドウが出ています。
これはコントロールルールバーといいます。
まれに出ていない人がいますが、そういう人はまずプロジェクトがダイアログベースになっているか確認して下さい。

確認する方法は思い出すことです。
思い出せない場合は以下の確認をしてだめならプロジェクトの新規作成からやりなおしです。

MDIやSDIではこのコントロールツールバーは出ません。
ちゃんとダイアログベースになっていても出ない場合は

1 ツール>>カスタマイズを選択
2 ツールバーを選択
3 コントロールにチェックがあるか確認、無ければチェックをいれる
4 OKを押して閉じる。

以上で出るはずですが、それでも出ない場合はVC++の再インストールをして見てください。

コントロールツールバーはダイアログで使えるパーツが入っています。
このほかにもいろいろあります。
何があるかちょっと見てみましょう。

そのなかのいかにもボタンというパーツにマウスカーソルをあわせて下さい。
カーソルの下に「ボタン」という文字がでればそれです。
それを押してからダイアログデザイナの中で左ボタンをクリックするとダイアログの中にボタンがおかれます。

この状態で一度実行してみましょう。

できましたか?
いま配置したボタンを押してみると確かに手応えがあります。
しかし反応はしますが、何も起こりません。
VC++はボタンを配置して押したときの動作までは面倒みてくれますが、押したらどうするかはプログラマが考えて作らなければならないという事ですね。

では終了しましょう。
ボタンを押したときになにかさせてみることにします。
貼り付けたボタンをダブルクリックします。
すると「クラスCtestDlg用のWinowsメッセージおよびイベントハンドラの新規作成」というダイアログが開きます。
うーん、難しそうですね。
簡単にいうと、ボタンを押したときにウィンドウズが通知してやるから必要な事をやれ、という意味です。
その必要な事というのは、「ハンドラの追加」と、「メンバ変数の追加」です。
ハンドラとはなにかというと、メッセージに対して処理をする関数です。
今やっているボタンの場合だと、ボタンを押したときに何をするか、を書いた関数を

ボタンが押されたときのハンドラといいます。
ハンドラはまたハンドラ関数、コマンドハンドラなどとも言われてどれも同じ意味です。
ややこしですが、それぞれ機能としては同じでもハンドラ関数と呼びたい場合や、コマンドハンドラと呼びたい場合があるのでしょう。

ではこのハンドラを作ります。
「クラスCtestDlg用のWinowsメッセージおよびイベントハンドラの新規作成」ダイアログの左側にBN_CLICKEDとBN_DOUBLECLICKEDというのがあります。
これはボタンというのは元々クリックしたりダブルクリックしたりするものなのであらかじめそれ用のハンドラは用意されているという意味です。

BN_CLICKEDが青く反転しています。
ハンドラの追加を押してみます。
「メンバ関数の追加」というダイアログが出てきました。
メンバ関数名は「OnButton1」になっています。
これは「OnButton1」というハンドルをメンバ関数として追加するという意味です。

メンバ関数とはなにか?
ダイアログの上にあるボタンのハンドルは、ダイアログのメンバ関数になる、ととりあえず覚えて下さい。
メンバ関数にならないとダイアログからボタンのハンドルにアクセスできないのです。

ダイアログからボタンのハンドルにアクセスするとはどういうことか?
ダイアログが受け取った「ボタンが押されたぞ」というメッセージをボタンに送ることです。

「メンバ関数の追加」ダイアログのOKボタンを押しましょう。
既存のメッセージイベントハンドラにBN_CLICKEDが移動しました。
次に「既存の編集」ボタンを押します。
ここで再度メンバ関数の追加や追加と編集を押すと、左にあるもう一つのBN_DOUBLECLICKEDのハンドルも追加されます。
「既存の編集」ボタンを押すと「testDlg.cpp」というファイルが表示されます。
カーソルは

void CTestDlg::OnButton1()

の所にあります。
これがボタン1のハンドルです。

ここに何か処理をかけばボタンを押したときにその処理が実行されるわけですが、なにを書いてよいかわかりませんね。まだプログラムの書き方もわからないわけですから。
そこで、とりあえず動くことを確認するためにボタンを移動させてみます。

ハンドルの

// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください

の下に以下のコードを書込みます。
 

  CRect Rect;//---------------------------------------1
  m_Button1.GetWindowRect(Rect);//--------------------2
  ScreenToClient(Rect);//-----------------------------3
  m_Button1.MoveWindow(Rect.left+10,//----------------4
      Rect.top,
      Rect.right-Rect.left,
      Rect.bottom-Rect.top,
      true );
 

じつはこれだけでは動きません。
なけかというと、ボタンに名前が付いていないからです。

m_Button1 というのは今書いたわけですが、これは勝手に書いたボタンの名前です。
プログラムは(というかVC++は)ボタンの名前がm_Buttonであるかどうか知らないのですね。
この頭に「m_」と付いているのはメンバ関数である、と言う意味です。
ウィンドウズのプログラムではいろいろ明文化されていない掟がありますが、まずはこの命名規則を覚えましょう。

  頭に m_ は、メンバ関数です。
  頭に h  は、ハンドルです。
  頭に C  は、クラス変数です。
  頭に p  は、ポインタです。
  頭に ID は、ID(認識番号)です。

上に書いたコードの説明の前にボタンの名前をつけましょう。
ダイアログデザイナのボタンの上で右クリックをします。
出たウィンドウの下から3番目くらいにClassWizaedというのがあるのでこれをクリックします。

メンバ変数というタブが前に来ています。
コントロールにIDC_BUTTON とあり、タイプはCButton メンバの所はあいています。

ウィンドウズの命名規則にしたがってこれを見ますと、貼り付けたボタンのIDは、
IDC_BUTTON1
という意味ですね。
さらにこれはID  の  C_BUTTONですから、ボタンクラスのIDが1とも読めます。
タイプはCBUTTONですから、ボタンのクラスですね。
ここで右側の三つ並んだボタンの真ん中、変数の追加をクリックします。
するとメンバ関数の追加というダイアログが開きます。

メンバ変数の場所に m_ とあります。
これは頭にm_をつけてね、と言う意味です。
これを消して勝手に変な名前をつけても良いのですが、あまり、というかまったくオススメしません。
あとで混乱の元になるからです。
ボタンが一個くらいならどうということはありませんが、ボタンがたくさんあったり、コントロールがたくさんあるようなプログラムを作るときには大混乱になってしまうでしょう。

そこで、ここはウィンドウズの常識にのっとり、m_Button1と命名します。

これで貼り付けたボタンにプログラムが認識できる名前がつきました。

OKを押してダイアログを閉じます。
早速実行してみましょう。
ボタンを押したらボタンがちょっとづつ右に動けば成功です。
うまく行ったらいったん終了しましょう。

これから打ち込んだコードの説明をしますが、理解できなくても大丈夫です。
これは意外と後の方で学ぶべき範囲なのでここではこんなコードがあったなあ、くらいの覚えかたで大丈夫です。
ですから以下の説明は読み飛ばしてかまいません。

まず、1 ですが、これは 変数の定義をしています。
ウィンドウズの命名規則から読むと、CRect(Rectというクラス)のRectという変数を使います。という意味です。
こうするとRectという変数が使えるようになります。
このRectは実はクラスのインスタンスというものですが、それは後で説明します。
ここでは変数という扱いにしておきましょう。

Rectの中には left, top, right , bottom という変数が含まれています。
これはウィンドウの位置と大きさをあらわすための構造体です。

ボタンもウィンドウの一種です。というか、基本的なウィンドウの構造を引き継いでボタンができているのですね。
これをウィンドウクラスを継承してボタンクラスを作ったと表現します。

で、このRectを定義してm_Button1の位置をRectの中にいれてくれという命令が2の GetWindowRect です。
その命令の前に m_Button1 と書いてあります。
これは、m_Button1 の位置に関して、その情報をRectにいれろ、という意味になります。
この m_Button1 を省略すると、Rectの中には、text.exe の全画面上の位置が入ります。

3の ScreenToClient は、Rectに入った位置を変換します。
なにに変換するのか?2でRectに入った値は、全画面上のボタンの位置なのです。
モニターの左上の角からの位置という意味です。

ついでに言うと、ウィンドウズの座標は、左の上が0、0です。
X方向は、右に行くにしたがってふえます。
Y方向は、下のほうが+方向です。
数学でならった座標とは違っているので注意が必要です。

GetWindowRectが全画面上の座標をRectにいれますが、
位置をセットするMoveWindowという命令ががクライアント(この場合は test.exe )のウィンドウの座標で指定する必要があるので、Rectの位置を変換する必要がある、という訳です。

全画面上の座標をtest.exe の座標に変換してから、ボタンの新しい位置をセットします。
 

  m_Button1.MoveWindow(Rect.left+10,//----------------4
     Rect.top,
     Rect.right-Rect.left,
     Rect.bottom-Rect.top,
     true );
 

これは一つの命令です。命令と言っても実態は関数なのですが、今は命令と言っておきます。
m_Buttonに関してMoveWindowをしなさい、その新しい位置は・・・という意味です。
最後の true の部分は新しい位置をセットしたら描画し直すか、というフラグです。
true にすると描画し直しという意味になります。

このカッコの中にあるものは命令(関数)の引数といいます。
以上がボタンが動くコードの説明です。

理解できましたか?
できなくても心配無用です。
しばらく勉強したあとに再びこの説明を読んで、ああなるほど、と思えば進歩した証拠です。
この説明を初期のわからなさの基準としましょう。