接着上文:这里写链接内容

我们来说说一个比较复杂的实现,

效果如图:



注意为了能够凸显没有NC(NotClient)区域,我们额外用了3个panel分别放在窗体的左右和下部。用来模拟客户自己的控件。

下面我们说下这种真正的无边框Form的实现方法

下面先无责任的贴下代码

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using System.Windows.Forms;
  11. using norlib;
  12. using norlib.Controls;
  13. using norlib.Error;
  14. using norlib.Native;
  15. using norlib.SystemExtension;
  16. namespace norlib.Controls
  17. {
  18. public partial class BorderlessForm
  19. :Form
  20. {
  21. public BorderlessForm()
  22. {
  23. InitializeComponent();
  24. _caption = 0;
  25. _mp = new MousePreview(this);
  26. #region 初始化_dtCursor
  27. _dtCursor.Add(HT.HTBOTTOM, Cursors.SizeNS);
  28. _dtCursor.Add(HT.HTTOP, Cursors.SizeNS);
  29. _dtCursor.Add(HT.HTLEFT, Cursors.SizeWE);
  30. _dtCursor.Add(HT.HTRIGHT, Cursors.SizeWE);
  31. _dtCursor.Add(HT.HTTOPLEFT, Cursors.SizeNWSE);
  32. _dtCursor.Add(HT.HTTOPRIGHT, Cursors.SizeNESW);
  33. _dtCursor.Add(HT.HTBOTTOMLEFT, Cursors.SizeNESW);
  34. _dtCursor.Add(HT.HTBOTTOMRIGHT, Cursors.SizeNWSE);
  35. #endregion
  36. _mp.AddMouseMessage(WM.WM_LBUTTONDOWN, mp_LButtonDownPreview);
  37. _mp.AddMouseMessage(WM.WM_MOUSEMOVE, mp_MouseMovePreview);
  38. //不能选这个
  39. //this.Capture = true;
  40. }
  41. public int Border
  42. {
  43. get { return _border; }
  44. set
  45. {
  46. if (value <= 0)
  47. return;
  48. _border = value;
  49. }
  50. }
  51. public int Caption
  52. {
  53. get { return _caption; }
  54. set { _caption = value; }
  55. }
  56. eMPResult mp_LButtonDownPreview(WM arg_wm, ref MOUSEHOOKSTRUCT argr_stInfo)
  57. {
  58. var p = argr_stInfo.pt;
  59. var cp = this.PointToClient(new Point(p.x, p.y));
  60. var hc = _GetHT(cp, _border, _caption);
  61. if (hc != HT.HTERROR && hc != HT.HTCLIENT)
  62. {
  63. Task.Factory.StartNew(() =>
  64. {
  65. this.BeginInvoke(new Action(()=>
  66. {
  67. NativeMethods.ReleaseCapture();
  68. NativeMethods.SendMessage(this.Handle, WM.WM_NCLBUTTONDOWN, (UIntPtr)hc, (IntPtr)0);
  69. }));
  70. });
  71. return eMPResult.CutOffMessage;
  72. }
  73. else
  74. {
  75. return eMPResult.ContinueHook;
  76. }
  77. }
  78. eMPResult mp_MouseMovePreview(WM arg_wm, ref MOUSEHOOKSTRUCT argr_stInfo)
  79. {
  80. var p = argr_stInfo.pt;
  81. var cp = this.PointToClient(new Point(p.x, p.y));
  82. var hc = _GetHT(cp, _border, _caption);
  83. if (hc != HT.HTCLIENT && hc != HT.HTERROR)
  84. {
  85. var c = _GetCursor(hc);
  86. if (Cursor != c)
  87. Cursor = c;
  88. return eMPResult.ContinueHook;
  89. }
  90. else
  91. {
  92. Cursor = Cursors.Default;
  93. return eMPResult.ContinueHook;
  94. }
  95. }
  96. HT _GetHT(Point arg_p, int arg_border, int arg_caption)
  97. {
  98. var pos = arg_p;
  99. var border = arg_border;
  100. var caption = arg_caption;
  101. if (pos.X < 0 || pos.Y < 0)
  102. {
  103. //
  104. //非法位置
  105. //
  106. return HT.HTERROR;
  107. }
  108. else if (pos.X <= border)
  109. {
  110. //
  111. //左侧
  112. //
  113. if (pos.Y <= border)
  114. {
  115. //左上侧
  116. return HT.HTTOPLEFT;
  117. }
  118. else if (pos.Y >= this.Height - border)
  119. {
  120. //左下侧
  121. return HT.HTBOTTOMLEFT;
  122. }
  123. else
  124. {
  125. //左侧
  126. return HT.HTLEFT;
  127. }
  128. }
  129. else if (pos.X >= this.Width - border)
  130. {
  131. //
  132. //右侧
  133. //
  134. if (pos.Y <= border)
  135. {
  136. //右上侧
  137. return HT.HTTOPRIGHT;
  138. }
  139. else if (pos.Y >= this.Height - border)
  140. {
  141. //右下侧
  142. return HT.HTBOTTOMRIGHT;
  143. }
  144. else
  145. {
  146. //右侧
  147. return HT.HTRIGHT;
  148. }
  149. }
  150. else
  151. {
  152. //
  153. //中部
  154. //
  155. if (pos.Y <= border)
  156. {
  157. //上中侧
  158. return HT.HTTOP;
  159. }
  160. else if (pos.Y >= this.Height - border)
  161. {
  162. //下中侧
  163. return HT.HTBOTTOM;
  164. }
  165. else if (pos.Y <= caption)
  166. {
  167. return HT.HTCAPTION;
  168. }
  169. else
  170. {
  171. return HT.HTCLIENT;
  172. }
  173. }
  174. }
  175. Cursor _GetCursor(HT arg_ht)
  176. {
  177. var cursor = (Cursor)null;
  178. if (_dtCursor.TryGetValue(arg_ht, out cursor))
  179. {
  180. return cursor;
  181. }
  182. else
  183. {
  184. return Cursors.Default;
  185. }
  186. }
  187. MousePreview _mp;
  188. readonly Dictionary<HT, Cursor> _dtCursor = new Dictionary<HT, Cursor>();
  189. int _border = 5;
  190. int _caption = 20;
  191. }
  192. }

其主要思想是通过设置SetWindowsHookEx的WH_MOUSE来截获当前应用程序的鼠标事件。

随后我们对WM_LBUTTONDOWN的消息加点料

  1. var p = argr_stInfo.pt;
  2. var cp = this.PointToClient(new Point(p.x, p.y));
  3. var hc = _GetHT(cp, _border, _caption);
  4. if (hc != HT.HTERROR && hc != HT.HTCLIENT)
  5. {
  6. Task.Factory.StartNew(() =>
  7. {
  8. this.BeginInvoke(new Action(()=>
  9. {
  10. NativeMethods.ReleaseCapture();
  11. NativeMethods.SendMessage(this.Handle, WM.WM_NCLBUTTONDOWN, (UIntPtr)hc, (IntPtr)0);
  12. }));
  13. });
  14. return eMPResult.CutOffMessage;
  15. }
  16. else
  17. {
  18. return eMPResult.ContinueHook;
  19. }
  • _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上弄个关闭按钮,所以我其实是这么搞得:

  1. public partial class FormBorderless2 : BorderlessForm
  2. {
  3. public FormBorderless2()
  4. {
  5. InitializeComponent();
  6. Border = 0;
  7. panelCaption.MouseDown += panelCaption_MouseDown;
  8. btnClose.Click += btnClose_Click;
  9. }
  10. void btnClose_Click(object sender, EventArgs e)
  11. {
  12. this.Close();
  13. }
  14. void panelCaption_MouseDown(object sender, MouseEventArgs e)
  15. {
  16. if(e.Button == MouseButtons.Left)
  17. {
  18. NativeMethods.ReleaseCapture();
  19. NativeMethods.SendMessage(this.Handle, WM.WM_NCLBUTTONDOWN, (UIntPtr)HT.HTCAPTION, (IntPtr)0);
  20. }
  21. }
  22. }

我另外搞了一个panelCaption,来实现各种按钮以及Caption的效果。

最后贴一下MousePreivew的实现

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Linq;
  5. using System.Runtime.InteropServices;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using NM = norlib.Native.NativeMethods;
  9. using norlib.Native;
  10. using norlib.SystemExtension;
  11. namespace norlib.Controls
  12. {
  13. /// <summary>
  14. /// 精简的 NM.HOOKMOUSEPROC
  15. /// 1.去掉了HC nCode
  16. /// 因为只响应HC.HC_ACTION
  17. /// 2.将UIntPtr wParam直接转化为WM
  18. /// 方便用户使用
  19. /// 3.返回值的作用不同于NM.HOOKMOUSEPROC
  20. /// </summary>
  21. /// <param name="arg_wm"></param>
  22. /// <param name="stHookStruct"></param>
  23. /// <returns></returns>
  24. public delegate eMPResult MPMOUSEPROC(WM wm, ref MOUSEHOOKSTRUCT stHookStruct);
  25. public class MousePreview
  26. {
  27. /// <summary>
  28. /// 把相关的鼠标消息发送给此(控件/窗体)
  29. /// </summary>
  30. /// <param name="arg_parent">
  31. /// </param>
  32. public MousePreview(Control arg_parent)
  33. {
  34. if (null == arg_parent)
  35. throw new NotSupportedException(string.Format("{0}的构造函数不接受参数arg_parent为Null", typeof(MousePreview).Name));
  36. _hookProc = new NM.HOOKMOUSEPROC(_MyMouseProc);
  37. _hookHandler = NM.SetWindowsHookEx(WH.WH_MOUSE, _hookProc, 0, NM.GetCurrentThreadId());
  38. _parent = arg_parent;
  39. }
  40. ~MousePreview()
  41. {
  42. ReleasePreview();
  43. }
  44. /// <summary>
  45. /// 将截获的消息改造,计算为适合此(控件/窗体)的正确格式后,
  46. /// 发送给此(控件/窗体)
  47. /// </summary>
  48. /// <param name="msg"></param>
  49. /// <param name="arg_mpc">
  50. /// 截获子(控件/窗体)或者仅截获顶层窗体的消息
  51. /// </param>
  52. /// <returns></returns>
  53. public bool AddMouseMessage(WM msg, eMPCategory arg_mpc = eMPCategory.Sub)
  54. {
  55. try
  56. {
  57. if (null == _parent)
  58. return false;
  59. _dtCategory.Add(msg, arg_mpc);
  60. _dtWM.Add(msg, _DefaultMPMouseProc);
  61. return true;
  62. }
  63. catch (System.Exception ex)
  64. {
  65. return false;
  66. }
  67. }
  68. public bool AddMouseMessage(WM msg, MPMOUSEPROC mouseProc, eMPCategory arg_mpc = eMPCategory.Sub)
  69. {
  70. try
  71. {
  72. _dtCategory.Add(msg, arg_mpc);
  73. _dtWM.Add(msg, mouseProc);
  74. return true;
  75. }
  76. catch (System.Exception ex)
  77. {
  78. return false;
  79. }
  80. }
  81. public void ReleasePreview()
  82. {
  83. if(_hookHandler != IntPtr.Zero)
  84. {
  85. NM.UnhookWindowsHookEx(_hookHandler);
  86. _hookHandler = IntPtr.Zero;
  87. }
  88. }
  89. /// <summary>
  90. ///
  91. /// </summary>
  92. /// <param name="nCode"></param>
  93. /// <param name="wParam">
  94. /// 将要被传递的消息Id
  95. /// </param>
  96. /// <param name="stHookStruct"></param>
  97. /// <returns></returns>
  98. IntPtr _MyMouseProc(HC nCode, UIntPtr wParam, ref MOUSEHOOKSTRUCT stHookStruct)
  99. {
  100. if(nCode != HC.HC_ACTION)
  101. return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
  102. var msg = (WM)wParam;
  103. var fn = (MPMOUSEPROC)null;
  104. if(!_dtWM.TryGetValue(msg, out fn))
  105. return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
  106. var e = _dtCategory[msg];
  107. if(_parent != null && !_Belong(_parent, ref stHookStruct, e))
  108. return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
  109. var r = fn(msg, ref stHookStruct);
  110. if (eMPResult.ContinueHook == r)
  111. return NM.CallNextHookEx(_hookHandler, nCode, wParam, ref stHookStruct);
  112. else if (eMPResult.CutOffMessage == r)
  113. {
  114. return (IntPtr)(int)-1;
  115. }
  116. else if (eMPResult.CutOffNextHook == r)
  117. {
  118. return (IntPtr)0;
  119. }
  120. else
  121. {
  122. throw new NotImplementedException();
  123. }
  124. }
  125. bool _Belong(Control arg_control, ref MOUSEHOOKSTRUCT stHookStruct, eMPCategory arg_e)
  126. {
  127. var b = false;
  128. if(arg_e.HasFlag(eMPCategory.Sub))
  129. {
  130. b = b | ((IntPtr)stHookStruct.hWnd).Belong(arg_control, arg_e.HasFlag(eMPCategory.Myself)/*false*/);
  131. }
  132. if (arg_e.HasFlag(eMPCategory.InRangeNoFocus))
  133. {
  134. var form = arg_control.GetRootForm();
  135. if (!arg_control.IsDisposed &&
  136. stHookStruct.hWnd == arg_control.Handle &&
  137. (form.GetFocusedControl()==null) &&
  138. stHookStruct.pt.ToPoint().IsIn(arg_control, true))
  139. {
  140. b = b | true;
  141. }
  142. else
  143. {
  144. b = b | false;
  145. }
  146. }
  147. return b;
  148. }
  149. eMPResult _DefaultMPMouseProc(WM msg, ref MOUSEHOOKSTRUCT stHookStruct)
  150. {
  151. var wParam = (UIntPtr)_GetKeyStates();
  152. var st = stHookStruct;
  153. _parent.BeginInvoke(new Action(() =>
  154. {
  155. var p = _parent.PointToClient(new Point(st.pt.x, st.pt.y));
  156. var lParam = (IntPtr)(p.X + (p.Y << 16));
  157. Native.NativeMethods.SendMessage(_parent.Handle, msg, wParam, lParam);
  158. }));
  159. return eMPResult.ContinueHook;
  160. }
  161. int _GetKeyStates()
  162. {
  163. int retval = 0;
  164. if (NM.HIWORD(NM.GetKeyState(VK.VK_LBUTTON))> 0)
  165. retval += 1;
  166. if (NM.HIWORD(NM.GetKeyState(VK.VK_RBUTTON)) > 0)
  167. retval += 2;
  168. if (NM.HIWORD(NM.GetKeyState(VK.VK_SHIFT)) > 0)
  169. retval += 4;
  170. if (NM.HIWORD(NM.GetKeyState(VK.VK_CONTROL)) > 0)
  171. retval += 8;
  172. if (NM.HIWORD(NM.GetKeyState(VK.VK_MBUTTON)) > 0)
  173. retval += 16;
  174. if (NM.HIWORD(NM.GetKeyState(VK.VK_XBUTTON1)) > 0)
  175. retval += 32;
  176. if (NM.HIWORD(NM.GetKeyState(VK.VK_XBUTTON2)) > 0)
  177. retval += 64;
  178. return retval;
  179. }
  180. /// <summary>
  181. /// 把此(控件/窗体)的子控件消息传递给此父窗体
  182. /// </summary>
  183. Control _parent;
  184. NM.HOOKMOUSEPROC _hookProc;
  185. IntPtr _hookHandler;
  186. readonly Dictionary<WM, MPMOUSEPROC> _dtWM = new Dictionary<WM, MPMOUSEPROC>();
  187. readonly Dictionary<WM, eMPCategory> _dtCategory = new Dictionary<WM, eMPCategory>();
  188. }
  189. public enum eMPCategory
  190. :int
  191. {
  192. /// <summary>
  193. /// 所有子(控件/窗体)的消息发送给目标(控件/窗体)
  194. /// </summary>
  195. Sub = 1,
  196. /// <summary>
  197. /// 消息来源是没有Focus的Form下的目标(控件/窗体)的子控件
  198. /// 例如一个没有焦点的Form被客户点击了此Form中的子控件
  199. /// 一般用于捕获窗体的自定义Caption区域( 没有焦点的Form的子控件第一次被单击时,子控件没有OnMouseDown消息)
  200. /// InRange表示鼠标点击在目标(控件/窗体)
  201. /// NoFocus表示目标(控件/窗体)所在的Form没有焦点
  202. /// </summary>
  203. InRangeNoFocus = 2,
  204. /// <summary>
  205. /// Hook得到的消息是目标(控件/窗体)本身发送的数据是否也做进一步处理
  206. /// </summary>
  207. Myself = 4,
  208. All = Sub|InRangeNoFocus|Myself,
  209. }
  210. public enum eMPResult
  211. : int
  212. {
  213. /// <summary>
  214. /// 要求MousePreview不调用CallNextHookEx,直接返回-1,
  215. /// 告诉Windows不要将消息传递到stHookStruct.hWnd去
  216. /// </summary>
  217. CutOffMessage =-1,
  218. /// <summary>
  219. /// 0:要求MousePreview不调用CallNextHookEx, 直接返回0,
  220. /// Windows要将消息传递到stHookStruct.hWnd
  221. /// </summary>
  222. CutOffNextHook = 0,
  223. /// <summary>
  224. /// 要求MousePreview调用CallNextHookEx
  225. /// </summary>
  226. ContinueHook = 1,
  227. }
  228. }

如何实现一个无边框Form的移动和改变大小(二)的更多相关文章

  1. 如何实现一个无边框Form的移动和改变大小(一)

    很多时候我们不希望使用Windows提供的窗体. 我们希望使用一个无边框的窗体,什么border,caption透明就行了. 下面我们来说下一些实现方法. 这个方法要求窗体自定义的border siz ...

  2. Qt:无标题栏无边框程序的拖动和改变大小

    From: http://blog.csdn.net/kfbyj/article/details/9284923 最近做项目遇到的问题,总结下. 有时候我们觉得系统的标题栏和按钮太丑太呆板,想做自己的 ...

  3. Qt 无标题无边框程序的拖动和改变大小

    最近做项目遇到的问题,总结下. 有时候我们觉得系统的标题栏和按钮太丑太呆板,想做自己的标题栏以及最大化.最小化.关闭,菜单按钮,我们就需要 setWindowFlags(Qt::FramelessWi ...

  4. WINFROM 无边框窗体的移动和改变大小

    因为去掉了边框  移动和调整大小都用不了了,可以调用WIN32的API来实现 1.定义必须常量 ; ; ; ; ; ; const int Guying_HTBOTTOMLEFT = 0x10; ; ...

  5. Qt无边框MainWindow如何拖动四周改变大小

    原来还有winEvent(), x11Event() and macEvent() 这些东西...不过貌似还需要找更好的办法,否则就无法跨平台了. 你需要重新处理部分窗体事件,以下代码适用于Windo ...

  6. Delphi无边框Form拖动

    用Delphi做登陆窗口,如果使用无边框Form,想要拖动窗口,可以在某个控件的OnMouseDown事件中写下以下代码 ReleaseCapture; Perform(WM_SYSCOMMAND, ...

  7. QT: 如何移动和缩放一个无边框窗口

    一个QT窗口如下可以做到无边框: Window { id: window //Designer 竟然不支持..., 设计模式时要注意 flags: Qt.FramelessWindowHint wid ...

  8. 【CITE】 C#中实现拖动无边框Form窗体

    首先建一个Windows应用程序 将Form1的 FormBorderStyle属性设置为None 主要是在Form1窗体触发三个事件:Form4_MouseDown,Form4_MouseMove, ...

  9. WPF实现无边框窗体拖拽右下角▲ 改变窗体大小【framwork4.0】 谢谢大家关注

    效果图:(右下角拖拽改变窗体大小) 第一步:添加xaml代码: <Border Name="ResizeBottomRight" MouseMove="Resize ...

随机推荐

  1. 2014年辛星解读css第五节

    本小节我们解说css中的"盒模型".即"box model",它通经常使用于在布局的时候使用,这个"盒模型"也有人成为"框模型&q ...

  2. lnmp下 nginx 配置虚拟主机

    <一.参考> 这里以配置2个站点(2个域名)为例,n 个站点可以相应增加调整,假设: IP地址: 202.55.1.100 域名1 example1.com 放在 /www/example ...

  3. 互联网时代的精准招聘-Uber新手游有感

    找工作难.招人也难.漫天的简历,全是求职者广撒网式的复制粘贴,如何找到合适的人.会认真对待职位的人?或许你须要换换思路,看看Uber新出的手机游戏能够咱啥启发. Uber在过去5年已经蹭蹭成长为估值5 ...

  4. objective-c中#import和@class的差别

    在Objective-C中,能够使用#import和@class来引用别的类型, 可是你知道两者有什么差别吗? @class叫做forward-class,  你常常会在头文件的定义中看到通过@cla ...

  5. 【BZOJ1064】[Noi2008]假面舞会 DFS树

    [BZOJ1064][Noi2008]假面舞会 Description 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择 ...

  6. 同一世界服务器架构--Erlang游戏服务器

        Erlang最大的优点是方便,很多基础功能都已经集成到Erlang语言中.之前用C++写服务器的时候,管理TCP连接很繁琐,需要写一大堆代码来实现.底层的框架需要写很多代码实现,这样既浪费时间 ...

  7. 开源流媒体云视频平台EasyDarwin中EasyCMS服务是如何进行命令转发和消息路由的

    EasyCMS介绍 EasyCMS做为EasyDarwin开源流媒体云平台解决方案的一部分,主要进行的是设备的接入和Session(DeviceSession & ClientSession) ...

  8. The PageFactory

    The PageFactory 原文地址:https://github.com/SeleniumHQ/selenium/wiki/PageFactory In order to support the ...

  9. C++引用详解【转】

    本文转载自:http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作 ...

  10. CentOS(Linux) - 安装软件笔记(总) - 开发环境安装顺序及汇总

    1.安装java环境 参考文章 CentOS7.1 使用资源搜集 2.需要可视化管理服务器时,需要先安装VPSmate 参考文章 CentOS(Linux) - 安装软件笔记(一) - VPSMate ...