WinForms无边框窗体缩放边缘闪烁问题求助
无边框WinForms窗体缩放时边缘闪烁问题
我已实现WinForms无边框窗体的各项功能,但在顶部、左上角或左侧缩放窗体时,对侧会出现闪烁。控件的闪烁问题已解决,目前聚焦于消除窗体边缘的闪烁,同时保持完全无边框状态。我知道问题源于窗体先绘制移动距离再重绘尺寸(或反之),但无法解决。
尝试过的方法
- PInvoke WndProc方式实现缩放
- 直接设置窗体位置和尺寸
- 修改位置和尺寸时锁定窗体
以上方法均无法消除闪烁,除非使用可缩放窗体,但会残留少量标题栏像素,不符合需求。搜索到的所有方法结果一致。
最初实现的EdgeDrag类代码
using System.Drawing; using System.Windows.Forms; namespace TestBorderlessModernGUI { /// <summary> /// Inherit from EdgeDrag in the main form /// </summary> public class EdgeDrag : Form { protected bool isDragging = false; protected bool canDrag = false; protected Rectangle lastRectangle = new Rectangle(); public EdgeDrag() { } /// <summary> /// Call after component Initialization in the Main Form /// </summary> protected void InitialiseFormEdge() { int resizeWidth = 2; this.MouseDown += new MouseEventHandler(form_MouseDown); this.MouseMove += new MouseEventHandler(form_MouseMove); this.MouseUp += new MouseEventHandler(form_MouseUp); // bottom UserControl uc1 = new UserControl() { Anchor = (AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right), Height = resizeWidth, Width = this.DisplayRectangle.Width - (resizeWidth * 2), Left = resizeWidth, Top = this.DisplayRectangle.Height - resizeWidth, BackColor = Color.Transparent, Cursor = Cursors.SizeNS }; uc1.MouseDown += form_MouseDown; uc1.MouseUp += form_MouseUp; uc1.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { this.Size = new Size(lastRectangle.Width, e.Y - lastRectangle.Y + this.Height); } }; uc1.BringToFront(); this.Controls.Add(uc1); this.Controls.SetChildIndex(uc1, 0); // right UserControl uc2 = new UserControl() { Anchor = (AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom), Height = this.DisplayRectangle.Height - (resizeWidth * 2), Width = resizeWidth, Left = this.DisplayRectangle.Width - resizeWidth, Top = resizeWidth, BackColor = Color.Transparent, Cursor = Cursors.SizeWE }; uc2.MouseDown += form_MouseDown; uc2.MouseUp += form_MouseUp; uc2.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { this.Size = new Size(e.X - lastRectangle.X + this.Width, lastRectangle.Height); } }; uc2.BringToFront(); this.Controls.Add(uc2); this.Controls.SetChildIndex(uc2, 0); // bottom-right UserControl uc3 = new UserControl() { Anchor = (AnchorStyles.Bottom | AnchorStyles.Right), Height = resizeWidth, Width = resizeWidth, Left = this.DisplayRectangle.Width - resizeWidth, Top = this.DisplayRectangle.Height - resizeWidth, BackColor = Color.Transparent, Cursor = Cursors.SizeNWSE }; uc3.MouseDown += form_MouseDown; uc3.MouseUp += form_MouseUp; uc3.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { this.Size = new Size((e.X - lastRectangle.X + this.Width), (e.Y - lastRectangle.Y + this.Height)); } }; uc3.BringToFront(); this.Controls.Add(uc3); this.Controls.SetChildIndex(uc3, 0); // top-right UserControl uc4 = new UserControl() { Anchor = (AnchorStyles.Top | AnchorStyles.Right), Height = resizeWidth, Width = resizeWidth, Left = this.DisplayRectangle.Width - resizeWidth, Top = 0, BackColor = Color.Transparent, Cursor = Cursors.SizeNESW }; uc4.MouseDown += form_MouseDown; uc4.MouseUp += form_MouseUp; uc4.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { int diff = (e.Location.Y - lastRectangle.Y); int y = (this.Location.Y + diff); this.Location = new Point(this.Location.X, y); this.Size = new Size(e.X - lastRectangle.X + this.Width, (this.Height + (diff * -1))); } }; uc4.BringToFront(); //uc4.BackColor = Color.Firebrick; this.Controls.Add(uc4); this.Controls.SetChildIndex(uc4, 0); // top UserControl uc5 = new UserControl() { Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right), Height = resizeWidth, Width = this.DisplayRectangle.Width - (resizeWidth * 2), Left = resizeWidth, Top = 0, BackColor = Color.Transparent, Cursor = Cursors.SizeNS }; uc5.MouseDown += form_MouseDown; uc5.MouseUp += form_MouseUp; uc5.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { int diff = (e.Location.Y - lastRectangle.Y); int y = (this.Location.Y + diff); this.Location = new Point(this.Location.X, y); this.Size = new Size(lastRectangle.Width, (this.Height + (diff * -1))); } }; uc5.BringToFront(); this.Controls.Add(uc5); this.Controls.SetChildIndex(uc5, 0); // left UserControl uc6 = new UserControl() { Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom), Height = this.DisplayRectangle.Height - (resizeWidth * 2), Width = resizeWidth, Left = 0, Top = resizeWidth, BackColor = Color.Transparent, Cursor = Cursors.SizeWE }; uc6.MouseDown += form_MouseDown; uc6.MouseUp += form_MouseUp; uc6.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { int diff = (e.Location.X - lastRectangle.X); int x = (this.Location.X + diff); this.Location = new Point(x, this.Location.Y); this.Size = new Size((this.Width + (diff * -1)), this.Height); } }; uc6.BringToFront(); this.Controls.Add(uc6); this.Controls.SetChildIndex(uc6, 0); // bottom-left UserControl uc7 = new UserControl() { Anchor = (AnchorStyles.Bottom | AnchorStyles.Left), Height = resizeWidth, Width = resizeWidth, Left = 0, Top = this.DisplayRectangle.Height - resizeWidth, BackColor = Color.Transparent, Cursor = Cursors.SizeNESW }; uc7.MouseDown += form_MouseDown; uc7.MouseUp += form_MouseUp; uc7.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { int diff = (e.Location.X - lastRectangle.X); int x = (this.Location.X + diff); this.Location = new Point(x, this.Location.Y); this.Size = new Size((this.Width + (diff * -1)), (e.Y - lastRectangle.Y + this.Height)); } }; uc7.BringToFront(); this.Controls.Add(uc7); this.Controls.SetChildIndex(uc7, 0); // top-left UserControl uc8 = new UserControl() { Anchor = (AnchorStyles.Top | AnchorStyles.Left), Height = resizeWidth, Width = resizeWidth, Left = 0, Top = 0, BackColor = Color.Transparent, Cursor = Cursors.SizeNWSE }; uc8.MouseDown += form_MouseDown; uc8.MouseUp += form_MouseUp; uc8.MouseMove += delegate (object sender, MouseEventArgs e) { if (isDragging) { int dX = (e.Location.X - lastRectangle.X); int dY = (e.Location.Y - lastRectangle.Y); int x = (this.Location.X + dX); int y = (this.Location.Y + dY); this.Location = new Point(x, y); this.Size = new Size((this.Width + (dX * -1)), (this.Height + (dY * -1))); } }; uc8.BringToFront(); this.Controls.Add(uc8); this.Controls.SetChildIndex(uc8, 0); } private void form_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { isDragging = true; lastRectangle = new Rectangle(e.Location.X, e.Location.Y, this.Width, this.Height); // If the mouse is within the dragging area if (e.Y <= 30 && e.X <= this.Size.Width - 120) canDrag = true; } } private void form_MouseMove(object sender, MouseEventArgs e) { if (isDragging) { // If we can drag the form if (canDrag) { int x = (this.Location.X + (e.Location.X - lastRectangle.X)); int y = (this.Location.Y + (e.Location.Y - lastRectangle.Y)); this.Location = new Point(x, y); } } } private void form_MouseUp(object sender, MouseEventArgs e) { if (isDragging) isDragging = false; if (canDrag) canDrag = false; } } }
补充:改用WndProc实现后的情况
按照建议改用WndProc实现缩放,闪烁情况仍未改善,开始怀疑只有使用可缩放窗体才能解决,求其他思路。
窗体构造函数
public MainForm() { InitializeComponent(); this.FormBorderStyle = FormBorderStyle.None; this.DoubleBuffered = true; this.SetStyle(ControlStyles.ResizeRedraw, true); }
WndProc边缘缩放代码
private const int HTLEFT = 10, HTRIGHT = 11, HTTOP = 12, HTTOPLEFT = 13, HTTOPRIGHT = 14, HTBOTTOM = 15, HTBOTTOMLEFT = 16, HTBOTTOMRIGHT = 17, HTCAPTION = 2; const int edgeSize = 3; // you can rename this variable if you like Rectangle Top { get { return new Rectangle(0, 0, this.ClientSize.Width, edgeSize); } } Rectangle Left { get { return new Rectangle(0, 0, edgeSize, this.ClientSize.Height); } } Rectangle Bottom { get { return new Rectangle(0, this.ClientSize.Height - edgeSize, this.ClientSize.Width, edgeSize); } } Rectangle Right { get { return new Rectangle(this.ClientSize.Width - edgeSize, 0, edgeSize, this.ClientSize.Height); } } Rectangle TopLeft { get { return new Rectangle(0, 0, edgeSize, edgeSize); } } Rectangle TopRight { get { return new Rectangle(this.ClientSize.Width - edgeSize, 0, edgeSize, edgeSize); } } Rectangle BottomLeft { get { return new Rectangle(0, this.ClientSize.Height - edgeSize, edgeSize, edgeSize); } } Rectangle BottomRight { get { return new Rectangle(this.ClientSize.Width - edgeSize, this.ClientSize.Height - edgeSize, edgeSize, edgeSize); } } protected override CreateParams CreateParams { get { var cp = base.CreateParams; cp.ExStyle |= 0x02000000; return cp; } } protected override void OnPaint(PaintEventArgs e) { if (this.WindowState == FormWindowState.Normal) { Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, this.ClientSize.Height - cGrip, cGrip, cGrip); ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc); } } protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == 0x84) { // Trap WM_NCHITTEST var cursor = this.PointToClient(Cursor.Position); if (TopLeft.Contains(cursor)) m.Result = (IntPtr)HTTOPLEFT; else if (TopRight.Contains(cursor)) m.Result = (IntPtr)HTTOPRIGHT; else if (BottomLeft.Contains(cursor)) m.Result = (IntPtr)HTBOTTOMLEFT; else if (BottomRight.Contains(cursor)) m.Result = (IntPtr)HTBOTTOMRIGHT; else if (Top.Contains(cursor)) m.Result = (IntPtr)HTTOP; else if (Left.Contains(cursor)) m.Result = (IntPtr)HTLEFT; else if (Right.Contains(cursor)) m.Result = (IntPtr)HTRIGHT; else if (Bottom.Contains(cursor)) m.Result = (IntPtr)HTBOTTOM; else if (cursor.Y < cCaption) m.Result = (IntPtr)HTCAPTION; } }
可行的解决思路
1. 强化双缓冲与重绘控制
除了窗体的DoubleBuffered = true,开启更多控件样式优化绘制流程:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); this.UpdateStyles();
AllPaintingInWmPaint让控件只在WM_PAINT消息中完成绘制,避免额外重绘触发;UserPaint指定控件自行处理绘制,减少系统默认步骤。
2. 拦截WM_ERASEBKGND消息
闪烁的常见原因是重绘前系统会擦除背景,拦截该消息并返回true,跳过不必要的背景擦除:
protected override void WndProc(ref Message m) { if (m.Msg == 0x14) // WM_ERASEBKGND { m.Result = (IntPtr)1; // 告知系统已自行处理背景擦除 return; } base.WndProc(ref m); // 原有的WM_NCHITTEST处理代码... }
3. 优化CreateParams扩展样式
在原有WS_EX_COMPOSITED基础上,添加WS_EX_LAYERED样式,让窗口使用分层绘制减少闪烁:
protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED:子控件按底层到顶层顺序绘制 cp.ExStyle |= 0x00080000; // WS_EX_LAYERED:分层窗口 return cp; } }
若分层窗口出现绘制问题,可调用SetLayeredWindowAttributes设置不透明:
[DllImport("user32.dll")] private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); private const uint LWA_COLORKEY = 0x1; private const uint LWA_ALPHA = 0x2; // 窗体加载时调用 SetLayeredWindowAttributes(this.Handle, 0, 255, LWA_ALPHA);
4. 一次性更新窗口位置与尺寸
手动缩放时,避免分开设置Location和Size,改用SetBounds或Windows API一次性完成:
// 方式1:使用SetBounds this.SetBounds(newX, newY, newWidth, newHeight); // 方式2:使用SetWindowPos API [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); private const uint SWP_NOZORDER = 0x0004; // 不改变窗口Z顺序 private const uint SWP_NOACTIVATE = 0x0010; // 不激活窗口 SetWindowPos(this.
相关产品推荐
相关产品推荐

