itagagaki
@itagagaki (板垣 史彦)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

MFC: モードレスで開いた子ダイアログをアクティブにする

Q&A

Closed

WindowsのMFCアプリをメンテナンスしています。

ダイアログAがモーダルで表示され、そのとき子ダイアログBをモードレスで隣に開いています。
ダイアログAがアクティブになるのですが、ダイアログBを(ユーザーのクリックによらずに)アクティブにしたく、SetFocusSetActiveWindowなどいろいろ試したのですが、どうしてもうまくいきません。
いい方法があれば教えてください m(_ _)m

ダイアログAを開く(モーダル)

    CDialogA dialogA(this);
    dialogA.DoModal();

ダイアログAがダイアログBを作成してモードレスで隣に表示する

CDialogA::CDialogA(CWnd* pParent) : CDialog(CDialogA::IDD, pParent)
{
    m_dialogB = new CDialogB(this);
}

BOOL CDialogA::OnInitDialog()
{
    CDialog::OnInitDialog();
    return TRUE;
}

void CDialogA::OnShowWindow(BOOL bShow, UINT nStatus)
{
    CDialog::OnShowWindow(bShow, nStatus);
    if (m_dialogB) {
        m_dialogB->Create(IDD_B, this);
        RECT rect;
        GetWindowRect(&rect);
        m_dialogB->SetWindowPos(this, rect.right + 1, rect.top, 0, 0, SWP_NOSIZE);
    }
}
0

1Answer

SetWindowPosのHWND_TOPMOSTで最前面に出させるという方法があるようです。

OSのバージョンでもAPIの動作に差異があるようで。

0Like

Comments

  1. @itagagaki

    Questioner

    情報ありがとうございます。試してみます。

  2. ちょっと勘違いしていました、最前面化ではなくアクティブ化がメインでしたね。
    ダイアログBが先にアクティブになって、その後にダイアログAがアクティブになってフォーカスが奪われているという可能性はないですか?

  3. @itagagaki

    Questioner

    そうです。Bが最前面にはなってはいるのですが、Aのほうがアクティブになっているのです。
    AがDoModalでアクティブとなり、BがCreateでアクティブとなるが、その後またAに移ってしまっているという可能性ですね。その視点も頭に入れて調べてみます。
    ただ、もしそうだとしても、その後さらにまたBを明示的にアクティブ化できても良さそうに思うのですが、どうやっても成功していないというのが現状です。。

  4. 試しにダイアログを表示するだけのMFCアプリを作成してみましたが、
    OnShowWindowでm_dialogBを表示せずに独自メッセージをPostMessageして、

    static UINT _WmInitialized;
    
    void CDialogA::OnShowWindow(BOOL bShow, UINT nStatus)
    {
    	CDialogEx::OnShowWindow(bShow, nStatus);
    
    	_WmInitialized = RegisterWindowMessage(L"CDialogA_Initialized");
    	this->PostMessage(_WmInitialized);
    }
    

    独自メッセージを受け取ったタイミングでm_dialogBを表示すると、フォーカスがm_dialogBになる事は確認できました。

    BOOL CDialogA::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
    	if (message == _WmInitialized)
    	{
    		if (m_dialogB) {
    			m_dialogB->Create(ID_DIALOG_B, this);
    			m_dialogB->ShowWindow(SW_SHOW);
    			RECT rect;
    			GetWindowRect(&rect);
    			m_dialogB->SetWindowPos(this, rect.right + 1, rect.top, 0, 0, SWP_NOSIZE);
    		}
    	}
    
    	return CDialogEx::OnWndMsg(message, wParam, lParam, pResult);
    }
    

    確信を持っている訳ではないですが、ウィンドウが完全に表示されるまでに、OnShowWindowの後にもいくつかのウィンドウメッセージが処理されるので、そこでフォーカス処理を含めた色々な初期化処理が行われているのではないかと推測します。

    今回の実験は、PostMessageで独自メッセージを投げて、メッセージキューに既に積まれているウィンドウメッセージの処理が終わった後にCDialogBの表示処理をずらす事で、フォーカス移動が出来るのではないかという想定です。フォーカス移動を実際に行っているウィンドウメッセージを特定し、そのイベントハンドラを書くのがベストかもしれませんが、面倒なのでそこまでは調べていません。

  5. @itagagaki

    Questioner

    なるほど!わざわざ実験までしていただいてありがとうございました。真似してみて結果を報告させていただきます。

  6. @itagagaki

    Questioner

    うまくいきました!
    そしてまた一つ勉強になりました。ありがとうございました。

Your answer might help someone💌