Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >正確使用 SetCapture ReleaseCapture [譯]「建议收藏」

正確使用 SetCapture ReleaseCapture [譯]「建议收藏」

作者头像
全栈程序员站长
发布于 2022-09-01 07:41:16
发布于 2022-09-01 07:41:16
53100
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

本文描述瞭如何正確處理自定義窗口和控件中的鼠標捕獲操作。

原文鏈接: http://www.codeproject.com/Tips/127813/Using-SetCapture…correctly.aspx 原作者: pasztorpisti 轉載請註明出處:http://www.imoldman.com/2010/11/30/ using-setcaptu…ture-correctly

鼠標捕獲是Windows的一項特性。即使光標不在某個窗口或者控件(帶HWND句柄)內,它也可以將所有的鼠標消息和該指定的窗口相關聯。現假定你已經熟悉了這項特性及其相關API(包括SetCapture()函數, ReleaseCapture()函數及WM_CAPTURECHANGED消息),在這裡,我想告訴你一項開發人員經常犯的錯誤。 有些窗口和控件為了完成某項操作,有時需要捕獲鼠標。spy++程序上的窗口選擇器(即那個十字)是一個很好的例子,它允許你拖動一個十字光標到你桌面的某個窗口上,這樣你就可以選中它。

該文給出的示例程序創建了一個窗口,你可以拖動它的客戶區來移動它。只要在DefWindowProc()響應WM_NCHITTEST消息時返回HTCLIENT,就可以達到這種效果,但是這樣主循環就不工作了,就好像是你在拖拽著它的標題欄一樣。某些程序(如媒體播放器,遊戲)通常自繪整個窗口,並且以該示例代碼中的方式提供拖拽窗口的功能。這樣做,程序的主循環可以一直在運作。這樣的窗口移動功能需要捕獲鼠標,那時因為如果你按下了鼠標左鍵,然後把鼠標從客戶區猛地拉到外面去,窗口仍然要能夠接收到WM_MOUSEMOVE消息。

那麼這種“抓著客戶區移動窗口”操作的執行步驟是怎樣的呢?

  1. WM_LBUTTONDOWN,開始拖拽操作並且捕獲鼠標。
  2. WM_MOUSEMOVE,隨著光標一起移動窗口。
  3. WM_LBUTTONUP,僅僅釋放捕獲的鼠標。
  4. WM_CAPTURECHANGED,結束拖拽操作。

這篇文章的重點就是你要在WM_LBUTTONUPWM_CAPTURECHANGED消息響應中處理什麼。這裡重要的一點就是WM_LBUTTONUP僅僅釋放鼠標,只有WM_CAPTURECHANGED才會中止拖拽操作!!!之所以這樣,原因在於拖拽操作可以被其他操作終止掉,比如說你按了ALT+TAB組合鍵。這種情況下,你的程序根本接收不到WM_LBUTTONUP消息,但是仍然失去了鼠標捕獲,此時,窗口會接收到WM_CAPTURECHANGED消息,於是整個拖拽操作結束。

如果你把“拖拽操作結束”的相關代碼放在了WM_LBUTTONUP消息響應裡,一旦用戶按了ALT+TAB,你的程序會失去鼠標捕獲,此時另外一個窗口會成為前景窗口,你整個窗口的邏輯狀態就會亂掉,因為它還認為它是在拖動過程中。

所以結論就是,總是將操作結束的處理代碼放在WM_CAPTURECHANGED消息響應裡,並且在其他你想結束操作的地方調用ReleaseCapture(),這可以發生在任何地方,比如在WM_LBUTTONUP消息響應函數中,在WM_MOUSEMOVE消息響應函數中,等等。

編譯運行我給出的代碼,在拖拽主窗口客戶區的過程中,使用ALT+TAB按鍵將一個大些的窗口提到前面,這樣示例程序的主窗口就會全部被蓋住。之後釋放鼠標按鍵並且切回到示例程序,將鼠標在示例窗口上移動,一切正常。然後將WM_LBUTTONUPWM_CAPTURECHANGED消息處理代碼註釋掉,並且去掉有問題的代碼的註釋,再按照步驟試試ALT+TAB!這次,在ALT+TAB切換窗口、釋放鼠標按鍵並且使用ALT+TAB再切回我們的窗口後,將鼠標在示例窗口上移動,並且嘗試很快的速度移動光標,此時你會發現窗口的行為很瘋狂,除非你在窗口上單擊一下給它發個WM_LBUTTONUP消息,它才能回歸正常。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <windows.h>

HINSTANCE g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
HWND g_hMainWnd = NULL;
bool g_MovingMainWnd = false;
POINT g_OrigCursorPos;
POINT g_OrigWndPos;
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_LBUTTONDOWN:
		// here you can add extra check and decide whether to start
		// the window move or not
		if (GetCursorPos(&g_OrigCursorPos))
		{
			RECT rt;
			GetWindowRect(hWnd, &rt);
			g_OrigWndPos.x = rt.left;
			g_OrigWndPos.y = rt.top;
			g_MovingMainWnd = true;
			SetCapture(hWnd);
		}
		return 0;
	// THE RIGHT WAY OF DOING IT:
	//*
	case WM_LBUTTONUP:
		ReleaseCapture();
		return 0;
	case WM_CAPTURECHANGED:
		g_MovingMainWnd = (HWND)lParam == hWnd;
		return 0;
	/**/
	// THE WRONG WAY OF DOING IT:
	/*
	case WM_LBUTTONUP:
		g_MovingMainWnd = false;
		ReleaseCapture();
		return 0;
	// buggy programs usually do not handle WM_CAPTURECHANGED at all
	case WM_CAPTURECHANGED:
		break;
	/**/
	case WM_MOUSEMOVE:
		if (g_MovingMainWnd)
		{
			POINT pt;
			if (GetCursorPos(&pt))
			{
				int wnd_x = g_OrigWndPos.x +
				  (pt.x - g_OrigCursorPos.x);
				int wnd_y = g_OrigWndPos.y +
				  (pt.y - g_OrigCursorPos.y);
				SetWindowPos(hWnd, NULL, wnd_x,
				  wnd_y, 0, 0, SWP_NOACTIVATE|
				  SWP_NOOWNERZORDER|SWP_NOZORDER|
				  SWP_NOSIZE);
			}
		}
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
bool CreateMainWnd()
{
	static const char CLASS_NAME[] = "MainWndClass";
	WNDCLASS wc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hInstance = g_hInstance;
	wc.lpfnWndProc = &MainWndProc;
	wc.lpszClassName = CLASS_NAME;
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	if (!RegisterClass(&wc))
		return false;
	g_hMainWnd = CreateWindowEx(
		0,
		CLASS_NAME,
		"Main Window",
		WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
		CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
		NULL,
		NULL,
		g_hInstance,
		NULL
		);
	return true;
}
int main()
{
	if (!CreateMainWnd())
		return -1;
	ShowWindow(g_hMainWnd, SW_SHOW);
	UpdateWindow(g_hMainWnd);
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return (int)msg.wParam;

ps:這篇文章是昨天看到的,當時感覺寫的挺好的(其實主要是因為短

),所以想拿來翻譯下,一來鍛煉下自己的英語水平,二來充實下自己的blog,跑去CPOL看了下,沒有提到翻譯相關的內容,然後給作者留了言,問他能不能翻譯下,人家說可以,不過你還是問下CodeProject的工作人員吧,然後屁顛屁顛的跑去問,結果被告知他們沒權利允許我翻譯拿來貼自己的blog上,因為文章的版權是原作者的,我想這好辦了,原作者都同意了,跟人家說下然後開始翻譯吧,結果在作者的About頁面愣是沒找到他的Email。於是在沒有得到最終許可的情況下,我翻譯了這篇文章,想來應該不會違反License的,如果需要轉載記得保留開始三個鏈接!

就這樣吧,謝謝原作者pasztorpisti和CodeProjcet平台

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/141917.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JS实现网页简体繁体字转换功能
在网页中经常会遇到将简体字转换成繁体字,方便于其他同胞查看。网页中实现简体中文转换成繁体字方法,今天分享给大家,此方法借鉴于他人博客;
申霖
2019/12/27
6.8K0
JS实现网页简体繁体字转换功能
给网站增加简繁体自动转换,抓住更多流量
国内做站,一般都是做百度,神马,搜狗等几大搜索引擎的流量。不过假如你的网站内容质量还行的话,一般也会有一些谷歌的流量,而用谷歌搜索中文网站的,一般是台湾香港地区的这些同胞,不过这些地区的同胞都是使用繁体中文语言。因此,本着用户体验至上的原则,如果能够给网站增加自动简体转繁体的功能,让这些地区的用户访问网站看到的是中文繁体字,既提升了用户体验,也让搜索引擎为你的站点加分。
皇上得了花柳病
2020/05/06
7600
淺談晶片實體設計-競爭力(Competitiveness)
题记:本文是 funBroad 系列文章的第一篇,中心思想还是,在头部企业可以吃更多『设计余量』红利时,老二老三老四老五跟老小们怎么跟随,怎么提高自身竞争力——最后一段标红部分可细读。
老秃胖驴
2020/05/22
7730
Hexo + Butterfly 自定义右键菜单
在BlogRoot/node_modules/hexo-theme-butterfly/layout/includes文件夹下新建一个right-menu的文件夹,在此文件夹下新建一个index.pug文件。 具体位置如下图:
唐志远
2022/10/27
5940
Hexo + Butterfly 自定义右键菜单
日文字符集
特殊说明: 解决问题的光鲜,藏着磕Bug的痛苦。 万物皆入轮回,谁也躲不掉! 以上文章,均是我实际操作,写出来的笔记资料,不会出现全文盗用别人文章!烦请各位,请勿直接盗用!
收心
2022/03/01
1.8K0
一些收集整理的JS
整理出一些收集的JS代码,在这里向所有原作者致敬。 1.收集键盘指令 按A就会跳转到练习的网页,请按A <SCRIPT language="JavaScript"> <!-- var hotkey=9
练小习
2017/12/29
6180
SQL简体繁体转换函数代码
在SQL查询窗口中直接创建表和函数 --生成码表 if exists (select * from dbo.sysobjects where id = object_id(N'[codetable]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [codetable] GO declare @j varchar(8000),@f varchar(8000) select @j=' 啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎
用户1149182
2020/06/19
3.9K0
PHP+HTML-常用功能实现
卡片式阴影矩形框 HTML <link rel="stylesheet" type="text/css" media="all" href="./css/zodiac_style.css?s1.1.1
偏有宸机
2020/11/04
1.9K0
PHP+HTML-常用功能实现
excel 汉字转拼音「建议收藏」
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151859.html原文链接:https://javaforall.cn
全栈程序员站长
2022/06/24
7.8K0
【笔记】EFCore & SQLite 拼音汉字互换
注意OnConfiguring方法里的UseSqlite输入的参数需要指定路径,否则容易遭遇惊喜“no such table”。因为sqlite在没有找到db文件的情况下会自动新建同名db文件,新文件为空,当然没有表了,然后它就会告诉你“no such table”。如果不嫌麻烦的话,可以在查询/修改/更新数据库代码前插入context.Database.EnsureCreated(),这样没找到数据库文件的话会抛出异常。
全栈程序员站长
2022/06/24
5.1K0
中文转拼音【真正的完整版】 拼音 驼峰命名专用
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151855.html原文链接:https://javaforall.cn
全栈程序员站长
2022/06/24
5.4K0
汉字转拼音 文字集
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151850.html原文链接:https://javaforall.cn
全栈程序员站长
2022/06/24
13.4K0
支持二级汉字的 php 汉字助记码生成
gbk2312 编码范围共94区, 0-55区为一级汉字, 是按照拼音顺序排列的, 可以按照编码区间确定汉字的拼音, 但是 56 区以后是按笔画顺序排列的, 所以只能用对照表来确定拼音 鉴于目前我找不到现成的代码, 固整理了一份, 测试可用.
全栈程序员站长
2022/06/24
4.2K0
如何制作离线tts?「建议收藏」
tts->把文字转化为语音。 先把中文转化为拼音。 这个主要依靠1个字典,能把汉字和读音对应。 下面提供字典:
全栈程序员站长
2022/06/24
3.5K0
相关推荐
JS实现网页简体繁体字转换功能
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验