EnterとSpaceの同時押しでCButtonのイベントが2回発生してしまう

[戻る]
ヤーナム 2018/04/17(火) 19:53:36
Win7、MFC

ボタンを押すとMessageBoxを表示するボタンを作っているのですが、
EnterキーとSpaceキーを同時に押すと、MessageBoxが2回表示されてしまいます。

マウスクリック連打や、Enterキー連打、テンキーと普通のEnterキー2つを同時押し、
では、この現象は発生せず、EnterとSpaceの組み合わせの場合だけ発生します。

これを防ぐにはどのような手段があるでしょうか?
今考えているのは、ボタンが押されたら500msほどはイベントを受け取らない、
としようかと考えているのですがもっとスマートな方法が無いものかと・・・

アドバイスでも構いませんんので、よろしくご教授お願いいたします。

snao 2018/04/18(水) 21:52:24
今まで気が付きませんでした。

非MFCで試したところ、スペースを押し下げ中に、エンターを押して離し、その後にスペ
ースを離すとMessageBoxが2回出ました。

1.スペースを押し下げるとボタンが下がった状態になる。
2.エンターを押すと、コマンドが発生して、MessageBoxを作り、元のダイアログは非アク
ティブになり、ボタンは元に戻る(MessageBoxはまだ表示されない)。
3.ボタンが元に戻るときに再度コマンドが発生して、MessageBoxを作り、こっちのMessag
eBoxが表示される。
4.後のMessageBoxを閉じると、前のMessageBoxが表示される。

こんな動作のようです。

対策として MessageBoxを出す前にボタンを上げることを思いつきました。

非MFCだと OnButtonClickedの先頭で
HWND hwndButton = GetDlgItem(hDlg, IDC_BUTTON);
UINT state = SendMessage(hwndButton, BM_GETSTATE, 0, 0);
if (state & BST_PUSHED)
  SendMessage(hwndButton, BM_SETSTATE, 0, 0);

snao 2018/04/19(木) 05:02:13
こっちのほうが楽ですね。

if(GetActiveWindow() == hDlg)
  MessageBox(hDlg, TEXT("message"), TEXT("caption"), MB_OK);

ヤーナム 2018/04/20(金) 16:54:45
お返事遅くなってしまって申し訳ありません。
アドバイス頂きありがとうございます。

2つ目の方法ですと、ポップアップでダイアログが
出てくる場合限定の処理になってしまうので
1つ目の方法を試させて頂きました。

MFCでサブクラス化していたので以下のようにしてみました。

void CMyButton::OnBnClicked()
{
    UINT state = SendMessage(BM_GETSTATE, 0, 0);
    if (state & BST_PUSHED)
    {
        // ボタン押下を無視
        SendMessage(BM_SETSTATE, 0, 0);
        return;
    }

    // ボタン押下
    GetParent()->PostMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), 
BN_CLICKED));
}

これだと、同時押しで一回もボタンイベントが発生しない場合がありました。
無視のほうに流れてしまうみたいです。

snao 2018/04/20(金) 17:48:27
MFCは良くわからないので想像ですが

CButton MyButton; として、

void CMyDlalog::OnBnClickedMyButton()
{
    UINT state = MyButton.GetState();
    if (state & BST_PUSHED)
    {
        // 押し下げた状態ならば元に戻し、処理を続行する
        MyButton.SetState(FALSE);
    }
    MessageBox("here");
}

こんな感じでいかがでしょうか。

ヤーナム 2018/04/27(金) 15:34:44
また返信遅くなってしまい申し訳ありません。

ありがとうございます。以下の内容で期待通りの動作ができました。
そもそも、↑に書いた私のコードではreturnが不要だったようです。
無駄に入れてしまいました。すみません。

void CMyButton::OnBnClicked()
{
    UINT state = GetState();
    if (state & BST_PUSHED)
    {
        // ボタン押下を無視
        SetState(FALSE);
    }

    // ボタン押下
    GetParent()->PostMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), 
BN_CLICKED));
}


ついでにお聞きしたいのですが、
このBM_SETSTATE(SetState)は何をしているのでしょうか?

https://msdn.microsoft.com/ja-jp/library/ebw1hfe8.aspx
MSDNの説明を読んでも、強調表示を削除、と記載されており、
いまいち意味がわかりません。

動きから見て、一回目のBST_PUSHEDがある場合は
ボタンを押し上げてクリックイベントを無効化する、だと思いますが
それだreturnでよいのでは?と思ってしまいました。
処理を続行しても上手くいっている理由が何故なのかが良くわかりません。

snao 2018/04/27(金) 19:11:51
https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb761823(v=vs.85).aspx
ここのRemarksに拠れば、highlighted とはPushButtonが押された状態の表示です。

さて、PushButtonをSpaceBarで押した状態(強調表示)でSetFocusでフォーカスを移動する
とBN_CLICKEDが生じます。

void MyButton::KeyDown()
   Parent->PostMessage(WM_APP);
   Button::KeyDown();
   // ここで、SetState(FALSE)を呼ぶと、BN_CLICKEDは発生しない

void MyParentWindow::WmApp()
   SetFocus();

--------------------------------
当初の症状は、
1)SpaceBarを押し下げる(buttonは強調表示になる)
2)Enterを押し下げる
  2-1) BN_CLICKEDが発生し、MessageBoxを作る
  2-2) ButtonのフォーカスがMessageBoxに移動する
  2-3) 強調表示のButtonからフォーカスが移動したので BN_CLICKEDが発生する
    2-3-1)MessageBoxが表示される
  2-4) 最初のMeeageBoxが表示される
3)SpaceBarを解放する

私の提案は、上記の 2-3)を防ぐものです。
上記の 2)でEnterの代わりにニーモニクキー(ボタンリソースで"&Button"など)が押され
た場合は、不具合は生じませんでした。これは、2-1)の直前にSetState(TRUE),SetState(
FALSE)が入るためでしょう。

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