我已经创建了一个应用程序,当鼠标悬停在静态控件上方时,需要将光标的外观更改为手,否则会将其重置为普通光标。
我的初始应用程序处于全屏模式,但最近条款已更改,并且必须具有可调整大小的窗口。
这意味着我必须重写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。
Bellow是我尝试过的代码片段,但它们都失败了:
第一个片段:
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
语句来检查命中测试代码,并相应地设置光标外观。
当然,如果我的问题有更好的解决方案,请提出建议,我也会考虑。
谢谢。
问候。
答案 0 :(得分:6)
通常,如果您处理WM_SETCURSOR
消息,则必须
SetCursor()
设置光标,然后返回TRUE
或FALSE
进行默认处理,或DefWindowProc()
我认为MSDN文档中没有明确说明最后两点。
鼠标指针下的窗口获取第一条WM_SETCURSOR
消息。如果它处理它并在那时返回,则不会发生任何其他事情。但是,如果它调用DefWindowProc()
,则DWP会将消息转发给窗口的父级以进行处理。如果父选择不处理它,它可以返回FALSE
并且DefWindowProc处理将继续。
但这仅适用于此消息来自之前对DWP的调用。如果消息来自窗口本身而不是子节点,则返回TRUE
或FALSE
而不设置光标意味着光标将根本不设置。
另一件事:虽然您的问题未指定,但我假设您使用GetDlgItem()
表示您的顶级窗口是对话框。如果这是真的,那么您不能只为消息返回TRUE
或FALSE
- 您需要使用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);
答案 1 :(得分: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;
警告:菜单和按钮未创建!创建您,请检查我的答案示例。
答案 2 :(得分: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;
}