You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 23:35:20