環境:WinXP, VS2005
CDialog派生クラスをモードレス表示して子ウィンドウとして使おうとしているのです
が,
子ウィンドウとして使われるダイアログ上にある
エディットボックスがマウスクリックに反応しないことに困っています.
(タブキーでコントロールを巡っていばエディットに入力することはできますが…)
リソースのプロパティをいろいろ変えてみたところ,どうやら
ダイアログにタイトルバーを持たせると反応しなくなるようです.
(タイトルバーが無い場合はクリックで入力状態になる)
タイトルバーの保持とマウスによるエディット操作を両立するには
どうすればよいのでしょうか?
追記:
どうやらダイアログ限定ではなくCWndの場合も同様の現象が発生するようです.
親ウィンドウへのポインタが
CWnd *m_pMainWnd のとき…
子ウィンドウ用のCWnd m_Dummy と,
その上に乗せるための CEdit m_Edit を用意して
以下のようにしてみましたが,やはり m_Dummy に WS_CAPTION を指定すると
エディットがマウスに反応しなくなってしまいました.
//子ウィンドウを作る
RECT rect;
SetRect( &rect, 0,0,200,200 );
m_Dummy.Create( AfxRegisterWndClass(0,0,(HBRUSH)GetStockObject
(GRAY_BRUSH),0), Dummy, WS_CHILD/*|WS_CAPTION*/|WS_VISIBLE|WS_CLIPCHILDREN,
rect, m_pMainWnd, 1 );
//その上にエディットを配置
SetRect( &rect, 10,10,100,60 );
m_Edit.Create( ES_CENTER|WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER ,rect,
&m_Dummy, 2 );
説明の中に出てきていませんが、文面からするとMFCアプリケーションで
プロジェクトを作成しているのですよね。
この辺の話もレスに影響がありますから質問時に提示してください。
あと、前回の質問を見た人がまた見てくれるとは限りませんから
スレッドを変える時は毎回説明する必要があります。
で、なぜ一々自分でCreateを呼んでいるのかがわかりません。
ダイアログリソースを利用してダイアログを作成する方法だと
何か支障があるのでしょうか?
あと、エディットコントロールを作成するコードはどのウインドウの
プロシージャに書いていますか?
通常の考え方で行けば、エディットボックスの親ウインドウの
プロシージャで書くと思いますけれど。
MFCのフレームワークの作りとか処理の順番を理解した上で
適切なタイミングと場所でCreateを呼ぶのであれば良いのですが、
タイミングや場所が悪いとうまく行かないケースがあると思います。
普通にダイアログリソースを利用してモードレスダイアログを
作成する分にはタイトルバーがあろうが無かろうが
エディットボックスはきちんと反応するはずです。
ウインドウのプロシージャを書いてしまいましたが、
どのウインドウのクラスのメンバー関数にが正しいですね。
あと、どのメンバー関数にかな。
せっかくクラスを使っているのにクラス毎にカプセル化できていない
様に感じます。何か意味があってやっているのであれば良いのですが、
意味がないならMFCを使ったときの一般的な方法を使った方が
良いと思いますけれど。
>説明の中に出てきていませんが、文面からするとMFCアプリケーションで
>プロジェクトを作成しているのですよね。
すみません,
MFCアプリケーションである旨の記述が欠けていました.
>あと、前回の質問を見た人がまた見てくれるとは限りませんから…
? 最近関連するようなスレッド
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+201009/10090013.txt
へレスは付けましたが,そのことをおっしゃっておられますか?
そうだとすれば,今回の質問はこれとは別件です.
エディットをCreateしたりしている2番目の書き込みについては,
単に,対面している問題が
「試しにウィンドウにしてみても同様だった.
どうやらダイアログだから,ということではないようだ」
ということを言っているだけです.
文面が不明瞭で申し訳ありません.
まず,本件においてやりたいことですが…
(1)そもそもダイアログベースで作っていたソフトであり,
そのソフトは複数のモードレスダイアログをポップアップさせていた.
(2)しかし「ばらばらにダイアログが浮いている外観に統一感がない」とかいう
理由で,MDIのような(?)メインのウィンドウのクライアント領域内に
各ダイアログがある形に変更するように要求された.
(3)修正にあたり,既存のコードをほぼそのまま流用したい.
(プロジェクトを作り直してどうこうという時間的余裕がない)
そこで,以下のような変更を試みています.
(1)メインウィンドウとして使うためのCWnd派生クラスCPseudoMainWndを用意
(2)元のダイアログベースアプリのAPPクラスにCPseudoMainWnd型メンバ変数加え,
InitInstance()内でCreateし,ポインタをm_pMainWndに代入する.
これでメインウィンドウがとりあえず表示される.
(3)既存のダイアログリソースのプロパティの「スタイル」を「子」に変える
(4)(3)に対応するダイアログクラスのインスタンスをCPseudoMainWndのメンバに加え,
OnCreate()内でダイアログの生成と表示を行う.
(CTestDlg m_TestDlgだとすれば)
m_TestDlg.Create( CTestDlg::IDD, this );
m_TestDlg.ShowWindow( SW_SHOW );
以上の変更を行った結果として,
「メインウィンドウの子ウィンドウとしてモードレスダイアログを作った」つもりなので
す.
問題は,最初の書き込みにあるように,このダイアログにある(リソース上で配置した)
エディットボックスが,ダイアログがタイトルバーを持つ場合に
マウス操作に反応しなくなってしまう,ということです.
私が行っていることは
>適切なタイミングと場所でCreateを呼ぶ
にあたらない行為だということなのでしょうか……?
再度すみません……前記,試みの(4)がまちがっていたので修正します.
>(4)(3)に対応するダイアログクラスのインスタンスをCPseudoMainWndのメンバに加え,
> OnCreate()内でダイアログの生成と表示を行う.
> (CTestDlg m_TestDlgだとすれば)
> m_TestDlg.Create( CTestDlg::IDD, this );
> m_TestDlg.ShowWindow( SW_SHOW );
メンバはポインタで,OnCreate()内でnewでインスタンス生成していました.
CPseudoMainWndのメンバ:
CTestDlg *m_pTestDlg;
CPseudoMainWnd::OnCreate()内:
m_pTestDlg = new CTestDlg( this );
m_pTestDlg->Create( CTestDlg::IDD, this );
m_pTestDlg->ShowWindow( SW_SHOW );
私の環境では以下のようにすると問題としている現象が再現できます.
(1)ダイアログベースプロジェクトをウィザードで新規作成
(ここでは便宜上,プロジェクト名をTestとする.)
(2)生成されたダイアログリソースにエディットボックスを適当に追加
(3)CTestAppクラスのメンバに以下を追加
CWnd m_Wnd; //親ウィンドウ用
CTestDlg *m_pDlg; //子ウィンドウとして使うダイアログ用
(4)CTestApp::InitInstance()内:CTestDlg dlg; から return までを削除し,
m_Wnd.CreateEx( 0, AfxRegisterWndClass(0,0,0,0), _T(MainWnd),
WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN, 0,0, 320,240, NULL, 0 );
m_pMainWnd = &m_Wnd;
m_pDlg = new CTestDlg( m_pMainWnd );
m_pDlg->Create( CTestDlg::IDD, m_pMainWnd );
return TRUE;
を追加.
newしたものをdeleteしていない等は置いておいて,
この状態でエディットボックスがマウスに反応しません.
MFCに関係なく、Windowsの仕様のようです。
子フレームへのWM_MOUSEACTIVATEで
子フレーム自身にWM_NCACTIVATEを送り、
子フレームをアクティブにすれば何とかなります。
複数の子フレームがあると、
子をアクティブ/非アクティブに切り替えるのが複雑になるかもしれません。
どうにも書き込みがエラーではじかれてしまうようなので簡潔に.
>ロマ様
ご教授いただいた方法で対処できそうです.
非常に助かりました.ありがとうございます.
>PATIO様
不明瞭な文面のため御迷惑をおかけしました.
まずは問題に対処できそうなので解決とさせていただきます.
行った対策:
親ウィンドウクラスに以下のような
子のアクティブ状態を切り替えるためのメソッドを追加し,
子はOnMouseActivate()から自身のウィンドウハンドルを引数として
このメソッドを呼ぶようにしました.
void CPseudoMainWnd::ActivateChild( HWND hActivateTgt )
{
CWnd *Child[N] = { N個の子ウィンドウ };
for( int i=0; i<N; i++ )
{
HWND hwnd = Child[i]->GetSafeHwnd();
::PostMessage( hwnd, WM_NACTIVATE, ((hwnd!=NULL && hwnd==hActivateTgt) ?
TRUE : FALSE), 0 );
}
}