如何实现一个无边框Form的移动和改变大小(二)
接着上文:这里写链接内容 
我们来说说一个比较复杂的实现, 
效果如图: 
 
注意为了能够凸显没有NC(NotClient)区域,我们额外用了3个panel分别放在窗体的左右和下部。用来模拟客户自己的控件。 
下面我们说下这种真正的无边框Form的实现方法 
下面先无责任的贴下代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using norlib;
using norlib.Controls;
using norlib.Error;
using norlib.Native;
using norlib.SystemExtension;
namespace norlib.Controls
{
    public partial class BorderlessForm
        :Form
    {
        public BorderlessForm()
        {
            InitializeComponent();
            _caption = 0;
            _mp = new MousePreview(this);
            #region 初始化_dtCursor
            _dtCursor.Add(HT.HTBOTTOM, Cursors.SizeNS);
            _dtCursor.Add(HT.HTTOP, Cursors.SizeNS);
            _dtCursor.Add(HT.HTLEFT, Cursors.SizeWE);
            _dtCursor.Add(HT.HTRIGHT, Cursors.SizeWE);
            _dtCursor.Add(HT.HTTOPLEFT, Cursors.SizeNWSE);
            _dtCursor.Add(HT.HTTOPRIGHT, Cursors.SizeNESW);
            _dtCursor.Add(HT.HTBOTTOMLEFT, Cursors.SizeNESW);
            _dtCursor.Add(HT.HTBOTTOMRIGHT, Cursors.SizeNWSE);
            #endregion
            _mp.AddMouseMessage(WM.WM_LBUTTONDOWN, mp_LButtonDownPreview);
            _mp.AddMouseMessage(WM.WM_MOUSEMOVE, mp_MouseMovePreview);
            //不能选这个
            //this.Capture = true;
        }
        public int Border
        {
            get { return _border; }
            set
            {
                if (value <= 0)
                    return;
                _border = value;
            }
        }
        public int Caption
        {
            get { return _caption; }
            set { _caption = value; }
        }
        eMPResult mp_LButtonDownPreview(WM arg_wm, ref MOUSEHOOKSTRUCT argr_stInfo)
        {
            var p = argr_stInfo.pt;
            var cp = this.PointToClient(new Point(p.x, p.y));
            var hc = _GetHT(cp, _border, _caption);
            if (hc != HT.HTERROR && hc != HT.HTCLIENT)
            {
                Task.Factory.StartNew(() =>
                {
                    this.BeginInvoke(new Action(()=>
                    {
                        NativeMethods.ReleaseCapture();
                        NativeMethods.SendMessage(this.Handle, WM.WM_NCLBUTTONDOWN, (UIntPtr)hc, (IntPtr)0);
                    }));
                });
                return eMPResult.CutOffMessage;
            }
            else
            {
                return eMPResult.ContinueHook;
            }
        }
        eMPResult mp_MouseMovePreview(WM arg_wm, ref MOUSEHOOKSTRUCT argr_stInfo)
        {
            var p = argr_stInfo.pt;
            var cp = this.PointToClient(new Point(p.x, p.y));
            var hc = _GetHT(cp, _border, _caption);
            if (hc != HT.HTCLIENT && hc != HT.HTERROR)
            {
                var c = _GetCursor(hc);
                if (Cursor != c)
                    Cursor = c;
                return eMPResult.ContinueHook;
            }
            else
            {
                Cursor = Cursors.Default;
                return eMPResult.ContinueHook;
            }
        }
        HT _GetHT(Point arg_p, int arg_border, int arg_caption)
        {
            var pos = arg_p;
            var border = arg_border;
            var caption = arg_caption;
            if (pos.X < 0 || pos.Y < 0)
            {
                //
                //非法位置
                //
                return HT.HTERROR;
            }
            else if (pos.X <= border)
            {
                //
                //左侧
                //
                if (pos.Y <= border)
                {
                    //左上侧
                    return HT.HTTOPLEFT;
                }
                else if (pos.Y >= this.Height - border)
                {
                    //左下侧
                    return HT.HTBOTTOMLEFT;
                }
                else
                {
                    //左侧
                    return HT.HTLEFT;
                }
            }
            else if (pos.X >= this.Width - border)
            {
                //
                //右侧
                //
                if (pos.Y <= border)
                {
                    //右上侧
                    return HT.HTTOPRIGHT;
                }
                else if (pos.Y >= this.Height - border)
                {
                    //右下侧
                    return HT.HTBOTTOMRIGHT;
                }
                else
                {
                    //右侧
                    return HT.HTRIGHT;
                }
            }
            else
            {
                //
                //中部
                //
                if (pos.Y <= border)
                {
                    //上中侧
                    return HT.HTTOP;
                }
                else if (pos.Y >= this.Height - border)
                {
                    //下中侧
                    return HT.HTBOTTOM;
                }
                else if (pos.Y <= caption)
                {
                    return HT.HTCAPTION;
                }
                else
                {
                    return HT.HTCLIENT;
                }
            }
        }
        Cursor _GetCursor(HT arg_ht)
        {
            var cursor = (Cursor)null;
            if (_dtCursor.TryGetValue(arg_ht, out cursor))
            {
                return cursor;
            }
            else
            {
                return Cursors.Default;
            }
        }
        MousePreview _mp;
        readonly Dictionary<HT, Cursor> _dtCursor = new Dictionary<HT, Cursor>();
        int _border = 5;
        int _caption = 20;
    }
}
其主要思想是通过设置SetWindowsHookEx的WH_MOUSE来截获当前应用程序的鼠标事件。 
随后我们对WM_LBUTTONDOWN的消息加点料
   var p = argr_stInfo.pt;
   var cp = this.PointToClient(new Point(p.x, p.y));
   var hc = _GetHT(cp, _border, _caption);
   if (hc != HT.HTERROR && hc != HT.HTCLIENT)
   {
       Task.Factory.StartNew(() =>
       {
           this.BeginInvoke(new Action(()=>
           {
               NativeMethods.ReleaseCapture();
               NativeMethods.SendMessage(this.Handle, WM.WM_NCLBUTTONDOWN, (UIntPtr)hc, (IntPtr)0);
           }));
       });
       return eMPResult.CutOffMessage;
   }
   else
   {
       return eMPResult.ContinueHook;
   }- _GetHT函数用于计算客户点(cp)是属于哪一个区域,包括但不限于HT_CAPTION,HT_CLIENT,HT_HTLEFT. 
 此函数主要通过给定的边框宽度border和给定的标题栏高度caption来确定NC(Not Client)区域的范围。(这里说一句题外话,如果你的border和caption设置的过大, 导致你的控件位于NC区域的部分将无法响应鼠标信息-_-#)
- 接着说下去,点击区域位于我们认为的NC区域,我们截断此消息(Cut off message),否则放过此消息。截获消息的同时,异步发送WM_NCLBUTTONDOWN消息给窗体的消息处理函数,要求窗体处理NC消息 
- 这样初步完成了Broderless的效果。为了进一步完善鼠标显示的效果,可以截获WM.WM_MOUSEMOVE消息,随后显示对应的光标
说一下实际使用中,强烈不推荐使用Caption这个属性,建议这个属性设置为0,然后自己实现一个Caption的控件,捕获MouseDown,然后自己发送NC消息。因为如果你使用Caption的话,你都没法在Caption上弄个关闭按钮,所以我其实是这么搞得:
    public partial class FormBorderless2 : BorderlessForm
    {
        public FormBorderless2()
        {
            InitializeComponent();
            Border = 0;
            panelCaption.MouseDown += panelCaption_MouseDown;
            btnClose.Click += btnClose_Click;
        }
        void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        void panelCaption_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left)
            {
                NativeMethods.ReleaseCapture();
                NativeMethods.SendMessage(this.Handle, WM.WM_NCLBUTTONDOWN, (UIntPtr)HT.HTCAPTION, (IntPtr)0);
            }
        }
    }我另外搞了一个panelCaption,来实现各种按钮以及Caption的效果。
最后贴一下MousePreivew的实现
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using NM = norlib.Native.NativeMethods;
using norlib.Native;
using norlib.SystemExtension;
namespace norlib.Controls
{
    /// <summary>
    /// 精简的 NM.HOOKMOUSEPROC
    /// 1.去掉了HC nCode
    ///   因为只响应HC.HC_ACTION
    /// 2.将UIntPtr wParam直接转化为WM
    ///    方便用户使用
    /// 3.返回值的作用不同于NM.HOOKMOUSEPROC
    /// </summary>
    /// <param name="arg_wm"></param>
    /// <param name="stHookStruct"></param>
    /// <returns></returns>
    public delegate eMPResult MPMOUSEPROC(WM wm, ref MOUSEHOOKSTRUCT stHookStruct);
    public class MousePreview
    {
        /// <summary>
        /// 把相关的鼠标消息发送给此(控件/窗体)
        /// </summary>
        /// <param name="arg_parent">
        /// </param>
        public MousePreview(Control arg_parent)
        {
            if (null == arg_parent)
                throw new NotSupportedException(string.Format("{0}的构造函数不接受参数arg_parent为Null", typeof(MousePreview).Name));
            _hookProc = new NM.HOOKMOUSEPROC(_MyMouseProc);
            _hookHandler = NM.SetWindowsHookEx(WH.WH_MOUSE, _hookProc, 0, NM.GetCurrentThreadId());
            _parent = arg_parent;
        }
        ~MousePreview()
        {
            ReleasePreview();
        }      
        /// <summary>
        /// 将截获的消息改造,计算为适合此(控件/窗体)的正确格式后,
        /// 发送给此(控件/窗体)
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="arg_mpc">
        /// 截获子(控件/窗体)或者仅截获顶层窗体的消息
        /// </param>
        /// <returns></returns>
        public bool AddMouseMessage(WM msg, eMPCategory arg_mpc = eMPCategory.Sub)
        {
            try
            {
                if (null == _parent)
                    return false;
                _dtCategory.Add(msg, arg_mpc);
                _dtWM.Add(msg, _DefaultMPMouseProc);
                return true;
            }
            catch (System.Exception ex)
            {
                return false;
            }
        }
        public bool AddMouseMessage(WM msg, MPMOUSEPROC mouseProc, eMPCategory arg_mpc = eMPCategory.Sub)
        {
            try
            {
                _dtCategory.Add(msg, arg_mpc);
                _dtWM.Add(msg, mouseProc);
                return true;
            }
            catch (System.Exception ex)
            {
                return false;
            }
        }
        public void ReleasePreview()
        {
            if(_hookHandler != IntPtr.Zero)
            {
                NM.UnhookWindowsHookEx(_hookHandler);
                _hookHandler = IntPtr.Zero;
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam">
        /// 将要被传递的消息Id
        /// </param>
        /// <param name="stHookStruct"></param>
        /// <returns></returns>
        IntPtr _MyMouseProc(HC nCode, UIntPtr wParam, ref MOUSEHOOKSTRUCT stHookStruct)
        {
            if(nCode != HC.HC_ACTION)
                return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
            var msg = (WM)wParam;
            var fn = (MPMOUSEPROC)null;
            if(!_dtWM.TryGetValue(msg, out fn))
                return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
            var e = _dtCategory[msg];
            if(_parent != null && !_Belong(_parent, ref stHookStruct, e))
                return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
            var r = fn(msg, ref stHookStruct);
            if (eMPResult.ContinueHook == r)
                return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
            else if (eMPResult.CutOffMessage == r)
            {
                return (IntPtr)(int)-1;
            }
            else if (eMPResult.CutOffNextHook == r)
            {
                return (IntPtr)0;
            }
            else
            {
                throw new NotImplementedException();
            }
        }
        bool _Belong(Control arg_control, ref MOUSEHOOKSTRUCT stHookStruct, eMPCategory arg_e)
        {
            var b = false;
            if(arg_e.HasFlag(eMPCategory.Sub))
            {
                b = b | ((IntPtr)stHookStruct.hWnd).Belong(arg_control, arg_e.HasFlag(eMPCategory.Myself)/*false*/);
            }
            if (arg_e.HasFlag(eMPCategory.InRangeNoFocus))
            {
                var form = arg_control.GetRootForm();
                if (!arg_control.IsDisposed &&
                    stHookStruct.hWnd == arg_control.Handle &&
                    (form.GetFocusedControl()==null) &&
                    stHookStruct.pt.ToPoint().IsIn(arg_control, true))
                {
                    b = b | true;
                }
                else
                {
                    b = b | false;
                }
            }
            return b;
        }
        eMPResult _DefaultMPMouseProc(WM msg, ref MOUSEHOOKSTRUCT stHookStruct)
        {
            var wParam = (UIntPtr)_GetKeyStates();
            var st = stHookStruct;
            _parent.BeginInvoke(new Action(() =>
                {
                    var p = _parent.PointToClient(new Point(st.pt.x, st.pt.y));
                    var lParam = (IntPtr)(p.X + (p.Y << 16));
                    Native.NativeMethods.SendMessage(_parent.Handle, msg, wParam, lParam);
                }));
            return eMPResult.ContinueHook;
        }
        int _GetKeyStates()
        {
            int retval = 0;
            if (NM.HIWORD(NM.GetKeyState(VK.VK_LBUTTON))> 0)
                retval += 1;
            if (NM.HIWORD(NM.GetKeyState(VK.VK_RBUTTON)) > 0)
                retval += 2;
            if (NM.HIWORD(NM.GetKeyState(VK.VK_SHIFT)) > 0)
                retval += 4;
            if (NM.HIWORD(NM.GetKeyState(VK.VK_CONTROL)) > 0)
                retval += 8;
            if (NM.HIWORD(NM.GetKeyState(VK.VK_MBUTTON)) > 0)
                retval += 16;
            if (NM.HIWORD(NM.GetKeyState(VK.VK_XBUTTON1)) > 0)
                retval += 32;
            if (NM.HIWORD(NM.GetKeyState(VK.VK_XBUTTON2)) > 0)
                retval += 64;
            return retval;
        }
        /// <summary>
        /// 把此(控件/窗体)的子控件消息传递给此父窗体
        /// </summary>
        Control _parent;
        NM.HOOKMOUSEPROC _hookProc;
        IntPtr _hookHandler;
        readonly Dictionary<WM, MPMOUSEPROC> _dtWM = new Dictionary<WM, MPMOUSEPROC>();
        readonly Dictionary<WM, eMPCategory> _dtCategory = new Dictionary<WM, eMPCategory>();
    }
    public enum eMPCategory
        :int
    {
        /// <summary>
        /// 所有子(控件/窗体)的消息发送给目标(控件/窗体)
        /// </summary>
        Sub = 1,
        /// <summary>
        /// 消息来源是没有Focus的Form下的目标(控件/窗体)的子控件
        /// 例如一个没有焦点的Form被客户点击了此Form中的子控件
        /// 一般用于捕获窗体的自定义Caption区域( 没有焦点的Form的子控件第一次被单击时,子控件没有OnMouseDown消息)
        /// InRange表示鼠标点击在目标(控件/窗体)
        /// NoFocus表示目标(控件/窗体)所在的Form没有焦点
        /// </summary>
        InRangeNoFocus = 2,
        /// <summary>
        /// Hook得到的消息是目标(控件/窗体)本身发送的数据是否也做进一步处理
        /// </summary>
        Myself = 4,
        All = Sub|InRangeNoFocus|Myself,
    }
    public enum eMPResult
     : int
    {
        /// <summary>
        /// 要求MousePreview不调用CallNextHookEx,直接返回-1,
        /// 告诉Windows不要将消息传递到stHookStruct.hWnd去
        /// </summary>
        CutOffMessage =-1,
        /// <summary>
        /// 0:要求MousePreview不调用CallNextHookEx, 直接返回0,
        /// Windows要将消息传递到stHookStruct.hWnd
        /// </summary>
        CutOffNextHook = 0,
        /// <summary>
        /// 要求MousePreview调用CallNextHookEx
        /// </summary>
        ContinueHook = 1,
    }
}
如何实现一个无边框Form的移动和改变大小(二)的更多相关文章
- 如何实现一个无边框Form的移动和改变大小(一)
		很多时候我们不希望使用Windows提供的窗体. 我们希望使用一个无边框的窗体,什么border,caption透明就行了. 下面我们来说下一些实现方法. 这个方法要求窗体自定义的border siz ... 
- Qt:无标题栏无边框程序的拖动和改变大小
		From: http://blog.csdn.net/kfbyj/article/details/9284923 最近做项目遇到的问题,总结下. 有时候我们觉得系统的标题栏和按钮太丑太呆板,想做自己的 ... 
- Qt 无标题无边框程序的拖动和改变大小
		最近做项目遇到的问题,总结下. 有时候我们觉得系统的标题栏和按钮太丑太呆板,想做自己的标题栏以及最大化.最小化.关闭,菜单按钮,我们就需要 setWindowFlags(Qt::FramelessWi ... 
- WINFROM 无边框窗体的移动和改变大小
		因为去掉了边框 移动和调整大小都用不了了,可以调用WIN32的API来实现 1.定义必须常量 ; ; ; ; ; ; const int Guying_HTBOTTOMLEFT = 0x10; ; ... 
- Qt无边框MainWindow如何拖动四周改变大小
		原来还有winEvent(), x11Event() and macEvent() 这些东西...不过貌似还需要找更好的办法,否则就无法跨平台了. 你需要重新处理部分窗体事件,以下代码适用于Windo ... 
- Delphi无边框Form拖动
		用Delphi做登陆窗口,如果使用无边框Form,想要拖动窗口,可以在某个控件的OnMouseDown事件中写下以下代码 ReleaseCapture; Perform(WM_SYSCOMMAND, ... 
- QT: 如何移动和缩放一个无边框窗口
		一个QT窗口如下可以做到无边框: Window { id: window //Designer 竟然不支持..., 设计模式时要注意 flags: Qt.FramelessWindowHint wid ... 
- 【CITE】 C#中实现拖动无边框Form窗体
		首先建一个Windows应用程序 将Form1的 FormBorderStyle属性设置为None 主要是在Form1窗体触发三个事件:Form4_MouseDown,Form4_MouseMove, ... 
- WPF实现无边框窗体拖拽右下角▲ 改变窗体大小【framwork4.0】 谢谢大家关注
		效果图:(右下角拖拽改变窗体大小) 第一步:添加xaml代码: <Border Name="ResizeBottomRight" MouseMove="Resize ... 
随机推荐
- EasyPlayer iOS开源流媒体播放器中AAC解码PCM问题
			本文转自EasyDarwin开源团队成员Penggy的博客:http://www.jianshu.com/p/feeb107b6657 最近遇到在 iOS 平台上实时播放 AAC 音频数据流, 一开始 ... 
- 分布式流媒体直播服务器系统 For Linux
			在之前的一篇<基于Darwin实现的分布式流媒体直播服务器系统>中,我们配置了在Win32下面的流媒体直播系统,今天我们分享一下在Linux下面EasyDSS分布式直播服务器系统的配置. ... 
- struts2的核心和工作原理 (转)
			转自--------http://blog.csdn.net/laner0515/article/details/27692673 在学习struts2之前,首先我们要明白使用struts2的目的是什 ... 
- SQL Server 2005中top关键字的用法
			1.返回N条记录数 select top n * from <表名> [查询条件] 2.返回总结果集中指定百分比记录数 select top n percent * from <表名 ... 
- Cocos2d-x动画播放(序列帧)
			简介 Cocos2d-x中,动画的具体内容是依靠精灵显示出来的,为了显示动态图片,我们需要不停切换精灵显示的内容,通过把静态的精灵变为动画播放器从而实现动画效果.动画由帧组成,每一帧都是一个纹理,我们 ... 
- dialog更新数据
			将数据显示在最上面 
- ubuntu:undefined reference to `snd_pcm_open'
			这几天在做一个局域网的对讲机和广播系统. 需要用到alsa的库来进行音频采集和播放. 但是在编译程序的时候有个比较奇怪的问题. undefined reference to `snd_pcm_open ... 
- 基于BASYS2的VHDL程序——分频和数码管静态显示程序
			转载请注明出处:http://www.cnblogs.com/connorzx/p/3633860.html 分频是基于计数器程序.由于FPGA的并行处理能力,根本不需要单片机式的中断指令,用起来很方 ... 
- html5--6-1 引入外部样式表
			html5--6-1 引入外部样式表 实例 学习要点 掌握引入外部样式表方法 插入样式的三种方法 内联样式表(行内) 内部样式表(style中) 外部样式表 创建一个外部样式表 在head中使用lin ... 
- TensorFlow 图像预处理(一) 图像编解码,图像尺寸调整
			from: https://blog.csdn.net/chaipp0607/article/details/73029923 TensorFlow提供了几类图像处理函数,下面介绍图像的编码与解码,图 ... 
