当前位置: 首页 > news >正文

利用SendMessage实现窗口拖动

                                    利用SendMessage实现窗口拖动
                                           周银辉

想想以前用跟踪鼠标位移的方式来实现窗口拖动的方式还真有些傻, 后来, .Net3.0以来的Window类内置了DragMove方法, 似乎让我们方便的不少, 但, 最近这个方法也不能满足需求了, 因为我需要DragMove过程中向外发事件来通知我"拖动开始了"和"拖动结束了", 可惜的是Window类没有提供者两个事件 (也曾企图通过其他方式来得到通知, 比如监视MouseUp等, 效果不好).
所以就自己来实现窗口拖动吧
不必同监视鼠标位移手动更新窗口位置, 其实通过向窗口发送SC_MOVE命令来移动窗口就可以了,这个命令会帮我们完成位置计算和更新工作:
         public   const   int  SC_MOVE  =   0xf012 ;
        
public   const   int  WM_SYSCOMMAND  =   0x112 ;
        
public   const   int  WM_LBUTTONUP  =   0x202 ;

        [DllImport(
" user32.dll " , CharSet  =  CharSet.Auto)]
        
public   static   extern  IntPtr SendMessage(IntPtr hWnd,  int  msg, IntPtr wParam, IntPtr lParam);

        
private   static   void  DragAndMoveInner(IntPtr hwnd)
        {
            OnDragAndMoveStarted(hwnd);

            
SendMessage(hwnd, WM_SYSCOMMAND, (IntPtr)SC_MOVE, IntPtr.Zero) ;
            
SendMessage(hwnd, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero );

            OnDragAndMoveEnded(hwnd);
        }
其中WM_SYSCOMMAND是说明向窗口发送指定的命名, 命令的具体值通过第3个参数传进去.
注意到上面在拖动结束时发送了一个WM_LBUTTONUP消息, 这是因为当鼠标左键按下(并移动)时我们会调用该函数来开始拖动,你的应用程序师可以检测到开始拖动前的这个MouseDown事件de, 但SC_MOVE会拦截MouseUp来结束拖动.你的应用程序监视不到这个MouseUp事件,所以你可能会发现鼠标左键Down和Up数目不配对, 所以在拖动结束时我们Mock了一个Up事件.
由于SendMessage 方法是不会立即返回的(同步的, SendMessageCallback  与 SendNotifyMessage 是立即放回的), 所以在SendMessage执行完毕时,也就是我们"拖动"操作完毕之时, 所以我们可以在这里调用 OnDragAndMoveEnded(hwnd)来引发我们自定义的"拖动结束"事件

SendMessage第三个参数(wParam )可以包含的具体的指令值,可以参考下面的枚举:
         public   enum  WM_SYSCOMMAND_WPARAM
        {
            SC_FIRST 
=   0xF000 ,

            
//  Sizes the window.
            SC_SIZE  =  SC_FIRST,

            
//  Moves the window.
            SC_MOVE  =  SC_FIRST  +   0x10 ,

            
//  Minimizes the window.
            SC_MINIMIZE  =  SC_FIRST  +   0x20 ,

            
//  Maximizes the window.
            SC_MAXIMIZE  =  SC_FIRST  +   0x30 ,

            
//  Moves to the next window.
            SC_NEXTWINDOW  =  SC_FIRST  +   0x40 ,

            
//  Moves to the previous window.
            SC_PREVWINDOW  =  SC_FIRST  +   0x50 ,

            
//  Closes the window.
            SC_CLOSE  =  SC_FIRST  +   0x60 ,

            
// Scrolls vertically
            SC_VSCROLL  =  SC_FIRST  +   0x70 ,

            
//  Scrolls horizontally.
            SC_HSCROLL  =  SC_FIRST  +   0x80 ,

            
//  Retrieves the window menu as a result of a mouse click.
            SC_MOUSEMENU  =  SC_FIRST  +   0x90 ,

            
//  Retrieves the window menu as a result of a keystroke.
            
//  For more information, see the Remarks section.
            SC_KEYMENU  =  SC_FIRST  +   0x100
             
            SC_ARRANGE 
=  SC_FIRST  +   0x110 ,

            
//  Restores the window to its normal position and size.
            SC_RESTORE  =  SC_FIRST  +   0x120 ,

            
//  Activates the Start menu.
            SC_TASKLIST  =  SC_FIRST  +   0x130 ,

            
//  Executes the screen saver application specified 
            
//  in the [boot] section of the System.ini file.
            SC_SCREENSAVE  =  SC_FIRST  +   0x140 ,

            
//  Activates the window associated with the application-specified hot key. 
            
//  The lParam parameter identifies the window to activate.
            SC_HOTKEY  =  SC_FIRST  +   0x150 ,

            
//  Selects the default item; 
            
//  the user double-clicked the window menu.
            SC_DEFAULT  =  SC_FIRST  +   0x160 ,

            
//  Sets the state of the display.
            
//  This command supports devices that have power-saving features,
            
//  such as a battery-powered personal computer.
            
//  The lParam parameter can have the following values:
            
//  -1 - the display is powering on
            
//   1 - the display is going to low power
            
//   2 - the display is being shut off
            SC_MONITORPOWER  =  SC_FIRST  +   0x170
           
            
//  Changes the cursor to a question mark with a pointer. 
            
//  If the user then clicks a control in the dialog box, 
            
//  the control receives a WM_HELP message.
            SC_CONTEXTHELP  =  SC_FIRST  +   0x180

            SC_SEPARATOR 
=   0xF00F
        }

完整的代码,参考下面, 其支持WinForm和WPF 窗口:
     public   static   class  DragMoveExtention
    {
        
public   static   event  EventHandler DragAndMoveStarted;
        
public   static   event  EventHandler DragAndMoveEnded;

        
public   const   int  SC_MOVE  =   0xf012 ;
        
public   const   int  WM_SYSCOMMAND  =   0x112 ;
        
public   const   int  WM_LBUTTONUP  =   0x202 ;

        [DllImport(
" user32.dll " , CharSet  =  CharSet.Auto)]
        
public   static   extern  IntPtr SendMessage(IntPtr hWnd,  int  msg, IntPtr wParam, IntPtr lParam);

        
private   static   void  DragAndMoveInner(IntPtr hwnd)
        {
            OnDragAndMoveStarted(hwnd);

            SendMessage(hwnd, WM_SYSCOMMAND, (IntPtr)SC_MOVE, IntPtr.Zero);
            SendMessage(hwnd, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);

            OnDragAndMoveEnded(hwnd);
        }


        
private   static   void  OnDragAndMoveStarted(Object sender)
        {
            
if (DragAndMoveStarted  !=   null )
            {
                DragAndMoveStarted(sender, EventArgs.Empty);
            }
        }

        
private   static   void  OnDragAndMoveEnded(Object sender)
        {
            
if (DragAndMoveEnded  !=   null )
            {
                DragAndMoveEnded(sender, EventArgs.Empty);
            }
        }

        
//  use it like this: 
        
//  wpfWindow.MouseMove += delegate{ wpfWindow.DragAndMove(); };
         public   static   void  DragAndMove( this  Window window)
        {
            
if  (Mouse.LeftButton  ==  MouseButtonState.Pressed)
            {
                IntPtr hwnd 
=   new  WindowInteropHelper(window).Handle;
                DragAndMoveInner(hwnd);
            }
        }

        
//  use it like this: 
        
//  winForm.MouseMove += delegate { winForm.DragAndMove(); };
         public   static   void  DragAndMove( this  Form form)
        {
            
if  (Control.MouseButtons  ==  MouseButtons.Left)
            {
                DragAndMoveInner(form.Handle);
            }
        }
        
    }


相关文章:

  • 使用T-SQL的Rand函数生成随机数的艰苦历程
  • x.25
  • songyang.me牌hotmail邮箱注册页面
  • 网站推广提交
  • apache安装完成后,如何添加模块
  • asp用mschart画曲线图(实例)
  • 不用工具照样恢复系统
  • IP数据包的分析实例
  • 好久没有发文章了
  • python 实现的范式huffman压缩,解压缩
  • C#开发基于ESMTP协议的邮件发送系统经验总结
  • 实习笔记2
  • 十五、不同VLAN之间相互通信的两种方式(单臂路由、三层交换)
  • PB与COM之关于创建COM,MTS, and COM+组件(1)
  • 让Windows Mobile模拟器通过你的PC上网
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • Android交互
  • canvas绘制圆角头像
  • create-react-app项目添加less配置
  • CSS盒模型深入
  • javascript面向对象之创建对象
  • java概述
  • k8s如何管理Pod
  • log4j2输出到kafka
  • Material Design
  • mysql中InnoDB引擎中页的概念
  • python学习笔记-类对象的信息
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 大型网站性能监测、分析与优化常见问题QA
  • 关于字符编码你应该知道的事情
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 思维导图—你不知道的JavaScript中卷
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 运行时添加log4j2的appender
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​​​​​​​​​​​​​​Γ函数
  • ​ssh免密码登录设置及问题总结
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #NOIP 2014#Day.2 T3 解方程
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • $GOPATH/go.mod exists but should not goland
  • (1)Nginx简介和安装教程
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (补)B+树一些思想
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (九)One-Wire总线-DS18B20
  • (论文阅读30/100)Convolutional Pose Machines
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (十三)Maven插件解析运行机制
  • .htaccess配置重写url引擎