我有一个应用程序,双击图像视图区域会更改图像视图的布局。同时单击图像上会放置一个点。 我的问题是,当双击时这两个功能都起作用。
当然我知道,当发生双击时控件首先会进入LButtonDown状态。但我不希望在发生双击时点的功能也起作用。我已经为此调试了一周多时间,请帮忙解决。
我有一个应用程序,双击图像视图区域会更改图像视图的布局。同时单击图像上会放置一个点。 我的问题是,当双击时这两个功能都起作用。
当然我知道,当发生双击时控件首先会进入LButtonDown状态。但我不希望在发生双击时点的功能也起作用。我已经为此调试了一周多时间,请帮忙解决。
解决这个问题最简单的方法是构建一个处理鼠标点击的有限状态机。
基本上,这将是一个单例对象,它接收来自当前使用的鼠标点击事件的输入。
它的输出将是SingleClickDetected, DoubleClickDetected, ...
。
红色箭头表示您正在向应用程序的其余部分报告的事件。
括号表示您正在报告的事件。
我编写了以下代码,它可以正常运行。
UINT TimerId;
int clicks;
VOID CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
KillTimer(NULL, TimerId);
if (clicks < 2 && !double_click){
MessageBox(hWnd, L"Show Widget", L"Widget", MB_OK);
}
clicks = 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
UINT uID;
UINT uMouseMsg;
uID = (UINT)wParam;
uMouseMsg = (UINT)lParam;
if (uMouseMsg == WM_LBUTTONDBLCLK){
double_click = true;
MessageBox(hWnd, L"Double click", L"CAPT", MB_OK);
return 0;
}
if (uMouseMsg == WM_LBUTTONDOWN){
double_click = false;
clicks++;
//single click opens context menu
if (clicks == 1){
TimerId = SetTimer(NULL, 0, 500, &TimerProc);
}
return 0;
}
,...
}
@E.T的回答非常准确。要实现这样的功能,您确实需要在消息循环中运行一个计时器。
在我的应用程序中,我希望能够区分鼠标按下/松开和双击,因为我不希望双击取消拖动操作(想象一下使用左键拖动选择框并双击缩放以适应屏幕)。
以下代码使用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);
}
click
或 doubleclick
(从不同时),则就应用而言,它并不是一个独立的事物。 - UmNyobe我非常喜欢有限状态机的答案,但其中存在一个缺陷。
实际上不存在“单击时间”这样的东西,你无法超过它。
如果你仔细观察鼠标的工作原理,你会发现:
单击并不是一个事件,而是 WM_LBUTTONDOWN、WM_LBUTTONUP 两个独立的事件,它们之间的时间不影响适当的操作。
双击左键实际上生成了四个消息:WM_LBUTTONDOWN、WM_LBUTTONUP、WM_LBUTTONDBLCLK 和 WM_LBUTTONUP,所以你应该利用第三个标志位。
顺便说一下,我也在做类似的事情。谢谢!