如何实现一个无边框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 ...
随机推荐
- 2014年辛星解读css第五节
本小节我们解说css中的"盒模型".即"box model",它通经常使用于在布局的时候使用,这个"盒模型"也有人成为"框模型&q ...
- lnmp下 nginx 配置虚拟主机
<一.参考> 这里以配置2个站点(2个域名)为例,n 个站点可以相应增加调整,假设: IP地址: 202.55.1.100 域名1 example1.com 放在 /www/example ...
- 互联网时代的精准招聘-Uber新手游有感
找工作难.招人也难.漫天的简历,全是求职者广撒网式的复制粘贴,如何找到合适的人.会认真对待职位的人?或许你须要换换思路,看看Uber新出的手机游戏能够咱啥启发. Uber在过去5年已经蹭蹭成长为估值5 ...
- objective-c中#import和@class的差别
在Objective-C中,能够使用#import和@class来引用别的类型, 可是你知道两者有什么差别吗? @class叫做forward-class, 你常常会在头文件的定义中看到通过@cla ...
- 【BZOJ1064】[Noi2008]假面舞会 DFS树
[BZOJ1064][Noi2008]假面舞会 Description 一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会.今年的面具都是主办方特别定制的.每个参加舞会的人都可以在入场时选择 ...
- 同一世界服务器架构--Erlang游戏服务器
Erlang最大的优点是方便,很多基础功能都已经集成到Erlang语言中.之前用C++写服务器的时候,管理TCP连接很繁琐,需要写一大堆代码来实现.底层的框架需要写很多代码实现,这样既浪费时间 ...
- 开源流媒体云视频平台EasyDarwin中EasyCMS服务是如何进行命令转发和消息路由的
EasyCMS介绍 EasyCMS做为EasyDarwin开源流媒体云平台解决方案的一部分,主要进行的是设备的接入和Session(DeviceSession & ClientSession) ...
- The PageFactory
The PageFactory 原文地址:https://github.com/SeleniumHQ/selenium/wiki/PageFactory In order to support the ...
- C++引用详解【转】
本文转载自:http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作 ...
- CentOS(Linux) - 安装软件笔记(总) - 开发环境安装顺序及汇总
1.安装java环境 参考文章 CentOS7.1 使用资源搜集 2.需要可视化管理服务器时,需要先安装VPSmate 参考文章 CentOS(Linux) - 安装软件笔记(一) - VPSMate ...