Visual C++ 学習日誌 No.7

1997年10月02日
モードレスダイアログボックスを作る

CD Player の制作中に、インデックスデータの登録用ダイアログボックスがどうしても必要になりました。
Visual C++ では、ダイアログボックスは通常、リソースエディタでお絵かきをする感覚で作ることができます。
そして、ダイアログボックスを実際に作成するには
ダイアログボックスのクラスをCTestDlgとすると、

	CTestDlg dlg;
	dlg.DoModal();
と、します。 このようにしてできるダイアログボックスをモーダルダイアログボックスといいます。

しかし、この方法で生成されるダイアログボックスは、ダイアログボックスが開いている間は、アプリケーション本体のウインドウにはアクセスできません。

僕が現在使っているCD Playerは便利なのですが、曲名のデータの編集中は、他の操作ができません。
曲名の登録用のダイアログボックスが開いている間は、アプリケーションのウインドウにアクセスできないのです。
だから、今度自作するCD Playerは、CDの操作と、曲名登録を平行してできるようにしたいと思っていました。

そこで、モードレスダイアログボックスというのを使うことにしました。
これなら、ダイアログボックスを開きながら、アプリケーションの他の操作をすることも可能です。
しかし、困ったことに名前は知っていたのですが、実際にどうすればいいかはさっぱりわかりませんでした。

そこで、また本を買ってきました。(お金が〜〜〜〜)
よって、以下はほとんど受け売りの解説です。

モードレスダイアログボックスというのは、生成されるときと、破壊されるときのタイミングが任意になります。
親ウインドウがあれば、いつでも生成、破壊が可能です。
また、親ウインドウが破壊されると、自動的にダイアログボックスも破壊されます。

モーダルダイアログボックスでは、生成、破壊をすべて自動的に行っていましたが、
モードレスダイアログボックスは、生成、破壊のタイミングが任意ですから、生成、破壊のコードも書かなければなりません。

モードレスダイアログボックスを生成するには、

	CTestDlg* dlg = new CTestDlg;
	dlg->Create(IDD_TEST_DIALOG);
と、します。なお、"IDD_TEST_DIALOG"は、ダイアログボックスのリソースIDです。
僕も、ここまではうまく行ったのですが、こうしてもダイアログボックスが出てきませんでした。
あわてて、本を読んでみたら、ウインドウはあるのに表示されていないだけだったようです。
モーダルダイアログボックスの場合、内部でShowWindow()メンバ関数を実行して、ウインドウを表示させるようになっていたようです。
モードレスの場合、それも自分で対処しなければなりません。
対処法は、ダイアログボックスのリソースの、プロパティで「その他のスタイル」のタブの中の「可視」をチェックします。
これは、ShowWindow()関数を使っても同じ事です。
でも、面倒ですから、リソース側で設定した方がいいですよね?

それでは、破壊はどうなるでしょう。

	dlg->DestroyWindow();
と、なります。しかし、この状態では、確かに「ウインドウ」は破壊されますが、CTestDlgという「オブジェクト」はまだメモリに残ったままです。
これを、破壊するには、上のコードの後に
	dlg->DestroyWindow();	//ウインドウの破壊
	delete dlg;		//オブジェクトの破壊
と、書くか、CTestDlgクラスの中のPostNcDestroy()をオーバーライドし、
void CTestDlg::PostNcDestroy()
{
	delete this;	//自分自身のオブジェクトの破壊
}
と、書くかすれば、実現できます。 後者は、自動クリーンアップと言います。PostNcDestroy()はウインドウが破壊されたときにMFCから呼び出されます。
これによって、ウインドウの破壊とオブジェクトの破壊が同時にできます。
モードレスダイアログボックスの時はこちらの方がいいでしょう。
ただし、この方法を用いたクラスは、new以外の方法でオブジェクトを作ることができなくなります。
スタックフレーム上に作成されたオブジェクトをdeleteで削除するのはルール違反だからです。

あと、<OK>ボタンと<キャンセル>ボタンを無効にしておきます。そうしないと、予期しないタイミングでダイアログボックスが閉じてしまいます。
<キャンセル>ボタンがたとえなくても、もしも、ESCキーが押されれば、<キャンセル>ボタンをクリックしたのと同じ働きをするからです。
システムメニューの「閉じる」や、ウインドウをクローズするボタン(×印のボタン)をクリックしても同じです。
また、リターンキーは<OK>ボタンを押したことになります。
ですから、OnOK()とOnCancel()を、オーバーライドします。

void CTestDlg::OnOK()
{
	//独自の処理
}
void CTestDlg::OnCalcel()
{
	//独自の処理
}
と、します。Default()という関数を呼び出せば、ボタンがあれば、ボタンが押されるように見えるだけで何もしないようにすることができます。
ただしこのときは、「閉じる」を選択しても、何も起こらないと言うことがあり得ます。
void CTestDlg::OnOK()
{
	Default();
}
void CTestDlg::OnCalcel()
{
	Default();
}

また、モードレスダイアログボックスでは、DDX/DDVの働きも変わります。
モーダルダイアログボックスでは、DoModal()関数の中で、DDX/DDVをサポートするコードが記述されていましたが、
それを、自分で定期的に呼び出してやらなければなりません。それには、UpdateData()という関数を使います。

UpdateData(FALSE);
で、DDX変数から、コントロールに値がセットされ、
UpdateData(TRUE);
で、コントロールからDDX変数に値がコピーされます。
これらを、必要に応じて呼び出してやらなければなりません。

僕が制作中のCD Playerでは、ビュークラスとのやりとりが必要だったので、ダイアログボックスのコンストラクタで、ビュークラスへのポインタを渡すようにして、 内部の処理も、これとは少し変えてあります。

参考文献:「Visual C++ プログラミングテクニック」 田口景介 著 アスキー出版局

[BACK | NEXT]


back
Visual C++ Laboratoryのページへ戻る


E-Mail:wbs03748@mail.wbs.ne.jp
質問・感想などのメールはこちらへ。


Copyright(C) 1997 Shingo Nakagawa / e-mail:wbs03748@mail.wbs.ne.jp