メインダイアログから子ダイアログをモードレスで作成し、
さらにメインダイアログから別の子ダイアログをモーダルで出したところ、
メインダイアログは操作不可になりますが、
モードレスで作成したほうは動かせてしまいます。
void CTestDlg::OnButton1()
{
m_dlg1.Create(CDialog1::IDD);
m_dlg1.ShowWindow(SW_SHOW);
}
void CTestDlg::OnButton2()
{
CDialog2 dlg2;
dlg2.DoModal();
}
このようになっているときにボタン1を押してからボタン2を押すと、
モーダル表示中もDialog1のほうは動かせてしまうのです。
Dialog1もDialog2もTestDlgが親のはずなので、
Dialog2をモーダルで表示している最中は
Dialog1のほうも動かせなくなるものだと思っていたのですが、
Windowsの仕組みではそのようにはならないものなのでしょうか?
(Dialog2はオプション設定ダイアログを想定しているのですが)
そういうもんですよ
自動的にはなりませんが、モーダルダイアログが出ている間
モードレスダイアログを使用不可にすること自体はできます。
解説ありがとうございます。
ということは、dlg2.DoModal();をコールする前後で、
「m_dlg1が表示されているときには操作不可にする・操作可能に戻す」
という処理を自前で実装しなければいけないうことでしょうか?
SDIやMDI形式のときには、モーダルダイアログ(設定画面)が表示されても
モードレスダイアログが操作可能のままになることは無かったのですが…。
たぶんだけど・・・
CFrameWndオブジェクトは使用不可にすると所有している
モードレスダイアログも使用不可にするような仕様になっているのでは
ないでしょうか?
CDialog::Create()は、2番目の引数省略すると親(オーナー)Window無しになった気が。
で、DoModal()では、子Windowや所有しているWindowのみDisableにしているのでは?
解説ありがとうございます。
> CDialog::Create()は、2番目の引数省略すると親(オーナー)Window無しになった気が
これが気になって調べ直してみましたが、デフォルトはNULLで、
ヘルプには「NULLを指定すると、ダイアログオブジェクトの親ウィンドウは
メインアプリケーションウィンドウになります」と書かれていました。
CTestDlgはダイアログベースのメインダイアログなので、
thisを渡しても結果は変わりませんでした。
やはり、CDialog1もCDialog2もCTestDlgの子なのに、
モーダル表示中もCDialog1は動かせてしまうのです。
> CFrameWndオブジェクトは使用不可にすると所有している
> モードレスダイアログも使用不可にするような仕様になっているのでは
> ないでしょうか?
なるほど、やはりSDIやMDIではCFrameWndなどが特別なことをやっていて、
ダイアログベースでモーダレスダイアログを別途出した状態で
モーダルダイアログを出そうと思ったら
そのへんも自分でやらなければいけなくなるのですね…。
IEでCtrl+Fで検索ダイアログ(モードレス)だして
IEのAboutダイアログ(モーダル)だしても
検索ダイアログの操作が出来てしまうので
そういうもんだと思うけど。
確かに変な感じがしないでもないが。
> IEでCtrl+Fで検索ダイアログ(モードレス)だして
> IEのAboutダイアログ(モーダル)だしても
> 検索ダイアログの操作が出来てしまうので
> そういうもんだと思うけど。
自分のテストプログラムだと、
ボタン1を押してモードレスダイアログを出し、
ボタン2を押してモーダルダイアログを出している状態
(メインダイアログは操作不可になる)で1のモードレスダイアログを閉じると、
2のモーダルダイアログが出たままメインダイアログが操作可能に戻り、
そこで再びボタン2を押すと、2つ目のモーダルダイアログが出てきてしまうのです。
さらにそれを繰り返すこともできてしまいます。
IEの例だと、検索ウィンドウが1のモードレスダイアログ、
バージョン情報が2のモーダルダイアログ、
IEのウィンドウがメインダイアログになりますが、
同じ動作をしてもそのようにはなりません。
やはりSDIやMDIなどではこういうことにはならないけど、
ダイアログベースでこれを防ぐためには、
自前で管理をしなければいけないということなのでしょうね…。
> (メインダイアログは操作不可になる)で1のモードレスダイアログを閉じると、
> 2のモーダルダイアログが出たままメインダイアログが操作可能に戻り、
> そこで再びボタン2を押すと、2つ目のモーダルダイアログが出てきてしまうのです。
> さらにそれを繰り返すこともできてしまいます。
この挙動は明らかに変です。
私が作成した簡単なテストプログラムではこのような現象は起きません。
少なくともモードレスダイアログを閉じる事で親の無効状態が解除されるわけが無いです。
まさかと思いますが、モードレスダイアログを消す時にEndDialogの呼び出しをしていたり
しませんか?直接呼んでいなくても間接的に読んでいれば同じ事になりますけれど。
たとえば、OnOkをオーバーライドしているのに中でCDialog::OnOkとか呼んでいると
そうなりそうな気がします。
CDialog::Create()の引数の件、勘違いしていました。すいません。
MFCのソースを調べたところ、CFrameWnd::OnEnable()ですべての所有WindowもDisableに
しているようです。
よってメインウィンドウがCFrameWnd派生のSDI/MDIではDoModal()呼び出しだけで
他のモーダレスダイアログやツールウィンドウなどもDisableになります。
一方CDialogはCWndと同じく所有WindowはDisableにしないようです。
> モードレスダイアログを消す時にEndDialogの呼び出しをしていたり
> しませんか?直接呼んでいなくても間接的に読んでいれば同じ事になりますけれど。
> たとえば、OnOkをオーバーライドしているのに中でCDialog::OnOkとか呼んでいると
> そうなりそうな気がします。
CDialog1/CDialog2は、なにも手を入れていません。
ClassWizardが出力した「CDialogの派生クラス」のままです。
そして、そのダイアログを以下のように出しているだけです。
static CDialog1 dlg1;
void CTestDlg::OnButton1()
{
dlg1.DestroyWindow();
dlg1.Create(CDialog1::IDD);
dlg1.ShowWindow(SW_SHOW);
}
void CTestDlg::OnButton2()
{
CDialog2 dlg2;
dlg2.DoModal();
}
もちろんこのままでは問題ありなのは理解してしますが、
動作を確認するために、こんな形になっています。
これをSDI形式でやると、CDialog1がモードレスで出ていても、
CDialog2がモーダルで出ている最中はさわれることはないのですが、
ダイアログベースでやると、上記の問題が発生してしまうのです。
やはりfacktさんに解説いただいたように、
ダイアログベース限定の問題ということになるのでしょうか。
私もダイアログベースの簡単なテストプログラムを作成して
行っていますが、モーダルダイアログの親がメインウインドウに当たる
ダイアログでそのダイアログから表示させているモードレスダイアログは
モーダルダイアログの影響を受けることなく操作できる状況です。
これはこれでそういうものだと思いますし、ダイアログベースだからと言うのは
facktさんの話からすると仕方ないと思います。
(ダイアログはそこまで管理していないと言う話ですし)
これが嫌なら自分で全てのウインドウを自前で管理して各ウインドウの有効/無効
管理もする必要があるともいます。
気になるのはモードレスダイアログを消すと親ウインドウが操作出来てしまって
いる点です。一般的なモードレスダイアログの操作方法であれば、親の無効化が
解除されるわけはありません。
無効化のコントロールはモーダルダイアログの操作のときには行われているようですが、
モードレスダイアログをDestroyWindowしても親に影響は出ないはずです。
この部分は気にしていないということであれば、これ以上の言及は避けますが。
ちなみにリソースもそのままにしていて
ダイアログクラスにも手を入れていないのであれば、
OKやキャンセルボタンを押して終了させると親の無効状態は解除されて当然ですね。
CDialogクラスの既存の実装が呼ばれるはずです。
システムメニューの×ボタンもキャンセル扱いになるはずですし、
EnterキーとESCキーも同じ扱いなので雛形をそのまま使っているなら
それが原因でしょう。
わかっていてやっていて、かつ、結果に関しても理解できているなら
この件についてはこれ以上議論する必要はなさそうです。
> ちなみにリソースもそのままにしていて
> ダイアログクラスにも手を入れていないのであれば、
> OKやキャンセルボタンを押して終了させると親の無効状態は解除されて当然ですね。
> CDialogクラスの既存の実装が呼ばれるはずです。
詳しい解説ありがとうございます。
つまり、子ダイアログがモードレスで作成された場合でも、そのままだと、
自分が親を操作不可にセットしたわけでもないのに、
ダイアログ終了時には親ウィンドウを操作可能にしてしまうということなのですね?
CDialogクラスのソースは見てみたのですが、
DoModalの中では、親を操作不可にしたり操作可能に戻したり
ということをやっているようなのですが、
そこまでは理解できていませんでした。