区分单击双击C ++

时间:2013-03-05 14:49:43

标签: visual-c++ mfc mouse double-click

我有一个应用程序,双击图像视图区域会更改图像视图的布局。单击也会在图像上放置一个点。 我的问题是,双击时两种功能都有效。

当然我知道,当双击发生时,控件首先进入LButtonDown。双击时,我不希望点功能工作。我一直在努力工作一个多星期。请帮忙。

6 个答案:

答案 0 :(得分:9)

解决此问题的最简单方法是构建finite-state machine来处理鼠标点击。 基本上,这将是一个单例对象,它从您当前使用的鼠标单击事件中获取输入。 它的输出将是SingleClickDetected, DoubleClickDetected, ...。 红色箭头表示您向应用程序的其余部分报告的事件。 括号表示您要报告的事件。

FSM

当然,如果您必须直接处理MouseDownMouseUp事件而不是MouseClick事件,则必须修改此状态机。 它会略大一点,但想法基本相同。

编辑: 从评论中看,Windows似乎没有干净地报告单击和双击,你需要将它们分开。 这种情况的状态机: FSM2

这对你正在尝试做的事情来说可能有点过头了,特别是因为大多数(如果不是所有的)基于GUI的程序在历史上都没有曾经使用了双击拖动。 它确实显示了基本思想,并展示了如何扩展状态机以处理不同类型的按钮点击。 此外,如果您愿意,您可以处理双击右键,包含左右按钮的拖动,或者您可以想到并合并到UI中的任何其他场景。

答案 1 :(得分:1)

我编写了以下代码,但它确实有用。

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;
        }
    ,...
    }

答案 2 :(得分:0)

尝试存储最后一个LButtonDown的时间戳;如果上一个时间戳与当前事件中产生的时间戳之间的时差太短,您可以取消操作(但仍然存储新的LButtonDown时间戳)

答案 3 :(得分:0)

您唯一能做的就是每次收到点击事件时等待很短的时间,并测试在此期间是否在执行单击响应之前没有发生等效的双击事件。这可能是新错误和无响应的UI的来源。也许尝试改变用户交互来抛弃问题。

编辑:你正在解决这个问题一个多星期的事实是一个糟糕的用户交互设计的症状。 “双击”仍然意味着发生两次点击,这意味着应用程序自然应该执行单击操作。检查桌面上安装的应用程序的ui以验证这一点。您是否考虑过使用其他用户媒体来触发UI响应?例如,您可以使用右键将点放在图像上。

答案 4 :(得分:0)

@ E.T的回答是现货。要实现类似的功能,您需要一个与消息循环一起运行的计时器。

在我的应用程序中,我希望能够区分鼠标向下/向上与双击,因为我想要双击不撤消拖动操作(想象选择框左键拖动并双击缩放以适合)。 / p>

以下代码使用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);
}

答案 5 :(得分:0)

我真的很喜欢有限状态机的答案,但它有一个缺陷。 没有&#34;单击时间&#34;你可以超越。

如果仔细观察鼠标的工作情况,您会看到: -

单击不是一个事件而是= WM_LBUTTONDOWN,WM_LBUTTONUP与之间的时间无关,无论如何都会发生相应的动作

双击鼠标左键实际上会生成四个消息的序列:WM_LBUTTONDOWN,WM_LBUTTONUP,WM_LBUTTONDBLCLK和WM_LBUTTONUP 所以你应该使用第3个标志为你的优势

顺便说一句,我也在做这样的事情。谢谢!