【问题标题】:Handling WM_LBUTTONDOWN and WM_LBUTTONUP inside WM_COMMAND win32 GUI C++在 WM_COMMAND win32 GUI C++ 中处理 WM_LBUTTONDOWN 和 WM_LBUTTONUP
【发布时间】:2019-12-19 20:59:43
【问题描述】:

我正在创建一个带有如下下拉列表的窗口:

如果我点击前四个选项中的任何一个,包括它们的子选项,那么我想处理那个选项。问题是我需要在这些选项中使用WM_LBUTTONDOWNWM_LBUTTONUP 和其他类似的东西,但不知道如何使用。

我不知道在case WM_COMMAND 内部做什么来处理所有这些选项,所以这是我的WindowProcedure

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_LBUTTONDOWN
        case WM_CREATE:
            AddMenu(hwnd);
            break;

        case WM_COMMAND:

            switch(wParam)
            {
                case Line_DDA:
                    break;
                case Line_Midpoint:
                    break;
                case Line_Parametric:
                    break;

                case Ellipse_Direct:
                    break;
                case Ellipse_Polar:
                    break;
                case Ellipse_Midpoint:
                    break;

                case Line_Clipping:
                    break;

                case Convex_Filling:
                    break;

                case Load_File:
                    break;

                case Save_File:
                    break;
            }
            break;

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

这是AddMenu()

void AddMenu(HWND hwnd)
{
    hMenu = CreateMenu();
    HMENU hSubMenu = CreateMenu();
    HMENU hLineMenu = CreateMenu();
    HMENU hEllipseMenu = CreateMenu();

    AppendMenu(hLineMenu, MF_STRING, Line_DDA, "DDA");
    AppendMenu(hLineMenu, MF_STRING, Line_Midpoint, "Midpoint");
    AppendMenu(hLineMenu, MF_STRING, Line_Parametric, "Parametric");

    AppendMenu(hEllipseMenu, MF_STRING, Ellipse_Direct, "Direct");
    AppendMenu(hEllipseMenu, MF_STRING, Ellipse_Polar, "Polar");
    AppendMenu(hEllipseMenu, MF_STRING, Ellipse_Midpoint, "Midpoint");

    AppendMenu(hSubMenu, MF_POPUP, (UINT_PTR)hLineMenu, "Line");
    AppendMenu(hSubMenu, MF_POPUP, (UINT_PTR)hEllipseMenu, "Ellipse");
    AppendMenu(hSubMenu, MF_SEPARATOR, NULL, NULL);
    AppendMenu(hSubMenu, MF_STRING, Line_Clipping, "Line Clipping");
    AppendMenu(hSubMenu, MF_SEPARATOR, NULL, NULL);
    AppendMenu(hSubMenu, MF_STRING, Convex_Filling, "Convex Filling");
    AppendMenu(hSubMenu, MF_SEPARATOR, NULL, NULL);
    AppendMenu(hSubMenu, MF_STRING, Load_File, "Load");
    AppendMenu(hSubMenu, MF_STRING, Save_File, "Save");

    AppendMenu(hMenu, MF_POPUP,(UINT_PTR)hSubMenu , "Draw");

    SetMenu(hwnd, hMenu);
}

【问题讨论】:

  • 需要“在这些选项中使用 WM_LBUTTONDOWN、WM_LBUTTONUP 和其他类似的东西”是什么意思?你的意思是当一个菜单项触发时你想根据鼠标按钮的状态执行不同的行为?
  • 是的,完全正确。我想画一条线,椭圆..等,为此我需要获取它的坐标(x,y),WM_LBUTTONDOWNWM_LBUTTONUP 可以使用它的坐标
  • 哦,所以你想知道如何实现用户扫出的整个交互,例如通过鼠标拖动椭圆?
  • 您不会将WM_LBUTTON... 消息与菜单本身一起使用,而仅与窗口一起使用。 WM_COMMAND 将告诉您用户选择了哪个菜单项。在某处的变量中记住该选择。 THEN,当你稍后得到WM_LBUTTONDOWN时,将那些鼠标坐标记住在另一个变量中,然后当你得到WM_MOUSEMOVE时,你可以根据需要在两组坐标之间绘制,基于第一个变量的值你保存了,当你收到WM_LBUTTONUP时停止绘图。

标签: c++ win32gui


【解决方案1】:

Remy Lebeau 的评论指出了正确的方向。让我们更具体一点。

在事件驱动编程中,您通常需要知道过去发生了什么,才能决定现在要做什么。当事件发生时,您需要知道事件是什么(例如,WM_LBUTTONDOWN)当前状态。

对于您的程序,我们需要跟踪一些不同的东西,所以让我们将它们捆绑到一个结构中。

struct State {
  enum { Idle, WaitingForPoint0, WaitingForPoint1, DrawIt } action;
  DrawType type;  // e.g., Line_DDA, Line_Midpoint, Ellipse_Direct, etc.
  POINT points[2];
};

您需要某种方式将这个State 的一个实例与您的窗口的每个实例相关联。现在,我们只是将其设为全局变量,并假设您的程序中只有这种类型的窗口。

State g_state = {Idle};

当用户从菜单中选择一个绘图选项时,您会更新当前状态。例如:

case Line_DDA:
    g_state.action = WaitingForPoint0;
    g_state.type = LineDDA;
    break;

case Line_Midpoint:
    g_state.action = WaitingForPoint0;
    g_state.type = Line_Midpoint;
    break;

// and so on

当用户按下按钮时,我们必须检查当前状态以知道该做什么。

case WM_LBUTTONDOWN:
    switch (g_state.action) {
        case Idle:  // just ignore the click
            break;
        case WaitingForPoint0:
            g_state.points[0].x = GET_X_LPARAM(lParam);
            g_state.points[0].y = GET_Y_LPARAM(lParam);
            g_state.action = WaitingForPoint2;
            break;
        case WaitingForPoint1:
            g_state.points[1].x = GET_X_LPARAM(lParam);
            g_state.points[1].y = GET_Y_LPARAM(lParam);
            g_state.action = DrawIt;
            InvalidateRect(hWnd, NULL, TRUE);
            break;
    }
    return 0;

最后,当窗口需要更新时,我们检查它的状态,找出它显示的形状。

case WM_PAINT:
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    if (g_state.action == DrawIt) {
      switch (g_state.type) {
          case Line_DDA:
              YourDrawLineDDA(hdc, g_state.points[0], g_state.points[1]);
              break;
          case Line_Midpoint:
              YourDrawLineMidpoint(hdc, g_state.points[0], g_state.points[1]);
              break;
          // and so on
      }
    }
    EndPaint(hwnd, &ps);
    return 0;

这可能会变得非常冗长,但它说明了您想要做的事情的基本要素。您当然可以考虑使这种类型的代码更简洁、优雅和可扩展。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 2013-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多