画像ビュー領域をダブルクリックすると画像ビューのレイアウトが変わるアプリケーションがあります。また、シングルクリックでドットが画像上に配置されます。私の問題は、ダブルクリックすると両方の機能が機能することです。
もちろん、ダブルクリックが発生すると、コントロールは最初に LButtonDown に移動します。ダブルクリックが発生したときにドット機能を動作させたくありません。私はこれに1週間以上取り組んできました。助けてください。
画像ビュー領域をダブルクリックすると画像ビューのレイアウトが変わるアプリケーションがあります。また、シングルクリックでドットが画像上に配置されます。私の問題は、ダブルクリックすると両方の機能が機能することです。
もちろん、ダブルクリックが発生すると、コントロールは最初に LButtonDown に移動します。ダブルクリックが発生したときにドット機能を動作させたくありません。私はこれに1週間以上取り組んできました。助けてください。
これを解決する最も簡単な方法は、マウス クリックを処理するための有限状態マシンを構築することです。基本的に、これは現在使用しているマウス クリック イベントから入力を取得するシングルトン オブジェクトになります。その出力は になりますSingleClickDetected, DoubleClickDetected, ...
。赤い矢印は、アプリケーションの残りの部分に報告しているイベントを示しています。括弧は、報告するイベントを示します。
もちろん、イベントではなくイベントを直接処理する必要がある場合は、このステート マシンを変更する必要がMouseDown
ありMouseUp
ますMouseClick
。少し大きくなりますが、考え方は基本的に同じです。
編集:コメントから、Windowsはシングルクリックとダブルクリックを明確に報告していないようで、それらを分離する必要があります。このシナリオのステートマシン:
特に、すべての歴史の中ですべてではないにしてもほとんどの GUI ベースのプログラムがダブルクリックのドラッグを使用したことがないため、これはおそらくあなたがやろうとしていることに対してやり過ぎです。これは基本的な考え方を示しており、ステート マシンを拡張してさまざまな種類のボタン クリックを処理する方法を示しています。さらに、必要に応じて、ダブル右クリック、左ボタンと右ボタンの両方を含むドラッグ、または考えて UI に組み込むことができるその他のシナリオを処理できます。
唯一できることは、クリックイベントを受信するたびに短時間待機し、その間にダブルクリックイベントに相当するものが発生しないかどうかをテストしてから、シングルクリック応答を実行することです。これは、新しいバグや応答しないUIの原因となる可能性があります。たぶん、ユーザーの操作を変更して、問題を解決してみてください。
編集:あなたがこれを1週間以上回避しているという事実は、悪いユーザーインタラクションデザインの症状です。「ダブルクリック」は、それでも2回のクリックが発生することを意味します。つまり、アプリケーションは当然、シングルクリックの操作を実行する必要があります。デスクトップにインストールされているアプリのUIをチェックして、これを確認します。別のユーザーメディアを使用してUI応答をトリガーすることを検討しましたか?たとえば、右ボタンを使用して画像にドットを配置できます。
@ETの答えは的を射ています。そのようなものを実装するには、メッセージ ループと共に実行されるタイマーが本当に必要です。
私のアプリケーションでは、マウスのダウン/アップとダブルクリックを区別できるようにしたかったのは、ダブルクリックでドラッグ操作を元に戻さないようにしたいからです(左ボタンのドラッグとダブルクリックでズームしてフィットする選択ボックスを想像してください)。
次のコードは、 を使用してこれを行いPreTranslateMessage
ます。私は怠惰からタイマーを追加しませんでした。そのため、左ボタンを押した後すぐにマウスを動かさないと、UI の動作が少し「おかしく」なります。私の場合、これは小さな問題です。
BOOL MyWindow::PreTranslateMessage(MSG *pMsg)
{
//From https://msdn.microsoft.com/en-us/library/windows/desktop/ms645606(v=vs.85).aspx
//Double-clicking the left mouse button actually generates a sequence of four messages:
//WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP
//So here's the problem. If an button up message arrives, we can't just
//take it, because it may be followed by a double click message. So we check
//for this.
//Ideally you need a timer - what happens if you don't get any messages
//after the button up or down? But here we get lazy and assume a message
//will come "soon enough".
static bool upMessageStored = false;
static bool dnMessageStored = false;
static MSG upMessage, dnMessage;
static bool processDnNow = false, processUpNow = false;
//This logic sequence absorbs all button up and down messages, storing them to be sent
//if something other than a double click message immediately follows.
if (!(pMsg->message == WM_LBUTTONDBLCLK ||
pMsg->message == WM_LBUTTONDOWN ||
pMsg->message == WM_LBUTTONUP) &&
(upMessageStored || dnMessageStored))
{
//If we receive any message other than double click and we've stored the messages,
//then post them.
Output::Message(L"Stored messages posted.\n");
if (upMessageStored)
{
processUpNow = true;
upMessageStored = false;
this->PostMessage(upMessage.message, upMessage.wParam, upMessage.lParam);
}
if (dnMessageStored)
{
processDnNow = true;
dnMessageStored = false;
this->PostMessage(dnMessage.message, dnMessage.wParam, dnMessage.lParam);
}
return TitlelessWindow::PreTranslateMessage(pMsg);
}
if (pMsg->message == WM_LBUTTONDOWN && !processDnNow)
{
Output::Message(L"WM_LBUTTONDOWN absorbed; message stored\n");
dnMessage = *pMsg;
dnMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONUP && !processUpNow)
{
Output::Message(L"WM_LBUTTONUP absorbed; message stored\n");
upMessage = *pMsg;
upMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONDBLCLK)
{
Output::Message(L"WM_LBUTTONDBLCLK; stored message discarded\n");
upMessageStored = false;
dnMessageStored = false;
processUpNow = false;
processDnNow = false;
}
//If we get here, we are processing messages normally. Be sure we clear the flags
//for up and down.
processDnNow = false;
processUpNow = false;
return ParentClass::PreTranslateMessage(pMsg);
}
最後の LButtonDown のタイムスタンプを保存してみてください。最後のタイムスタンプと現在のイベントで生成されたタイムスタンプの時間差が短すぎる場合は、操作をキャンセルできます (ただし、新しい LButtonDown タイムスタンプは引き続き保存されます)。