原文:利用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);
            }
        }
        
    }

利用SendMessage实现窗口拖动的更多相关文章

  1. 利用 canvas 破解 某拖动验证码

    利用 canvas 破解 某拖动验证码 http://my.oschina.net/u/237940/blog/337194

  2. Duilib改进窗口拖动,使整个窗口都能拖动两种方法(转载)

    转载:http://www.cnblogs.com/XiHua/articles/3490490.html 转载:http://blog.csdn.net/lostspeed/article/deta ...

  3. duilib进阶教程 -- 改进窗口拖动 (12)

    现在大家应该都知道caption="0,0,0,32",是指示标题栏区了吧,如果想要整个窗口都能拖动呢? 那直接把高度改成和窗口一样不就得了~O(∩_∩)O~ 嗯,这样是可以,比如 ...

  4. Winform 窗口拖动

    把窗口边框去掉后,窗口拖动问题: private Point mouseOffset; //记录鼠标指针的坐标 private bool isMouseDown = false; //记录鼠标按键是否 ...

  5. 窗体的Alpha通道透明色支持(一旦 Form 被定义为利用 LayeredWindow ,窗口的绘图不再响应沿用多年的 WM_Paint 消息)

    参考: http://www.delphibbs.com/delphibbs/dispq.asp?lid=2190768 Windows 2000后,为了支持类似MAC界面的Alpha通道混合效果,提 ...

  6. 29.QT-自定义窗口拖动、自定义QToolButton/QPushButton开关按钮、界面阴影,声音等总结

    自定义窗口及拖动 1.自定义无边框窗口时,需要将窗口标志设为: Qt::FramelessWindowHint |Qt::WindowSystemMenuHint | Qt::WindowMinMax ...

  7. 【Win32 API】利用SendMessage实现winform与wpf之间的消息传递

    原文:[Win32 API]利用SendMessage实现winform与wpf之间的消息传递 引言    有一次心血来潮,突然想研究一下进程间的通信,能够实现消息传递的方法有几种,其中win32ap ...

  8. C# zedgraph利用另一窗口取得的串口数据绘图

    C# zedgraph利用另一窗口获得的串口数据绘图第一次用zedgraph,非常不熟悉,网上很多内容看的云里雾里... 这个程序主界面接收串口数据,而另外一个窗口进行实时曲线绘图,要怎么样实现for ...

  9. 基于jQuery页面窗口拖动预览效果

    今天给大家分享一款基于Query页面窗口拖动预览效果.这是一款基于jQuery+HTML5实现的模拟页面窗口显示拖动窗口预览特效.这款实例适用浏览器:IE8.360.FireFox.Chrome.Sa ...

随机推荐

  1. vue使用jsonp

    axios不支持jsonp,所以需使用其他插件:vue-jsonp npm i vue-jsonp -S 然后在 src/main.js : import Vue from 'vue' import ...

  2. amazeui学习笔记--css(常用组件1)--小徽章Badge

    amazeui学习笔记--css(常用组件1)--小徽章Badge 一.总结 1.am-badge:添加am-badge来声明小徽章对象 <span class="am-badge a ...

  3. 基于bootstrap的主流框架有哪些

    基于bootstrap的主流框架有哪些 一.总结 一句话总结:其实可以直接百度bootstrap后台模板,出来一大堆,想用哪个用哪个. 二.[前端框架系列]浅谈当前基于bootstrap框架的几种主流 ...

  4. POJ 2983 Is the Information Reliable? 依旧差分约束

    http://poj.org/problem?id=2983 题目大意: 星际大战开始了.你购买了情报,需要判断它的准确性.已知地方的根据地在由南向北排成一条直线.P A B X,表示A在B北面距离X ...

  5. 1、Java快速入门

    第一课 Java基础1. ubuntu上环境搭建 (建议使用我们提供的VMWare映象文件) 如果要自己安装ubuntu, 请参考<韦东山Android系统视频使用手册.pdf> ubun ...

  6. js 第四章 cookie的操作

    js 第四章 cookie的操作 一.学习要点 掌握cookie的简单应用 二. js 第四章 cookie的操作 了解cookie 什么是cookie? cookie 是存储于访问者的计算机中的变量 ...

  7. idea+springboot+freemarker热部署(转)

    今天在学习springboot集成freemarker模板引擎修改代码时,发现每次修改一次freemarker文件时,都必须重启下应用,浏览器刷新才能显示修改后的内容,这样效率太低,每次启动一次应用都 ...

  8. [Angular] Creating an Observable Store with Rx

    The API for the store is really simple: /* set(name: string, state: any); select<T>(name: stri ...

  9. 终端复用工具tmux的使用

    tmux的作用在于终端复用. 1. 在server上启动一个bash.并在里面执行tmux 2. 通过ssh远程登录server,执行tmux attach,就会切换到server上的那个bash中, ...

  10. Android 自定义RadioButton样式

     上面这种3选1的效果如何做呢?用代码写? 其实有更简单的办法,忘了RadioButton有什么特性了吗? 我就用RadioButton实现了如上效果,其实很简单的. 首先定义一张background ...