收藏 分享(赏)

窗口移动无边框各种方式.doc

上传人:ysd1539 文档编号:6903321 上传时间:2019-04-26 格式:DOC 页数:9 大小:48.50KB
下载 相关 举报
窗口移动无边框各种方式.doc_第1页
第1页 / 共9页
窗口移动无边框各种方式.doc_第2页
第2页 / 共9页
窗口移动无边框各种方式.doc_第3页
第3页 / 共9页
窗口移动无边框各种方式.doc_第4页
第4页 / 共9页
窗口移动无边框各种方式.doc_第5页
第5页 / 共9页
点击查看更多>>
资源描述

1、第一步:/先定义一个坐标点对象 mypointprivate Point mypoint;第二步:在窗体的点击事件中写 private void Form1_MouseDown(object sender, MouseEventArgs e)/鼠标在窗体内按下时,自动记录鼠标的 x y 值,并将它们改为负数mypoint = new Point(-e.X,-e.Y);第三步:在窗体的鼠标移动事件中写private void Form1_MouseMove(object sender, MouseEventArgs e)/*鼠标在窗体内移动时,首先判断 e.Button 按下的是哪个鼠标按钮再判

2、断* 是否等于左键按下,MouseButtons.Left 这句的意思是鼠标左键按下状态*/if (e.Button = MouseButtons.Left)/*新建一个坐标点对象,它的坐标等于(Control.MousePosition;)* 桌面上坐标的位置*/Point myposition = Control.MousePosition;/*myposition.offset 中的 Offset 是坐标平移的意思,现在将在窗体* 内点击左键时产生的负数加进来,也就等于 现在鼠标在桌面上的坐标* 减去鼠标在窗体内的坐标位置,就等于现在窗体的位置*/myposition.Offset(my

3、point.X, mypoint.Y);/*this.DesktopLocation 这句的意思是获取或设置窗体在桌面上的位置* 它的位置来自 myposition*/this.DesktopLocation = myposition;其实这是个让人说过无数次的内容,但是最近在写一个测试小程序的时候发现了一个问题,今天没什么事做,就做个小的总结。通过拖动窗体的客户区来移动一个窗体并不是很新鲜的内容,很多的程序都用到了这一点,尤其是一些可以换肤的程序。这篇文章并不打算详细论述如何在 C#下实现这一功能,因为它的代码实在是简单得不能再简单。这里简单说一下实现的原理:首先说一个概念窗体的客户区,窗体

4、的客户区指的是一个窗体除了标题栏和边框以外的部分。当我们的鼠标在窗体中移动的时候,会触发 WM_NCHITTEST 系统消息,MSDN 中对这个消息的说明为:The WM_NCHITTEST message is sent to a window when the cursor moves, or when a mouse button is pressed or released. If the mouse is not captured, the message is sent to the window beneath the cursor. Otherwise, the message

5、 is posted to the window that has captured the mouse. (当光标移动或一个鼠标键被按下或释放时,WM_NCHITTEST 消息会被发送到一个窗口中,如果光标没有被捕获,这个消息被送到光标下的窗口。否则这 个消息被送到捕获了光标的窗口。)这个消息被默认的(请注意是“默认的”)窗口过程(窗口过程这个概念后面再说)处理后,会根据触发这个消息时鼠标的位置返回一个值,例如当鼠标在窗 口的标题栏上时,返回 HTCAPTION;当鼠标在一个窗口的客户区中时,返回HTCLIENT;如果鼠标指向某个窗口的字窗口的“关闭”按钮或系统菜单 (就是点击窗口图标后出现

6、的那个菜单),就返回 HTSYSMENU。所以我们要做的就是骗!我们要欺骗 Windows,当我们的鼠标在窗体的客户区中移动时,默认的窗口过程处理后会返回 HTCLIENT,Windows 系统根据这个值进行相应的操作,把适当的消息插入到应用程序的消息队列(这个概念同样在后面讨论)中。这时如果我们做一 些改变,人为地修改窗口过程的返回值,把HTCLIENT 修改为 HTCAPTION 并返回给系统,系统就会认为鼠标这时在窗体的标题栏中,而拖动标题栏可 以移动一个窗体,所以当我们在一个被这样修改后的应用程序的客户区按下鼠标并拖动时,Windows 会认我们在拖动一个窗体的标题栏,于是它把一个移动

7、窗 体的消息插入到程序的消息队列中,再经过窗口过程的处理,就实现了我们需要的功能拖动窗体的客户区移动窗体。于是就有了下面的代码:protected override WndProc(ref message m)switch (m.Msg)case WM_NCHITTEST: /如果鼠标移动或单击 base.WndProc(ref m);/调用基类的窗口过程WndProc 方法处理这个消息if (m.Result = (IntPtr)HTCLIENT)/如果返回的是 HTCLIENTm.Result = (IntPtr)HTCAPTION;/把它改为 HTCAPTIONreturn;/直接返回退

8、出方法break;base.WndProc(ref m);/如果不是鼠标移动或单击消息就调用基类的窗口过程进行处理这也是 MSDN 上的一个例子,重写窗体的 WndProc 方法,判断消息然后返回。我本来也是想这样就完事了,可是后来测试的时候却发现有个大问题:双击窗体的时候,窗体会最大化。那天脑子有点迷糊,也没多想就到网上问了一下,一个高手提醒我说可能是那个重写的窗口过程的问题,于是一下子明白了。我们向 Windows 传递了假 信息,Windows 我们在窗体的客户区双击鼠标在 Windows 看来是双击了窗体的标题栏,理所当然的 Windows 会告诉窗口过程窗体需要最大化,窗 口过程又理

9、所当然的照做了,于是就出现了这样的现象。刚开始的时候还不是很确定这样想对不对,不过后来出现的另一个现象证实了这个推断是正确的:因为窗体没有标题栏,每次退出程序都要按 Alt+F4 实在是比较不爽,我就给窗体添加了一个 ContextMenuStrip,只有一个菜单项用来 关闭窗体退出程序。我发现当我修改了窗体的默认窗口过程后,这个右键菜单就显示不出来了。出现这个现象的原因只能有一个Windows 真的把窗体的客 户区当成标题栏处理了,而默认 ContextMenuStrip 只能用在窗体的客户区或控件上。后来我又跟那位高手讨论了一下,他给我提供了另一种方法,这个方法需要两个 API 函数:LR

10、ESULT SendMessage(HWND hWnd,/ handle of destination windowUINT Msg,/ message to sendWPARAM wParam,/ first message parameterLPARAM lParam / second message parameter);BOOL ReleaseCapture(VOID)SendMessage 函数想必不用多说了,无数个木马制作教学贴里都有详细的描述,这个函数用来向指定的窗体发送 Windows 消息,功能的确十分强大_ReleaseCapture 函数相对陌生一些,这个函数用来释放被当

11、前线程中某个窗口捕获的光标。他给出的代码是这样的:private void Form1_MouseDown(object sender, MouseEventArgs e)ReleaseCapture();SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);也就是说,在窗体的 MouseDown(鼠标按下)事件中,首先释放光标,然后向窗口过程发送 WM_SYSCOMMAND 消息。说实话这个方式的 SendMessage 函数调用我也是第一次遇到,刚开始怎么也不明白为什么要把它们(SC_MOVE 和 HTCAPTION

12、)用一个加号连接起来。这个 WM_SYSCOMMAND 消息包含了一些信息表示用户要进行的操作,消息的wParam 字段表示用户要最大化、最小化或移动窗体。我去掉 HTCAPTION 试了一下,每当我在窗体上按下鼠标左键时,鼠标就会移动到窗体的最顶端原来标题栏的位置,与我们在系统菜单中选择“移 动”菜单项的效果一样。于是查阅 MSDN,在 WM_SYSCOMMAND 的帮助中查到这样一句话:In WM_SYSCOMMAND messages, the four low-order bits of the uCmdType parameter are used internally by Win

13、dows. To obtain the correct result when testing the value of uCmdType, an application must combine the value 0xFFF0 with the uCmdType value by using the bitwise AND operator. (在 WM_SYSCOMMAND 消息中,uCmdType 的低四位是 Windows 内部使用的。如果测试时要获得 uCmdType 的正确结果,应用程序必 须把 0xFFF0这个值与 uCmdType 的值通过位相加的方式合并在一起。)于是豁然开

14、朗:这个所谓的“Windows 内部使用的值”在移动这个窗体的 时候其实就是表示“窗体的标题栏”,所以我们使用 SC_MOVE + HTCAPTION 表示窗体将被移动且目前鼠标正在窗体的标题栏中。至于这里为什么要先释放光标再发送消息,根据前面的 WM_NCHITTEST 消息的说明,如果光标被当前窗口捕获,那么当鼠标移动时,就会发送 WM_NCHITTEST到我们的窗体,而由于我们这时没有重写窗口过程,这个消息会被按照默认的方式处理,Windows 会发现鼠标在窗体的客户区移 动,SendMessage 发过去的HTCAPTION 被 WM_NCHITTEST 消息中的 HTCLIENT 覆

15、盖,这样窗体就不会有任何动作。还有两个名词解释:窗口过程:窗口过程是一个应用程序定义的、用来处理 Windows 消息的回调(CallBack)函数,当有消息发生时,窗口过程负责处理消息。消息队列:Windows 会把有关一个应用程序的全部消息放到一块内存区域中,这个区域使用一种先进先出(FIFO)的方式工作,就是说最先进入这 个区域的消息会第一个离开这个区域,故称消息队列。应用程序从消息队列中取出一条消息,进行相应的处理,返回一定的信息,然后再取出下一条处理。例 1:using System; using System.ComponentModel; using System.Drawing

16、; using System.Windows.Forms; namespace LiBo.WinControls.Forms / / DragForm 类是可 / public class DragForm : System.Windows.Forms.Form private bool dragEnable; private bool dragging; private int xOld, yOld; public DragForm() : base() dragEnable = false; / / 获取或设置一个值,该值指示窗体是否可以通过鼠标左键拖动。 / DefaultValue(f

17、alse) Category(“Behavior“) Description(“指示窗体是否可以通过鼠标左键拖动“) public bool DragEnable get return dragEnable; set dragEnable = value; protected override void OnMouseDown(MouseEventArgs e) if(dragEnable yOld = MousePosition.Y; / 标识鼠标正在拖动窗体 dragging = true; protected override void OnMouseMove(MouseEventArg

18、s e) if(dragEnable int dy = MousePosition.Y - yOld; / 根据上面的结果计算出窗体偏移后的位置 Point point = this.Location; point.Offset(dx, dy); / 设置上面的偏移位置为窗体的位置 this.Location = point; / 保存当前鼠标的位置,用于下一个循环的计算 xOld = MousePosition.X; yOld = MousePosition.Y; protected override void OnMouseUp(MouseEventArgs e) if(dragEnabl

19、e 例 2:using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Runtime.InteropServices; namespace Example013_无标题窗体的拖动 / / Form1 的摘要说明。 / public class Form1 : System.Windows.Forms.Form / / 必需的设计器变量。 / privat

20、e System.ComponentModel.Container components = null; public Form1() / / Windows 窗体设计器支持所必需的 / InitializeComponent(); / / TODO: 在 InitializeComponent 调用后添加任何构造函数代码 / / / 清理所有正在使用的资源。 / protected override void Dispose( bool disposing ) if( disposing ) if (components != null) components.Dispose(); base

21、.Dispose( disposing ); #region Windows Form Designer generated code / / 设计器支持所需的方法 - 不要使用代码编辑器修改 / 此方法的内容。 / private void InitializeComponent() System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1); / / Form1 / this.AutoScaleBaseSize = new System.Drawing.Siz

22、e(6, 14); this.BackgroundImage = (System.Drawing.Bitmap)(resources.GetObject(“$this.BackgroundImage“); this.ClientSize = new System.Drawing.Size(292, 273); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.Name = “Form1“; this.Text = “Form1“; this.MouseDown += new System.Windows

23、.Forms.MouseEventHandler(this.Form1_MouseDown); #endregion / / 应用程序的主入口点。 / STAThread static void Main() Application.Run(new Form1(); DllImport(“user32.dll“) public static extern bool ReleaseCapture(); DllImport(“user32.dll“) public static extern bool SendMessage(IntPtr hwnd,int wMsg,int wParam,int lParam); public const int WM_SYSCOMMAND=0x0112; public const int SC_MOVE=0xF010; public const int HTCAPTION=0x0002; private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) ReleaseCapture(); SendMessage(this.Handle,WM_SYSCOMMAND,SC_MOVE+HTCAPTION, 0);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报