覆盖winform窗口的调整大小行为
Posted
技术标签:
【中文标题】覆盖winform窗口的调整大小行为【英文标题】:Override resize behavior of winform window 【发布时间】:2013-10-12 22:28:45 【问题描述】:我有一个 winform 窗口。当我改变屏幕大小时,屏幕会立即增大或减小。
我希望窗口的 Resize 行为类似于 Split Container,只要我拖动鼠标,我只会看到标记窗口大小的行,并且只有在离开将进行调整大小操作。
我看到了几个例子,通过隐藏窗口的框架,然后通过单击窗口本身来绘制框架。
我希望通过点击窗口的框架(我不想隐藏框架)和窗口上的不。
有没有办法做到这一点? (可能以任何方式覆盖 Resize 的行为)。
【问题讨论】:
【参考方案1】:我很确定您在 Internet 上找不到任何解决方案。不过,我已经为此尝试了一个演示,它相当效果很好。
在 winforms
和许多其他 UI 技术中,您不能在窗口本身之外呈现一些东西。为了获得我们想要的效果,我们必须根据用户调整窗口大小的方式在窗口外部或内部渲染一些指示性边框。所以看起来我们被困住了?
但是有一种技术可以做到这一点(我称之为层技术)。我们需要一个透明的非聚焦层来渲染指示性边框。该层的Size
和Location
将与主窗口的Size
和Location
(只有一点偏移)同步。该层默认为invisible
,仅在用户调整大小时显示,在结束调整大小时隐藏。
对于我提到的技术,这还不错。但是,当用户调整窗口大小时,如何防止/放弃默认调整大小?幸运的是,Win32
支持 2 条消息,以便轻松完成:
LParam
在调整大小时保存当前窗口的RECT
结构。我们阅读此信息以正确呈现指示性边框。那么我们需要将这个RECT
修改为窗口当前的Bounds
,以放弃默认的大小调整效果(大小和位置立即改变)。
WM_EXITSIZEMOVE :在调整大小或移动结束时发送到窗口。我们需要捕捉到这个消息,根据透明层的Size
和Location
分配窗口的Size
和Location
,当然然后隐藏层。
现在问题完全可以解决了。这是我制作的演示代码。请注意,这里有一个非常讨厌的无法解决和难以理解的错误,它发生在您调整Top-Left
角的大小时,释放鼠标后Size
会正确更新,但Location
设置了偏移量。我已经调试但没有运气。在某些时候,Top
和 Left
会因为没有明确的原因而跳到意想不到的值。 但是,调整所有边(左、上、右、下)和其他角的大小是可以的。事实上,用户很难通过Top-Left corner
调整大小,所以我认为这个解决方案是可以接受的。
//Must add using System.Runtime.InteropServices;
public partial class Form1 : Form
public Form1()
InitializeComponent();
//Sizing border initialization
SizingBorderWidth = 3;
SizingBorderStyle = DashStyle.Custom;
SizingBorderColor = Color.Orange;
//layer initialization
layer.Owner = this;//especially this one.
layer.Width = Width + SizingBorderWidth * 2;
layer.Height = Height + SizingBorderWidth * 2;
//Paint the border when sizing
layer.Paint += (s, e) =>
using (Pen p = new Pen(SizingBorderColor) Width = SizingBorderWidth )
if (Use3DSizingBorder)
ControlPaint.DrawBorder3D(e.Graphics, sizingRect.Left, sizingRect.Top, sizingRect.Width, sizingRect.Height, Border3DStyle.Bump, Border3DSide.All);
else
p.DashStyle = SizingBorderStyle;
p.LineJoin = LineJoin.Round;
if(p.DashStyle == DashStyle.Custom)
p.DashPattern = new float[] 8f, 1f, 1f, 1f ;//length of each dash from right to left
e.Graphics.DrawRectangle(p, sizingRect);
;
//Bind the Location of the main form and the layer form together
LocationChanged += (s, e) =>
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
;
//Set the intial Location of layer
Load += (s, e) =>
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
;
//Set this to true to use 3D indicative/preview border
public bool Use3DSizingBorder get; set;
//Change the indicative/preview border thickness
public int SizingBorderWidth get; set;
//Change the indicative/preview border style
public DashStyle SizingBorderStyle get; set;
//Change the indicative/preview border color
public Color SizingBorderColor get; set;
//hold the current sizing Rectangle
Rectangle sizingRect;
bool startSizing;
bool suppressSizing;
//This is a Win32 RECT struct (don't use Rectangle)
public struct RECT
public int left, top, right, bottom;
protected override void WndProc(ref Message m)
if (m.Msg == 0x214&&!suppressSizing)//WM_SIZING = 0x214
RECT rect = (RECT) m.GetLParam(typeof(RECT));
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
sizingRect = new Rectangle() X = SizingBorderWidth/2, Y = SizingBorderWidth/2,
Width = w, Height = h;
layer.Left = rect.left-SizingBorderWidth;
layer.Top = rect.top-SizingBorderWidth;
layer.Width = w+2*SizingBorderWidth;
layer.Height = h+2*SizingBorderWidth;
if (!startSizing)
layer.Show();
startSizing = true;
layer.Invalidate();
//Keep the current position and size fixed
rect.right = Right;
rect.bottom = Bottom;
rect.top = Top;
rect.left = Left;
//---------------------------
Marshal.StructureToPtr(rect, m.LParam, true);
if (m.Msg == 0x232)//WM_EXITSIZEMOVE = 0x232
layer.Visible = false;
BeginInvoke((Action)(() =>
suppressSizing = true;
Left = layer.Left + SizingBorderWidth;
Top = layer.Top + SizingBorderWidth;
Width = layer.Width - 2 * SizingBorderWidth;
Height = layer.Height - SizingBorderWidth * 2;
suppressSizing = false;
));
startSizing = false;
base.WndProc(ref m);
//Here is the layer I mentioned before.
NoActivationForm layer = new NoActivationForm();
public class NoActivationForm : Form
public NoActivationForm()
//The following initialization is very important
TransparencyKey = BackColor;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
//----------------------------------------------
protected override bool ShowWithoutActivation
get return true;
一些屏幕截图:
编辑:(此编辑由 OP Hodaya Shalom
建议(奇怪:)
我找到了解决左角问题的方法:
在 BeginInvoke 之前我保存变量并在调用中放入局部变量:
int _top = layer.Top + SizingBorderWidth;
int _left = layer.Left + SizingBorderWidth;
int _width = layer.Width - 2 * SizingBorderWidth;
int _height = layer.Height - SizingBorderWidth * 2;
BeginInvoke((Action)(() =>
suppressSizing = true;
Left = _left;
Top = _top;
Width =_width;
Height =_height;
suppressSizing = false;
));
【讨论】:
@HodayaShalom 感谢您发布建议。真的行。好吧,但是我不认为这是因为使用了BeginInvoke
,我使用了BeginInvoke
,因为我认为我们需要尽快执行窗口更新。我们不需要使用BeginInvoke
,如果是因为BeginInvoke
,删除BeginInvoke
会解决问题,但它不能。我认为分配Left
和Top
直接 可能会导致问题,因此将值保存到一些局部变量中可以解决它。至少感谢您,我知道这种奇怪的行为,并将在相同的情况下应用它。以上是关于覆盖winform窗口的调整大小行为的主要内容,如果未能解决你的问题,请参考以下文章