コンテキストメニュー処理が終わるタイミング

[戻る]
NOR 2008/02/12(火) 21:19:50
Visual C++ 2005です。
CView派生クラスのOnContextMenu()内でTrackPopupMenu()を使っています。

このTrackPopupMenu()の前に、コマンドハンドラや
ON_UPDATE_COMMAND_UIで参照される情報を作成しておき、
コンテキストメニュー終了後には毎回その情報をクリアするようにしたいのですが、
実際にコマンドハンドラが呼ばれるのはOnContextMenu()を抜けてからに
なっているようで、OnContextMenu()内に情報をクリアする処理を入れると、
実際のコマンドが呼ばれなくなってしまいます。

以下がルーチンの概要です。
m_bEnableをTRUEにした状態でTrackPopupMenu()をコールしていて、
実際のコンテキストメニューのコマンド自体は選択可能になるのですが、
それを選択したあとに再度コールされるOnUpdateAaaa32771()では、
すでにm_bEnableがFALSEに戻ってしまっていて、
実際のコマンドハンドラであるOnAaaa32771()も呼ばれません。

void CAAAAView::OnAaaa32771()
{
    AfxMessageBox(_T("OnAaaa32771"));
}

void CAAAAView::OnUpdateAaaa32771(CCmdUI *pCmdUI)
{
    pCmdUI->Enable(m_bEnable);
}

void CAAAAView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
{
    CMenu menu;
    menu.LoadMenu(IDR_MAINFRAME);
    CMenu* pMenu = menu.GetSubMenu(4);

    m_bEnable = TRUE;
    pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
                          point.x, point.y, AfxGetMainWnd());
    m_bEnable = FALSE;
}

メニューがキャンセルされる可能性もあるので、
コマンドハンドラ内でクリアするわけにもいきません。
実際のコンテキストメニュー処理が終わってから情報をクリアするためには
どのようなタイミングがあるのでしょうか?

玲音 (st.lain) 2008/02/13(水) 10:25:35
以下とか、参考にならないのカナ。

>> Topメニューopen/close時のイベント
> http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200406/04060044.txt

> このTrackPopupMenu()の前に、コマンドハンドラや
> ON_UPDATE_COMMAND_UIで参照される情報を作成しておき、
情報、というのがどのようなものかは分かりませんが、OnAaaa32771()内で使用する
処理の前計算のようである場合はびみょ〜な感じがします。
私的には、OnContextMenu()で行う処理の主としては、動的なメニューの追加削除
のみに留めたほうが無難かと思われます。

シャノン 2008/02/13(水) 10:34:40
> TrackPopupMenu()の前に、コマンドハンドラや
> ON_UPDATE_COMMAND_UIで参照される情報を作成しておき、
> コンテキストメニュー終了後には毎回その情報をクリアするようにしたい

そういう設計にしない、という選択肢は取れませんか。
メニューが選択された場合、メッセージキューに WM_COMMAND がポストされ、
それが処理されるタイミングでコマンドハンドラが実行されますので、
メニューが表示されたり閉じられたりしたタイミングでは、
コマンドハンドラがいつ呼ばれるのか予測できません。

あまり格好良くない方法としては、TrackPopupMenu のフラグに TPM_NONOTIFY と
TPM_RETURNCMD を含めれば、TrackPopupMenu の戻り値として、選択された
アイテムの ID(キャンセルされた場合は 0)が返り、コマンドハンドラが
呼ばれなくなりますので、この戻り値を見て、自分でコマンドハンドラを呼ぶ、とか。

NOR 2008/02/13(水) 12:35:15
[[解決]]
玲音 (st.lain)さん、シャノンさん、解説ありがとうございます。

> 情報、というのがどのようなものかは分かりませんが、OnAaaa32771()内で使用する
> 処理の前計算のようである場合はびみょ〜な感じがします。

この情報は、実際にはOnRButtonDown()時に作成しています。

やりたかったことは、特定のマウス位置(文字や画像の上)での
右ボタン限定によるコンテキストメニュー処理です
(マウス限定なのでOnContextMenu()ではなくOnRButtonUp()でもよいはず)。

OnRButtonDown()時にマウス位置の情報を憶えて対象物の強調表示を開始し、
そのままドラッグせずにOnContextMenu()に来たときには、
その対象物に対応するコンテキストメニューを出し、
メニュー終了時には現在位置情報を無効に戻しておきたいと思っています。

操作感覚的には、ボタンをクリックした直後に、
ボタン自体は凹んだままコンテキストメニューを出し、
メニューが消えたらボタンが普通の状態に戻るような感じです。

そして、実際のコマンドハンドラは、この「現在位置情報」を参照するので、
コマンドハンドラが終わるまでは無効に戻すわけにいかないのです。

> あまり格好良くない方法としては、TrackPopupMenu のフラグに TPM_NONOTIFY と
> TPM_RETURNCMD を含めれば、TrackPopupMenu の戻り値として、選択された
> アイテムの ID(キャンセルされた場合は 0)が返り、コマンドハンドラが
> 呼ばれなくなりますので、この戻り値を見て、自分でコマンドハンドラを呼ぶ、とか

こんなフラグがあったのですね。
CMenu::TrackPopupMenu()の説明ばかり読んでいました。

ネットで探してみたところ、同じように
TrackPopupMenu(TPM_RETURNCMD | TPM_NONOTIFY...)とコールして、
「後片付け」をしてから自分自身にSendMessage(WM_COMMAND...)で送り直す
という方法のサンプルがありました。

格好良くないと言われてしまうと厳しいですが(^^;
今の状況だとこれが一番近道のような気もします。
最悪この方法でも出来るということで、最終手段で使わせていただきます。

NOR 2008/02/13(水) 15:24:19
TrackPopupMenu()のフラグについて、追加で質問させていただきます。

TrackPopupMenu()にTPM_NONOTIFYのみを追加すると、
メニュー表示直前のUPDATE_COMMAND_UIハンドラだけがコールされない状態になり、
WM_COMMANDハンドラは相変わらず選択時に自動でコールされてしまいます。

TPM_NONOTIFYとTPM_RETURNCMDを追加すると、
UPDATE_COMMAND_UIハンドラもWM_COMMANDハンドラもコールされなくなります。

逆にTPM_RETURNCMDのみを追加すると、
UPDATE_COMMAND_UIハンドラは自動でコールされたまま、
WM_COMMANDハンドラはコールされなくなります。

MSDNの説明を読む限り、TPM_NONOTIFYを追加していない状態で
WM_COMMANDハンドラがコールされなくなるのは不自然な感じがするのですが、
他のかたもこのような動きになりますでしょうか?

UPDATE_COMMAND_UIハンドラをすでに用意している立場としては、
それのみ相変わらず自動でコールしてくれて、WM_COMMANDは自分で送り直す
という上記のTPM_RETURNCMDのみの挙動がむしろ都合がよいのですが、
なにか変なフラグが立っているのではと思い、質問させていただきました。
よろしくお願いいたします。

シャノン 2008/02/13(水) 18:29:26
コマンドハンドラとメニューキャンセルの両方のタイミングで後始末するんじゃだめ?
TPM_RETURNCMD を使わない場合のメニューキャンセルは、玲音さんが提示してくれたスレ
で書いていますが、OnMenuSelect で検出できます。

NOR 2008/02/13(水) 21:07:59
[[解決]]
> コマンドハンドラとメニューキャンセルの両方のタイミングで後始末するんじゃだめ?

たしかに、メニュー処理自体はそのままにして、
WM_MENUSELECTのキャンセル対応の中でも後始末をしてしまったほうが
他のポップアップメニュー処理と同じようなメッセージ順にできますね。

OnMenuSelect()の中で
if (nFlags == 0xFFFF && hSysMenu == 0) {
    ここでもコマンドハンドラと同じように後始末;
}
というように追記したところ、キャンセル時も現在位置情報を無効にできました。
ありがとうございます。

古いスレッドには追加発言できません。