Buttonの使い方
今回のコントロールはボタンです。このボタンは、いたるところで使われますので用途は様々なんですが、是非習得しておきたい項目でしょう。

1. とりあえず、ボタンを使ってみよう
おなじみの新規作成でプロジェクトを作ることから始めましょう。
1.プロジェクトでMFC AppWizard(exe)を選択し、プロジェクト名は、DlgButtonとしておく
2.「作成するアプリケーションの種類」はダイアログベースを選択
.3.終了
ダイアログを編集します。
1.ワークスペースのリソースタブをクリック
2.DlgButtonリソースのDialogフォルダをダブルクリック
3.IDD_DLGBUTTON_DIALOGを開く
4.以下の図のように編集する。
OKボタンは残しておいて、キャンセルボタンと”Todo:ダイアログのコントロールをここに配置”というスタティックは消す。Buttonを適当な大きさで配置(IDはIDC_BUTTON1
5.ビルド--実行を行う
実行結果も図にしておきました。まだボタンを押しても何にもなりません(笑)
<--(編集内容)<--(実行結果)

2.さてボタンに動作をつけましょう
今までの作業はボタンを配置しただけです。MFCの基本はメッセージの受け渡し、というのは先ほどから結構述べていますが、現段階では、ボタンはメッセージを送るようになっていないために、ボタンを押しても何にもならないのです。じゃあ、メッセージを早くつけてよ。ということでメッセージの付け方を述べたいと思います。

1.ボタンを右クリックしてClassWizardを起動
2.以下の様に選択されているか確認
プロジェクト:DlgButton
クラス名:CDlgButtonDlg
オブジェクトID:IDC_BUTTON1
3.メッセージでBN_CLICKEDをハイライト
このメッセージは「ボタンを押した瞬間に送られる」メッセージです。「関数の追加」ボタンが淡色表示から通常の表示になったらよいです。
4.「関数の追加」ボタンを押す。
関数名を指定せよと表示されます。今は、ディフォルトのOnButton1でOKです。
5.「コードの編集」ボタンを押して、ClassWizardを終了します。
別にこの時点でコードの編集を押さずにOKだけ押してClassWizardを終了し、CDlgButtonDlgのファイルからさっき作った関数を見ても同じことです。ただ手間を省くだけですよ。
さて、後はコードを書くのみです。一番手っ取り早いのはAfxMessageBox関数を使うことです。この関数は、メッセージ関数とも呼ばれ、ただメッセージを小さなウィンドウで表示してくれるだけですが、このように関数が動作しているのかなどのちょっとした確認するのには非常に便利な関数です。これを利用してコードを書きます。

OnButton関数内のコード
void CDlgButtonDlg::OnButton1() 
{
	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
	AfxMessageBox("OnButton1関数");
}

この関数はただ単に、「OnButton1関数」と書かれたウィンドウを表示するだけです。さて、実行してButtonを押してみましょう。


となったらOK。しかし、今回はこれがメインではありません。ボタンアクションを作るのは実は別のコーナーでも解説しています。今回はちょっと変わったボタンについて解説しようと思います。

3.コントロールをいじるために〜SubClass化
コントロールをいじる、例えばボタンに表示する文字(キャプション)が味気ないので、ビットマップが表示できたらいいいと思うかもしれません。MFCでは、CBitmapButtonなんてその名前のとおりのクラスが用意されているので実際は楽にできそうなのですが、そうも行きません。実際は動的にSubClass化しないとコントロールは変更できないのです。そのサブクラスという物は?という疑問が湧いてきましたね。まずそこから解説します。

サブクラスとは何でしょう?
サブクラス化を超簡単にいうと(実際今から言う表現は実際は間違っているかもしれないのですが、わかりやすくいうなら・・・という感じで説明しています)、あるダイアログやウィンドウが持つコントロールをあたかもそのダイアログやウィンドウの子分のように扱えるようにするということです。極端にいうと、ダイアログのクラスからコントロールを直接カスタマイズすることが可能になるわけです。さきほど作ったプログラムのボタンをビットマップ表示します。どんな感じでやるかといいますと・・・
1.ビットマップリソースの準備
・これがなくちゃ始まりません。リソースタブをクリックし、DlgButtonリソースというところを右クリックします。「挿入」を選んでBitmapを追加して下さい。Bitmapは自分の好きな絵を描いてくれればOKです。
・ビットマップのIDはIDB_BITMAP1にしておきます。
2.DlgButtonDlg.hにメンバ変数を追加
private:
CBitmapButton m_BmpBtn;
と追加して下さい。これは名前の通りビットマップのボタンを操る変数です。(CButtonと似ているかな?)
3.OnInitDialog関数(DlgButtonDlg.cpp)に以下の文を追加する。
OnInitDialogのコード
// TODO: 特別な初期化を行う時はこの場所に追加してください。
	if (!m_BmpBtn.SubclassDlgItem(IDC_BUTTON1, this))
	return(FALSE);

	m_BmpBtn.SetButtonStyle(BS_PUSHBUTTON|BS_OWNERDRAW);
	m_BmpBtn.LoadBitmaps(IDB_BITMAP1);
	m_BmpBtn.SizeToContent();
4.実行します
私は日の丸の絵のビットマップをボタンに貼り付けました。どうです?

なぜあのコードが必要なのか?
サブクラス化によって、あたかもコントロールがダイアログクラスの一員のように考えることができコントロールの詳細な操作ができると、言いましたが、実際どのように動いているかを説明します、

if(!m_BmpBtn.SubclassDlgItem(IDC_BUTTON1, this))
return(FALSE);
・thisというのは、自分自身(すなわちこのダイアログ自身)です。IDC_BUTTON1を動的サブクラス化します。サブクラス化は「動的」に行われるため、失敗する可能性があります。失敗すると関数が0を返してきます。これを"!"演算子で論理否定しています。ということは、関数が失敗するとifの論理値が1(真)になるため、return (FALSE)が返されます。
・この関数が無事に実行されると、IDC_BUTTON1というコントロールは、CWnd(ウィンドウやダイアログの操作ができる関数)のメッセージを受け取るとるようになります。このサブクラス化をしないと、ボタンはCButtonクラスのメッセージのみしか受け付けないのです。
m_BmpBtn.SetButtonStyle(BS_PUSHBUTTON|BS_OWNERDRAW);
ボタンのスタイル(外観や機能)を決めています。SetButtonStyle(フラグ)という形でセットします。今回はBS_PUSHBUTTONとBS_OWNERDRAWというフラグを設定しています。
BS_OWNERDRAW:オーナー描画ボタンを作成します(通常のボタンにようにMFCが直接描画するのとちがいプログラマーが描画させるボタン)。特にCBitmapButton クラスを使うときはこのスタイルを設定しなければなりません。
BS_PUSHBUTTON:ユーザーがボタンを選択したときに、オーナー ウィンドウ(この場合ダイアログ)に WM_COMMAND(ボタンをクリックしたという)メッセージを送るプッシュ ボタンを作成します。
詳しくはMSDNのボタンスタイルを参照して下さい。
m_BmpBtn.LoadBitmaps(IDB_BITMAP1);
ビットマップを読み込んでいます。これはなんとなく分かりますね。
m_BmpBtn.SizeToContent();
ボタンのサイズをビットマップの大きさにしてくれる便利な関数です。実際これをかかないとビットマップが欠けてしまったり、ビットマップでないところを押してもボタンが動作してしまうことがあるので、一応このコードは書いておく方がいいでしょう。
これをOnInitDlgで記述することも肝心です。このOnInitDlgはダイアログを表示する前に実行されるので、ここでボタンをビットマップを付けて表示するコードを記述しておかないと意味がないのです。(まぁ別のところでできないことはないけど)。このコーナーの最後にチェックボックスについて触れます

4.チェックボックスを使おう

チェックボックスは「ボタン」ではないような感じがします。しかしこれはボタンなのです。CButtonクラスにCheckボタンに関する関数がありますし。RadioButtonについてはまた別に取り上げる予定です。とりあえず作ってみましょう。
1.先ほどのダイアログを変更する。
チェックボックスを追加します。3個チェックボックスを追加してください。IDとキャプションの関係は一応このように設定すればこのコーナーの説明は楽に読むことができるでしょう(おそらく)
ID:IDC_CHECK1 キャプション:A
ID:IDC_CHECK2 キャプション:B
ID:IDC_CHECK3 キャプション:C
2.実行します。
先ほどのボタンビットマップ表示が反映されているのでIDC_BITMAP1がビットマップ表示されたままですが、別に気にしなくて結構です。以下の感じに仕上がればリソースの変更は終了です。

このままではチェックボックスが扱えません(実行してチェックボックスにチェックはできますが、ボタンを押しても一番始めのメッセージボックスが起動するだけです)。そのためにはコードを記述する必要性があります。まぁ当然といえば当然の話ですが。

先ほどボタンとメッセージを結び付けた関数OnButton1を編集します。
OnButton1関数の編集
void CDlgButtonDlg::OnButton1() 
{
	int nCheckFlg[3] = {0,0,0};
	int nCheckCount = 0;

	CButton* pBtn1 = (CButton*)GetDlgItem(IDC_CHECK1);//CheckButton1のポインタの取得
	CButton* pBtn2 = (CButton*)GetDlgItem(IDC_CHECK2);//CheckButton2のポインタの取得
	CButton* pBtn3 = (CButton*)GetDlgItem(IDC_CHECK3);//CheckButton3のポインタの取得
	
	nCheckFlg[0] = pBtn1->GetCheck();//チェック状態の取得(チェックされていると1が返る)
	nCheckFlg[1] = pBtn2->GetCheck();
	nCheckFlg[2] = pBtn3->GetCheck();

	for(int i=0;i<3;i++)
		nCheckCount += nCheckFlg[i];//チェック数の数をカウント
	
	switch(nCheckCount){
		case 0:AfxMessageBox("Not Selected.");break;
		case 1:AfxMessageBox("One Box Selected.");break;
		case 2:AfxMessageBox("Two Boxes Selected.");break;
		case 3:AfxMessageBox("All Boxes Selected.");break;
		default:break;
	}	
}

実行してもらえると分かりますが、適当な数チェックを入れて、ボタンを押すと何個チェックしているのかを答えてくれるものができています。ここでは上のコードの中で重要なものを2つ説明します。
CButton*  pBtn1 = (CButton*)GetDlgItem(IDC_CHECK1);
GetDlgItemとはダイアログの中のコントロールを指定するのによく使う関数で、通常そのコントロールのクラスにキャスト(強制型変換)して使います。(CButton*)というのがキャストと呼ばれる物で、GetDlgItemの戻り値をCButton型(ポインタ)に変更しています。これをpBtn1というCButton型に代入しています。これ以降pBtn1を使うことによってIDC_CHECK1を操作できるようになるわけです。
nCheckFlg[0] = pBtn1->GetCheck();
先ほどのpBtn1はCButtonクラスなのでCButtonクラスの関数が使用できます。GetCheck関数は、チェックボックスがチェックされていたら1、されていなかったら0を返す関数です。これが1ならチェックボックスはチェックされている状態です。
チェックされているかを調べる関数があるなら、強制的にチェックを入れる関数もあるんです。これは、SetCheck関数という関数なんですが、以下のように使います。
SetCheck(true);
チェックを入れます。チェックがあらかじめ入っている場合は何もしません。
SetCheck(false);
チェックを外します。チェックがあらかじめ入ってない場合は何もしません。
具体的には・・・
void CDlgButtonDlg::OnButton1() 
{
	CButton* pBtn1 = (CButton*)GetDlgItem(IDC_CHECK1);//CheckButton1のポインタの取得
	pBtn1->SetCheck(true);//チェック状態の設定
}

実行してボタンを押して下さい。何にもチェックしていないとき、IDC_CHECK1(キャプションAのやつ)が強制的にチェックが入ったでしょうか?チェックが入ったら成功です。このボタンアクションはかなり使えるテクニックです。絶対習得することをお勧めします。

Back to Index