如何实现一个无边框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,   
    }
}

转载于:https://www.cnblogs.com/norsd/p/6359304.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值