9

简介及相关资料:

我制作了一个应用程序,当鼠标悬停在静态控件上方时,需要将光标的外观更改为手形,否则将其重置为普通光标。

我的初始应用程序处于全屏模式,但最近条款发生了变化,它必须有一个可调整大小的窗口。

这意味着WM_SETCURSOR必须重写我的处理程序以反映新引入的更改。

游标已加载WM_CREATE,我已经定义了类游标,如下所示:

   // cursors 
   case WM_CREATE:
      hCursorHand = LoadCursor( NULL, IDC_HAND );
      hCursorArrow = LoadCursor( NULL, IDC_ARROW );
      // other stuff

在我的课堂上:

   WNDCLASSEX wc;
   // ...
   wc.hCursor = hCursorArrow;
   //...

这是我的旧WM_CURSOR处理程序(为了清楚起见,代码被简化):

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
             SetCursor(hCursorHand);
        else
             SetCursor(hCursorArrow);
        return TRUE;

如果光标悬停在静态控件上方,则我的处理程序将其更改为手动,否则将其设置为默认光标(箭头)。

Bellow 是我在 Paint 中绘制的图片,当光标悬停在静态控件上方、位于客户区域以及用户调整窗口大小时,它会显示所需的光标外观。

在此处输入图像描述

如果需要额外的代码片段,请询问,我将编辑我的帖子,但现在,它们被省略以保持帖子简短和简洁。

我在 Windows XP 上工作,使用 MS Visual Studio C++ 和纯 Win32 API。

我为解决问题所做的努力:

下面是我尝试过的代码片段,但它们都失败了:

第一个片段:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE; 
        }
        else
             return DefWindowProc( hWnd, msg, lParam, wParam );

第二个片段:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE; 
        }
        break; // based on MSDN example

第三个片段:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE; 
        }
        else
             return FALSE;

这些将光标设置为手,无论它在哪里。

如果我让我的WM_SETCURSOR处理程序保持不变,我得到的唯一问题是,当我将鼠标悬停在边框上时,我得到的不是调整箭头大小,而是常规箭头(作为光标的外观),但是可以调整窗口大小。

如果我注释掉我的WM_SETCURSOR处理程序,大小箭头和光标箭头会正确显示,但是当光标悬停在静态控件上方时,光标不会变成手形(这是合乎逻辑的,因为没有WM_SETCURSOR处理程序可以更改它)。

我浏览了 SO 存档,查看了 MSDN、CodeProject、DaniWeb、Cprogramming 和 CodeGuru,但没有成功。

通过这些,我发现了一个示例,人们将低字词 lParam与命中测试代码进行比较。

通过 MSDN,我找到了命中测试值的链接(http://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx)并且我找到了链接游标类型(http://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx)。

目前我正在阅读它们,因为我认为我将不得不加载额外的游标资源,对命中测试值进行几次比较,然后使用这些资源来相应地设置游标外观。

问题:

我真的希望我的WM_SETCURSOR处理程序看起来像这样:

   case WM_SETCURSOR:
        if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
        {
             SetCursor(hCursorHand); 
             return TRUE;
        }
        else
             // reset cursor's look to default

所以我要求社区指导我如何做到这一点。

如果这不可能,那么我会考虑使用多个if语句来检查命中测试代码,并相应地设置光标的外观。

当然,如果我的问题有更好的解决方案,请提出建议,我也会考虑。

谢谢你。

问候。

4

3 回答 3

15

通常,如果您处理该WM_SETCURSOR消息,则必须

  • 调用SetCursor()设置光标,然后返回TRUE,或
  • 如果消息来自子窗口,则返回FALSE默认处理,或者
  • 如果消息来自您自己的窗口,请将消息传递给DefWindowProc()

我认为最后两点在 MSDN 文档中并没有说得很清楚。

鼠标指针下的窗口获得第一条WM_SETCURSOR消息。如果它处理它并在该点返回,则不会发生其他任何事情。但是,如果它调用DefWindowProc(),则 DWP 会将消息转发给窗口的父级进行处理。如果父级选择不处理它,它可以返回FALSE并且 DefWindowProc 处理将继续。

但这仅适用于消息来自先前对 DWP 的调用的情况。如果消息源自窗口本身,而不是子窗口,则返回TRUEFALSE不设置光标意味着根本不会设置光标。

另一件事:虽然您的问题没有具体说明,但我假设您使用的GetDlgItem()顶级窗口是一个对话框。如果这是真的,您不能只返回TRUEFALSE获取消息 - 您需要使用SetWindowLongPtr()返回值并将返回值存储在DWLP_MSGRESULT. 从对话过程返回FALSE表明您根本没有处理该消息 - 这相当于将消息传递给DefWindowProc().

因此,我认为在您的顶级窗口中正确处理您的情况是:

case WM_SETCURSOR:
    if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
    {
        SetCursor(hCursorHand); 
        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
        return TRUE;
    }
    return FALSE;

如果您的顶级窗口实际上不是对话框,您可以这样做:

case WM_SETCURSOR:
    if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) ) 
    {
        SetCursor(hCursorHand); 
        return TRUE;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
于 2013-10-08T20:33:06.133 回答
1

这是我的第一个示例,如果光标转到菜单栏,则光标变为光标手:

HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
    if(LOWORD(lParam) == HTMENU)
    {
          SetCursor(cursorHand);
    }
 break;

这是我的第二个示例,如果光标转到按钮,光标将变为 cursorHand:

HCURSOR cursorHand =  LoadCursor(NULL, IDC_HAND);

case WM_SETCURSOR:
      if(LOWORD(lParam) == buttonId)//declare you
      {
            SetCursor(cursorHand);
       }
   break;

警告:菜单栏和按钮未创建!创建你,请检查我的答案示例。

于 2020-06-05T05:11:46.807 回答
0

据我了解的问题是,给定一个父窗口还有一个子窗口(其中一个是静态控件),当光标在静态控件上时,如何将光标设置为手,当光标位于父窗口的客户区,当光标位于父窗口的非客户区时,默认处理。

为了检查一下,我编写了一个简单的程序,其中包含一个顶级窗口和一个静态控件作为子窗口。我的第一次尝试如下:

1) 设置顶层窗口的类光标为LoadCursor(NULL, IDC_ARROW)。这允许默认处理在适当时将光标设置为箭头。

2)通过调用我的 HandleWMMouseMove 函数处理WM_MOUSEMOVE消息来跟踪鼠标光标的位置,如下所示:


case WM_MOUSEMOVE:
    HandleWMMouseMove(lParam);
    break;

//ptCurrMousePos is a global variable of type POINT
static void     HandleWMMouseMove(LPARAM mousepos)
{
    ptCurrMousePos.x = (int)(short)(LOWORD(mousepos));
    ptCurrMousePos.y = (int)(short)(HIWORD(mousepos));
}

3)然后我所要做的就是通过调用我的 HandleWMSetCursor 函数来处理WM_SETCURSOR消息,如下所示:


    case WM_SETCURSOR:
        if (HandleWMSetCursor())
            return TRUE;
        break;

//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL     HandleWMSetCursor(void)
{
    if (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic) {
        SetCursor(hCursorHand);
        return TRUE;
    }

    return FALSE;
}

这工作得很好,但我无法理解问题中发布的代码是如何工作的,甚至部分工作。于是我问提问者子窗口是否真的是一个静态控件。答案是肯定的,但它是使用 SS_NOTIFY 样式创建的。所以我用这种风格创建了我的子窗口,我的代码停止工作,但发布的代码开始工作。在随 Visual Studio 分发的 Spy++ 程序的帮助下,我学到了以下内容。

静态控件通常是透明的(不是视觉意义上的,而是意味着即使鼠标在透明窗口上方,Windows 也会认为鼠标在透明窗口下方的窗口上方)。

当您使用 SS_NOTIFY 创建静态控件时,控件不再透明(即它开始接收进程鼠标消息(如 WM_MOUSEMOVE),因此我的代码停止工作,因为当光标位于静态控件上时它从未收到 WM_MOUSE 消息。另一方面,当在没有 SS_NOTIFY 的情况下创建静态控件时,问题中的代码不起作用,因为没有这种样式,静态控件是透明的,这意味着 WM_SETCURSOR 消息中的 WARAM 永远不会等于静态控件(换句话说,Windows从未考虑过鼠标在静态控件上,因为它是透明的)。

可以通过将 WM_SETCURSOR 处理函数更改为以下内容来组合这两种方法:


//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL     HandleWMSetCursor(WPARAM wParam)
{
    if (((HWND)wParam == hwndStatic) || (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic)) {
        SetCursor(hCursorHand);
        return TRUE;
    }

    return FALSE;
}
于 2013-10-09T03:46:35.747 回答