C# Winform 实现窗体无系统边框,但是具有系统边框的所有功能,同时保证控件不闪烁

本人新手,想写个上位机,窗口能够实现最小化,最大化,拖动,改变大小等常用功能,Winform窗体的FormBorderStyle设置成sizable即可实现,但是窗体有自带的边框,不好看且无法编辑,想去掉,网上查到几种方法,都尝试了,但是都有缺陷,不太满意,不过最终还是实现了。走了弯路,浪费了不少时间,在这分享一下,最后效果是没有冗余边框,菜单栏在顶部

网上查到有两个方法(方法一和方法二),都需要将FormBorderStyle设置成None,首先说一下窗体的最小化最大化关闭,以及移动,然后是太多嵌套表格后防止闪烁,最后是窗体缩放,可以按目录自取。

目录

窗体最大化最小化和关闭

窗体移动

解决闪烁问题

缩放

方法一

 方法二

方法三

 方法四

完整代码


窗体最大化最小化和关闭

这个简单,直接放三个button,改背景图片,实现相应功能

private void buttonClose_Click(object sender, EventArgs e)
        {
            this.Close();  //关闭窗口
        }

private void buttonMax_Click(object sender, EventArgs e)
       {
           if (this.WindowState == FormWindowState.Maximized)   //如果处于最大化,则还原
           {
               this.WindowState = FormWindowState.Normal;
               Image backImage = Resources.最大化;
               buttonMax.BackgroundImage = backImage;
           }
           else
           {
               this.WindowState = FormWindowState.Maximized;   //如果处于普通状态,则最大化
               Image backImage = Resources.还原;
               buttonMax.BackgroundImage = backImage;
           }
       }

private void buttonMin_Click(object sender, EventArgs e)
       {
           this.WindowState = FormWindowState.Minimized;  //最小化
       }

private void Form1_SizeChanged(object sender, EventArgs e)
       {
           if (this.WindowState != FormWindowState.Maximized)
               buttonMax.BackgroundImage = Resources.最大化;
       }

最后的 Form1_SizeChanged的作用:当窗体最大化时,拖动窗体顶部窗体会变小,但是这种情况下最大化按钮的图标还是还原图标,所以写个方法刷新一下

点击最大化按钮后会发现窗体变大后覆盖了整个屏幕,把任务栏都遮住了,解决这个问题的方法如下:

//在窗体的Load事件中编写
private void Form1_Load(object sender, EventArgs e)
{
     this.MaximizedBounds = Screen.PrimaryScreen.WorkingArea;
}

图标可以到这里下载 :iconfont-阿里巴巴矢量图标库https://www.iconfont.cn/search/index?searchType=icon&q=close&page=1&fills=&tag=&fromCollection=1

窗体移动

 我这里设置的是鼠标在菜单栏上按下时可以拖动,网上方法很多,下面这个是我认为最简单的

//窗体移动
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();

[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

private void menuStrip1_MouseDown(object sender, MouseEventArgs e)
     {
         if (e.Clicks == 1)
         {
             //窗体移动
             if (e.Button == MouseButtons.Left)
             {
                 ReleaseCapture(); //释放鼠标捕捉
                 //发送左键点击的消息至该窗体(标题栏)
                 SendMessage(this.Handle, 0xA1, 0x02, 0);
             }
         }
     }

虽然不懂原理是啥,但是好用哈哈哈

参考文章:winform自定义最大化 - CSDNhttps://www.csdn.net/tags/MtTaAgzsNjk5ODYtYmxvZwO0O0OO0O0O.html

解决闪烁问题

为了让窗体在改变大小时各个部分能够更合理的自动分布,设计时用了很多表格,表格嵌套多了最大化最小化以及缩放窗体时就容易出现闪烁的情况,网上也有较多方法,下面这个是我试了之后有效果的

 代码:

 protected override CreateParams CreateParams   //防止改变窗口大小时控件闪烁
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
                return cp;
            }
        }

参考文章:(1条消息) Winfrom窗体由于控件过多而闪烁的解决方法_路漫漫都是坑的博客-CSDN博客_winform 最大化 闪烁https://blog.csdn.net/qq_25465525/article/details/88739837?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-5-88739837-blog-117190950.pc_relevant_paycolumn_v3&spm=1001.2101.3001.4242.4&utm_relevant_index=8

 补充说明:

FormBorderStyle设置成None,且添加了防闪烁代码后,最大化最小化窗口会显示不全,可以通过在Form1_SizeChanged方法中添加两个refersh()可以解决,但是视觉效果不好

private void Form1_SizeChanged(object sender, EventArgs e)
       {
           if (this.WindowState != FormWindowState.Maximized)
               buttonMax.BackgroundImage = Resources.最大化;
           refresh();
           refresh();
       }

缩放

方法一

自己编写程序,主要通过MouseDown,MouseUp,MouseMove三个方法和自定义ResizeWindow方法实现根据鼠标位置判断和实施对应的窗体缩放。

        //窗体改变大小
        private bool isMouseDown = false;  //表示鼠标当前是否处于按下状态,初始值为否 
        MouseDirection direction = MouseDirection.None;  //表示拖动的方向,起始为None,表示不拖动
        //private Point mPoint;  //鼠标坐标
        private bool changeornot = false;  //是否是改变窗体大小,true为是,false为不是

        //定义一个枚举,表示拖动方向,用于判断
        public enum MouseDirection
        {
            HerizontalLeft,  //水平方向拖动,只改变窗体的宽度
            HerizontalRight,
            VerticalTop,  //垂直方向拖动,只改变窗体的高度 
            VerticalBottom,
            DecliningLefttop,  //倾斜方向,同时改变窗体的宽度和高度
            DecliningLeftbottom,
            DecliningRighttop,
            DecliningRightbottom,
            None  //不做标志,即不拖动窗体改变大小
        }
        //鼠标在窗体内按下时获取鼠标的位置,并设置按下状态为真
        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            //mPoint = new Point(e.X, e.Y);
            isMouseDown = true;
        }
        // 鼠标弹起,不再改变窗体尺寸
        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            isMouseDown = false;
            direction = MouseDirection.None;
        }
        //鼠标移动过程中,坐标时刻在改变,根据不同位置改变拖动方向、鼠标样式和changeornot的状态
        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (isMouseDown && direction != MouseDirection.None) //满足条件则直接执行改变窗口大小的方法
            {
                ResizeWindow();
                return;
            }
            //当鼠标移动时横坐标距离窗体左边缘5像素以内且纵坐标距离上边缘也在5像素以内时
            if (e.Location.X <= 5 && e.Location.Y <= 5)
            {
                this.Cursor = Cursors.SizeNWSE;   //光标变为右下倾斜的箭头形状\
                direction = MouseDirection.DecliningLefttop;
                changeornot = true;
            }
            //左下
            else if (e.Location.X <= 5 && e.Location.Y >= this.Height - 5)
            {
                this.Cursor = Cursors.SizeNESW;   //光标变为右上倾斜的箭头形状/
                direction = MouseDirection.DecliningLeftbottom;
                changeornot = true;
            }
            //右上
            else if (e.Location.X >= this.Width - 5 && e.Location.Y <= 5)
            {
                this.Cursor = Cursors.SizeNESW;   //光标变为右上倾斜的箭头形状/
                direction = MouseDirection.DecliningRighttop;
                changeornot = true;
            }
            //右下
            else if (e.Location.X >= this.Width - 5 && e.Location.Y > this.Height - 5)
            {
                this.Cursor = Cursors.SizeNWSE;   //光标变为右下倾斜的箭头形状\
                direction = MouseDirection.DecliningRightbottom;
                changeornot = true;
            }
            //左
            else if (e.Location.X <= 5)
            {
                this.Cursor = Cursors.SizeWE;  //将光标变为水平的箭头形状
                direction = MouseDirection.HerizontalLeft;
                changeornot = true;
            }
            //右
            else if (e.Location.X >= this.Width - 5)
            {
                this.Cursor = Cursors.SizeWE;  //将光标变为水平的箭头形状
                direction = MouseDirection.HerizontalRight;
                changeornot = true;
            }
            //上
            else if (e.Location.Y <= 1)
            {
                this.Cursor = Cursors.SizeNS;  //将光标变为竖直的箭头形状
                direction = MouseDirection.VerticalTop;
                changeornot = true;
            }
            //下
            else if (e.Location.Y >= this.Height - 5)
            {
                this.Cursor = Cursors.SizeNS;   //将光标变为竖直的箭头形状
                direction = MouseDirection.VerticalBottom;
                changeornot = true;
            }
            //否则,其他窗体区域,鼠标星座均为单向箭头(默认)
            else
            {
                this.Cursor = Cursors.Arrow;
                direction = MouseDirection.None;
                changeornot = false;
            }
        }
        private void ResizeWindow()
        {
            //MousePosition的参考点是屏幕的左上角,表示鼠标当前相对于屏幕左上角的坐标。
            //this.left和this.top的参考点也是屏幕,属性MousePosition是程序的重点
            if (direction == MouseDirection.DecliningLefttop)  //左上
            {
                this.Cursor = Cursors.SizeNWSE;    //光标为右下倾斜的箭头形状\
                this.Width = this.Right - MousePosition.X;   //改变窗体宽和高
                this.Height = this.Bottom - MousePosition.Y;
                this.Location = new Point(MousePosition.X, MousePosition.Y);  //改变窗体位置
            }
            else if (direction == MouseDirection.DecliningLeftbottom)  //左下
            {
                this.Cursor = Cursors.SizeNESW;
                this.Location = new Point(MousePosition.X, this.Top);
                this.Width = this.Right - MousePosition.X;
                this.Height = MousePosition.Y - this.Top;
            }
            else if (direction == MouseDirection.DecliningRighttop)   //右上
            {
                this.Cursor = Cursors.SizeNESW;
                this.Location = new Point(this.Left, MousePosition.Y);
                this.Width = MousePosition.X - this.Left;
                this.Height = this.Bottom - MousePosition.Y;
            }
            else if (direction == MouseDirection.DecliningRightbottom)  //右下
            {
                this.Cursor = Cursors.SizeNWSE;  //不改变窗体位置
                this.Width = MousePosition.X - this.Left;
                this.Height = MousePosition.Y - this.Top;
            }
            else if (direction == MouseDirection.HerizontalLeft)   //左
            {
                this.Cursor = Cursors.SizeWE;
                this.Location = new Point(MousePosition.X, this.Top);
                this.Width = this.Right - MousePosition.X;
            }
            else if (direction == MouseDirection.HerizontalRight)   //右
            {
                this.Cursor = Cursors.SizeWE;
                this.Width = MousePosition.X - this.Left;
            }
            else if (direction == MouseDirection.VerticalTop)   //上
            {
                this.Cursor = Cursors.SizeNS;
                this.Location = new Point(this.Left, MousePosition.Y);
                this.Height = this.Bottom - MousePosition.Y;
            }
            else if (direction == MouseDirection.VerticalBottom)   //下
            {
                this.Cursor = Cursors.SizeNS;
                this.Height = MousePosition.Y - this.Top;
            }
            //即使鼠标按下,但是不在窗口右和下边缘,那么也不能改变窗口大小
            else
            {
                this.Cursor = Cursors.Arrow;
            }
        }

 实际效果是能缩放,但是闪烁卡顿严重

参考文章:(1条消息) winform无边框在panel上拖动窗口位置,改变窗口大小_傲娇萌主的博客-CSDN博客_winform窗体可拖动变大小属性https://blog.csdn.net/qq_37341685/article/details/122220117


winform 设置无边框后的窗口大小调整 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/505185678

 方法二

直接上代码

//窗体缩放
        const int Guying_HTLEFT = 10;
        const int Guying_HTRIGHT = 11;
        const int Guying_HTTOP = 12;
        const int Guying_HTTOPLEFT = 13;
        const int Guying_HTTOPRIGHT = 14;
        const int Guying_HTBOTTOM = 15;
        const int Guying_HTBOTTOMLEFT = 0x10;
        const int Guying_HTBOTTOMRIGHT = 17;
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case 0x0084:
                    base.WndProc(ref m);
                    Point vPoint = new Point((int)m.LParam & 0xFFFF,
                        (int)m.LParam >> 16 & 0xFFFF);
                    vPoint = PointToClient(vPoint);
                    if (vPoint.X <= 5)
                        if (vPoint.Y <= 5)
                            m.Result = (IntPtr)Guying_HTTOPLEFT;
                        else if (vPoint.Y >= ClientSize.Height - 5)
                            m.Result = (IntPtr)Guying_HTBOTTOMLEFT;
                        else m.Result = (IntPtr)Guying_HTLEFT;
                    else if (vPoint.X >= ClientSize.Width - 5)
                        if (vPoint.Y <= 5)
                            m.Result = (IntPtr)Guying_HTTOPRIGHT;
                        else if (vPoint.Y >= ClientSize.Height - 5)
                            m.Result = (IntPtr)Guying_HTBOTTOMRIGHT;
                        else m.Result = (IntPtr)Guying_HTRIGHT;
                    else if (vPoint.Y <= 2)
                        m.Result = (IntPtr)Guying_HTTOP;
                    else if (vPoint.Y >= ClientSize.Height - 5)
                        m.Result = (IntPtr)Guying_HTBOTTOM;
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }

其方法与一类似,通过判断鼠标位置实施不同缩放,但是缩放的方法不是自己写的,这个方法缩放会流畅很多,和有边框的窗口差不多,但是因为FormBorderStyle设置成None,没有边框,缩放过程中部分控件又会轻微闪烁(有防闪烁代码的基础上)。一般使用边框附近5个像素,因为我的菜单栏距顶部只有两个像素,设置成5会失效,所以上方缩放我设置的2 (else if (vPoint.Y <= 2))

Winform自定义无边框窗体 - landptf - 博客园 (cnblogs.com)https://www.cnblogs.com/landptf/p/5068523.html

方法三

设置FormBorderStyle设置成Sizable,但是窗口样式中controlbox设置成false,且Form1的Text设置为空,效果如下。这样能够拥有边框的缩放功能,没有一点卡顿和闪烁,但是顶端有一个白色窄边,其他三边没有,但是最大化窗口时其他三边会有缝隙,也就是说边框虽然不显示,但还是在。

 

 方法四

此方法可以说是完美解决问题,经过方法二和方法三的折腾,我发现可以将窗口的FormBorderStyle设置成FixedSingle(3D也行,看个人审美),然后controlbox设置成false,且Text设置为空,此时窗口拥有系统的边框,不占位置,能够使用系统自带窗体的功能(比如点击任务栏图标可以最小化和还原),且显示正常(最大化最小化不会有显示缺陷),但是窗口不能缩放,再将方法二的代码加上,就完美解决了

适合新手使用

完整代码(需要按照方法四设置窗体)

#region 窗体设置
        private void buttonClose_Click(object sender, EventArgs e)
        {
            this.Close();  //关闭窗口
        }

        private void buttonMax_Click(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Maximized)   //如果处于最大化,则还原
            {
                this.WindowState = FormWindowState.Normal;
                Image backImage = Resources.最大化;
                buttonMax.BackgroundImage = backImage;
            }
            else
            {
                this.WindowState = FormWindowState.Maximized;   //如果处于普通状态,则最大化
                Image backImage = Resources.还原;
                buttonMax.BackgroundImage = backImage;
            }
        }

        private void buttonMin_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;  //最小化
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            if (this.WindowState != FormWindowState.Maximized)
                buttonMax.BackgroundImage = Resources.最大化;
        }

        protected override CreateParams CreateParams   //防止改变窗口大小时控件闪烁
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
                return cp;
            }
        }

        //窗体移动
        [DllImport("user32.dll")]
        public static extern bool ReleaseCapture();

        [DllImport("user32.dll")]
        public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

        private void menuStrip1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Clicks == 1)
            {
                //窗体移动
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture(); //释放鼠标捕捉
                    //发送左键点击的消息至该窗体(标题栏)
                    SendMessage(this.Handle, 0xA1, 0x02, 0);
                }
            }
        }

        //窗体缩放
        const int Guying_HTLEFT = 10;
        const int Guying_HTRIGHT = 11;
        const int Guying_HTTOP = 12;
        const int Guying_HTTOPLEFT = 13;
        const int Guying_HTTOPRIGHT = 14;
        const int Guying_HTBOTTOM = 15;
        const int Guying_HTBOTTOMLEFT = 0x10;
        const int Guying_HTBOTTOMRIGHT = 17;
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case 0x0084:
                    base.WndProc(ref m);
                    Point vPoint = new Point((int)m.LParam & 0xFFFF,
                        (int)m.LParam >> 16 & 0xFFFF);
                    vPoint = PointToClient(vPoint);
                    if (this.WindowState != FormWindowState.Normal)
                        break;  //源代码在窗体最大化状态下,鼠标移到窗口边缘也会出现拖动标识,添加这句代码可以避免
                    if (vPoint.X <= 5)
                        if (vPoint.Y <= 5)
                            m.Result = (IntPtr)Guying_HTTOPLEFT;
                        else if (vPoint.Y >= ClientSize.Height - 5)
                            m.Result = (IntPtr)Guying_HTBOTTOMLEFT;
                        else m.Result = (IntPtr)Guying_HTLEFT;
                    else if (vPoint.X >= ClientSize.Width - 5)
                        if (vPoint.Y <= 5)
                            m.Result = (IntPtr)Guying_HTTOPRIGHT;
                        else if (vPoint.Y >= ClientSize.Height - 5)
                            m.Result = (IntPtr)Guying_HTBOTTOMRIGHT;
                        else m.Result = (IntPtr)Guying_HTRIGHT;
                    else if (vPoint.Y <= 2)
                        m.Result = (IntPtr)Guying_HTTOP;
                    else if (vPoint.Y >= ClientSize.Height - 5)
                        m.Result = (IntPtr)Guying_HTBOTTOM;
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
        #endregion

——————更新一下(2023.03):

更新一下:
1.实践发现protected override CreateParams CreateParams这段防闪烁的代码实际没有效果,可以去掉,有fixed边框后就几乎没有闪烁了
2. Form1_Load事件中添加的this.MaximizedBounds = Screen.PrimaryScreen.WorkingArea这段代码会导致窗口在扩展屏上最大化时显示错误(我遇到的情况是最大化未占满整个屏幕),可以去掉,然后在buttonMax_Click事件中添加如下代码

this.MaximumSize = Screen.FromHandle(this.Handle).WorkingArea.Size。

直接将this.MaximizedBounds = Screen.PrimaryScreen.WorkingArea搬运到buttonMax_Click事件中是不行的,可能是因为窗体在加载的时候获取了屏幕大小,然后就将其值固定不变了,只改变显示区域不会改变最大化的尺寸,因此需要在buttonMax_Click事件中获取当前屏幕工作区的尺寸并赋值给窗体最大化尺寸。
3.要比较容易的缩放窗体,需要窗体预留足够宽度的margin(或者是padding,具体我忘了,反正就是窗体边缘的几个像素要留出来)因为系统识别的窗体边缘的像素,如果用其他控件遮挡了窗体的边缘,鼠标移到窗体边缘时就检测不到,结果就是鼠标滑动很久才能勉强找到可以缩放的点,轻微移动缩放标识就会消失。

 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值