如何使用窗体边框上的控件修复无边界窗体大小调整?
How to fix borderless form resize with controls on borders of the form?
我有一个无边界的 winForm,我需要调整它的大小,我设法这样做了:
protected override void WndProc(ref Message m)
{
const int wmNcHitTest = 0x84;
const int htLeft = 10;
const int htRight = 11;
const int htTop = 12;
const int htTopLeft = 13;
const int htTopRight = 14;
const int htBottom = 15;
const int htBottomLeft = 16;
const int htBottomRight = 17;
if (m.Msg == wmNcHitTest)
{
Console.Write(true + "\n");
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
///allow resize on the lower right corner
if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
///allow resize on the lower left corner
if (pt.X <= 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomRight : htBottomLeft);
return;
}
///allow resize on the upper right corner
if (pt.X <= 16 && pt.Y <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htTopRight : htTopLeft);
return;
}
///allow resize on the upper left corner
if (pt.X >= clientSize.Width - 16 && pt.Y <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htTopLeft : htTopRight);
return;
}
///allow resize on the top border
if (pt.Y <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htTop);
return;
}
///allow resize on the bottom border
if (pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htBottom);
return;
}
///allow resize on the left border
if (pt.X <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htLeft);
return;
}
///allow resize on the right border
if (pt.X >= clientSize.Width - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htRight);
return;
}
}
else
{
Console.Write(false + "\n");
}
base.WndProc(ref m);
}
问题是在我的表单的左右边框上有控件,所以上面代码中使用的调整大小覆盖在有任何类型控件的那些区域不起作用。
这是一个例子:
在上图中,您可以看到标记区域内的标签位于我的表单的左边框上,它不允许我调整它的大小。
有办法解决这个问题吗?
这里的问题是获取鼠标通知的是 Label 控件,而不是您的无边框窗体。到目前为止,解决此问题的最佳方法是使标签对鼠标透明。您已经知道如何做到这一点,WM_NCHITTEST 也允许返回 HTTRANSPARENT。 Windows 一直在寻找通知的下一个候选者,它将是标签的父级。
标签特别容易做到,因为您通常根本不需要它的鼠标事件:
using System;
using System.Windows.Forms;
public class LabelEx : Label {
protected override void WndProc(ref Message m) {
const int wmNcHitTest = 0x84;
const int htTransparent = -1;
if (!DesignMode && m.Msg == wmNcHitTest) m.Result = new IntPtr(htTransparent);
else base.WndProc(ref m);
}
}
适用于任何控件 class,如果它是一个按钮,您会希望更有选择性。可能是你所需要的,但如果你在边缘附近有很多不同类型的控件,那么仍然很尴尬。您可以使用的另一种技术在本机 Windows 编程中称为 "sub-classing"。在 Winforms 中普遍用于为本机 Windows 控件创建包装器 .NET classes。它在这里也工作得很好,你可以偷看任何控件的消息并以这种方式拦截WM_NCHITTEST:
const int edge = 16;
class MouseFilter : NativeWindow {
private Form form;
public MouseFilter(Form form, Control child) {
this.form = form;
this.AssignHandle(child.Handle);
}
protected override void WndProc(ref Message m) {
const int wmNcHitTest = 0x84;
const int htTransparent = -1;
if (m.Msg == wmNcHitTest) {
var pos = new Point(m.LParam.ToInt32());
if (pos.X < this.form.Left + edge ||
pos.Y < this.form.Top + edge||
pos.X > this.form.Right - edge ||
pos.Y > this.form.Bottom - edge) {
m.Result = new IntPtr(htTransparent);
return;
}
}
base.WndProc(ref m);
}
}
并且只需为靠近 window 边缘的每个控件创建一个 MouseFilter 实例:
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
subClassChildren(this.Controls);
}
private void subClassChildren(Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
var rc = this.RectangleToClient(this.RectangleToScreen(ctl.DisplayRectangle));
if (rc.Left < edge || rc.Right > this.ClientSize.Width - edge ||
rc.Top < edge || rc.Bottom > this.ClientSize.Height - edge) {
new MouseFilter(this, ctl);
}
subClassChildren(ctl.Controls);
}
}
我有一个无边界的 winForm,我需要调整它的大小,我设法这样做了:
protected override void WndProc(ref Message m)
{
const int wmNcHitTest = 0x84;
const int htLeft = 10;
const int htRight = 11;
const int htTop = 12;
const int htTopLeft = 13;
const int htTopRight = 14;
const int htBottom = 15;
const int htBottomLeft = 16;
const int htBottomRight = 17;
if (m.Msg == wmNcHitTest)
{
Console.Write(true + "\n");
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
///allow resize on the lower right corner
if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
///allow resize on the lower left corner
if (pt.X <= 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomRight : htBottomLeft);
return;
}
///allow resize on the upper right corner
if (pt.X <= 16 && pt.Y <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htTopRight : htTopLeft);
return;
}
///allow resize on the upper left corner
if (pt.X >= clientSize.Width - 16 && pt.Y <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htTopLeft : htTopRight);
return;
}
///allow resize on the top border
if (pt.Y <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htTop);
return;
}
///allow resize on the bottom border
if (pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htBottom);
return;
}
///allow resize on the left border
if (pt.X <= 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htLeft);
return;
}
///allow resize on the right border
if (pt.X >= clientSize.Width - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(htRight);
return;
}
}
else
{
Console.Write(false + "\n");
}
base.WndProc(ref m);
}
问题是在我的表单的左右边框上有控件,所以上面代码中使用的调整大小覆盖在有任何类型控件的那些区域不起作用。
这是一个例子:
在上图中,您可以看到标记区域内的标签位于我的表单的左边框上,它不允许我调整它的大小。
有办法解决这个问题吗?
这里的问题是获取鼠标通知的是 Label 控件,而不是您的无边框窗体。到目前为止,解决此问题的最佳方法是使标签对鼠标透明。您已经知道如何做到这一点,WM_NCHITTEST 也允许返回 HTTRANSPARENT。 Windows 一直在寻找通知的下一个候选者,它将是标签的父级。
标签特别容易做到,因为您通常根本不需要它的鼠标事件:
using System;
using System.Windows.Forms;
public class LabelEx : Label {
protected override void WndProc(ref Message m) {
const int wmNcHitTest = 0x84;
const int htTransparent = -1;
if (!DesignMode && m.Msg == wmNcHitTest) m.Result = new IntPtr(htTransparent);
else base.WndProc(ref m);
}
}
适用于任何控件 class,如果它是一个按钮,您会希望更有选择性。可能是你所需要的,但如果你在边缘附近有很多不同类型的控件,那么仍然很尴尬。您可以使用的另一种技术在本机 Windows 编程中称为 "sub-classing"。在 Winforms 中普遍用于为本机 Windows 控件创建包装器 .NET classes。它在这里也工作得很好,你可以偷看任何控件的消息并以这种方式拦截WM_NCHITTEST:
const int edge = 16;
class MouseFilter : NativeWindow {
private Form form;
public MouseFilter(Form form, Control child) {
this.form = form;
this.AssignHandle(child.Handle);
}
protected override void WndProc(ref Message m) {
const int wmNcHitTest = 0x84;
const int htTransparent = -1;
if (m.Msg == wmNcHitTest) {
var pos = new Point(m.LParam.ToInt32());
if (pos.X < this.form.Left + edge ||
pos.Y < this.form.Top + edge||
pos.X > this.form.Right - edge ||
pos.Y > this.form.Bottom - edge) {
m.Result = new IntPtr(htTransparent);
return;
}
}
base.WndProc(ref m);
}
}
并且只需为靠近 window 边缘的每个控件创建一个 MouseFilter 实例:
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
subClassChildren(this.Controls);
}
private void subClassChildren(Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
var rc = this.RectangleToClient(this.RectangleToScreen(ctl.DisplayRectangle));
if (rc.Left < edge || rc.Right > this.ClientSize.Width - edge ||
rc.Top < edge || rc.Bottom > this.ClientSize.Height - edge) {
new MouseFilter(this, ctl);
}
subClassChildren(ctl.Controls);
}
}