Winform中實現拖動 Windows 邊緣來調整其大小

firespeed發表於2024-09-30

Winform中實現無邊框窗體只需要設定一個屬性FormBorderStyle = FormBorderStyle.None;即可,或者在設計器中直接設定。無邊框表單的結果是丟失了標題欄和控制框(最小化、最大值和關閉按鈕)。如果沒有標題欄,則無法拖動和移動視窗。如果沒有邊框,則無法拖動 Windows 邊緣來調整其大小。移動表單非常簡單,但邊緣拖動調整大小有點棘手。

下面透過監聽滑鼠事件和調整游標形狀,實現了一個使用者可以透過拖拽窗體邊緣來調整窗體大小的功能。同時,它還確保了當滑鼠離開窗體時,游標形狀會恢復為預設的箭頭形狀。

先上效果圖

Winform中實現拖動 Windows 邊緣來調整其大小

具體來說,它允許使用者透過拖動滑鼠來改變窗體的大小。以下是程式碼的工作原理概述:

1.初始化:

在建構函式 Form1() 中,為 Form1 例項註冊了兩個事件處理器:MouseMove 用於處理滑鼠移動時的行為,MouseLeave 用於處理滑鼠離開窗體時的行為。

2.滑鼠移動處理 (Main_MouseMove):

如果滑鼠接近右下角,則顯示雙向對角線箭頭。

如果滑鼠接近右側,則顯示水平箭頭。

如果滑鼠接近底部,則顯示垂直箭頭。

Cursors.SizeNWSE: 允許同時改變窗體的寬度和高度。

Cursors.SizeWE: 只允許改變窗體的寬度。

Cursors.SizeNS: 只允許改變窗體的高度。

當滑鼠在窗體內移動時,會觸發 MouseMove 事件。

如果滑鼠左鍵被按下,並且游標形狀是某種尺寸調整箭頭(Cursors.SizeNWSE, Cursors.SizeWE, 或 Cursors.SizeNS),則根據當前游標型別調整窗體的寬度和/或高度。

根據滑鼠位置與窗體邊緣的距離(5畫素內),動態地改變游標形狀以提示使用者可以進行何種型別的尺寸調整:

如果滑鼠不在這些區域,則恢復預設的箭頭游標。

3.滑鼠離開窗體處理 (Main_Leave):

當滑鼠指標離開窗體範圍時,將游標恢復到預設的箭頭狀態。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp7
{
    public partial class Form1 : Form
    {
        private int borderSize = 2;
        [DllImport("user32.DLL", EntryPoint = "ReleaseCapture")]
        private extern static void ReleaseCapture();
        [DllImport("user32.DLL", EntryPoint = "SendMessage")]
        private static extern void SendMessage(System.IntPtr hWnd, int wMsg, int wParam, int lParam);

        private void panelTitleBar_MouseDown(object sender, MouseEventArgs e)  //panel的MouseDown事件
        {
            ReleaseCapture();
            SendMessage(this.Handle, 0x112, 0xf012, 0);
        }

        public Form1()
        {
            InitializeComponent();
            this.Padding = new Padding(borderSize);
            this.BackColor = Color.FromArgb(245, 245, 255); //98, 102, 244);// 
            this.ControlBox = false;
            this.Text = string.Empty;
            this.FormBorderStyle = FormBorderStyle.None;
            //this.WindowState = FormWindowState.Maximized;
            this.MaximizedBounds = Screen.FromHandle(this.Handle).WorkingArea;           
            MouseMove += Main_MouseMove;
            MouseLeave += Main_Leave; // 有控制元件在邊緣時,處理一下更好一些
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            this.panelMenu.Width = 180;
        }

        #region 退出按鈕
        private void appExit(object sender, EventArgs e)
        {
            //退出按鈕
            Application.Exit();
        }
        #endregion
        #region 最大化按鈕
        private void butMax_Click(object sender, EventArgs e)
        {   
            //最大化按鈕
            if (this.WindowState == FormWindowState.Normal)
            { 
                this.WindowState = FormWindowState.Maximized;
            }
            else
            {
                this.WindowState = FormWindowState.Normal;
            }
        }
        #endregion
        #region 最小化按鈕
        private void butMin_Click(object sender, EventArgs e)
        {
            //最小化按鈕
            this.WindowState = FormWindowState.Minimized;
        }
        #endregion
        #region 側面選單欄摺疊與展開
        //private void CollapseMenu()
        //{
        //    if (this.panel1.Width > 50)
        //    {
        //        this.panel1.Width = 50;
        //        this.pictureBox1.Visible = false;
        //        //this.button7.Dock = DockStyle.Fill;
        //        foreach (Button control in panel1.Controls.OfType<Button>())
        //        {
        //            control.Text = "";
        //            control.ImageAlign = ContentAlignment.MiddleCenter;
        //            control.Padding = new Padding(0);

        //        }
        //    }
        //    else
        //    {
        //        this.panel1.Width = 180;
        //        this.pictureBox1.Visible = true;
        //        //this.button7.Dock = DockStyle.Right;
        //        foreach (Button control in panel1.Controls.OfType<Button>())
        //        {
        //            control.Text = control.Tag.ToString();
        //            control.ImageAlign = ContentAlignment.MiddleLeft;
        //            control.Padding = new Padding(45, 0, 0, 0);

        //        }
        //    }


        //}
        private void CollapseMenu()
        {
            //這段C#程式碼實現了一個CollapseMenu()方法,用於控制側邊欄選單的摺疊和展開
            //首先判斷當前側邊欄的寬度是否大於200,如果是,則將寬度設定為100,隱藏圖示,
            //將選單按鈕的Dock屬性設定為Top,
            //設定皮膚的內邊距為上邊10畫素,下左右為0,
            //並遍歷所有的選單按鈕,將它們的文字設定為空,
            //影像對齊方式設定為居中,內邊距設定為0。
            //如果當前側邊欄的寬度小於等於200,
            //就將寬度設定為260,顯示圖示,將選單按鈕的Dock屬性設定為None,
            //設定皮膚的內邊距為上下左右都為0,
            //並遍歷所有的選單按鈕,將它們的文字設定為按鈕的Tag屬性值前面加兩個空格,
            //影像對齊方式設定為左對齊,文字對齊方式設定為右對齊,
            //按鈕文字和影像的相對位置設定為文字在影像左側,
            //內邊距設定為上0畫素,左邊距為10畫素,下右邊距為0。
            if (this.panelMenu.Width > 100)
            {
                panelMenu.Width = 50;
                iconPictureBox1.Visible = false;
                //btnMenu.Dock = DockStyle.Top;
                //this.panelMenu.Padding = new Padding(0, 10, 0, 0);
                foreach (var mybutton in this.panelMenu.Controls.OfType<Button>()) //篩選出panelMenu內所有屬於Button型別的子控制元件
                {
                    mybutton.Text = "";
                    mybutton.ImageAlign = ContentAlignment.MiddleCenter; //將其影像對齊方式設定為居中對齊
                    mybutton.Padding = new Padding(0); //內邊距設為0
                }
            }
            else
            {
                panelMenu.Width = 180;
                iconPictureBox1.Visible = true;
                //btnMenu.Dock = DockStyle.None;
                //this.panelMenu.Padding = new Padding(0, 0, 0, 0);
                foreach (var mybutton in this.panelMenu.Controls.OfType<Button>()) //篩選出panelMenu內所有屬於Button型別的子控制元件
                {
                    mybutton.Text =mybutton.Tag.ToString();     //將控制元件的Tag賦值給Text     "  " + 
                    mybutton.ImageAlign = ContentAlignment.MiddleLeft;
                    mybutton.TextAlign = ContentAlignment.MiddleRight;
                    mybutton.TextImageRelation = TextImageRelation.ImageBeforeText;  //Text和Image位置關係
                    mybutton.Padding = new Padding(30, 0, 0, 0);
                }
            }
        }
        //側面選單欄摺疊與展開
        private void btnMenu_Click(object sender, EventArgs e)
        {
            CollapseMenu();
        }
        #endregion
        #region 保持整體佈局不變
        private void AdjustForm()
        {
            switch (this.WindowState)
            {
                case FormWindowState.Normal:
                    if (this.Padding.Top != borderSize)
                        this.Padding = new Padding(borderSize);
                    break;
                case FormWindowState.Minimized:
                    this.Padding = new Padding(8, 8, 8, 8);
                    break;
                case FormWindowState.Maximized:
                    break;
                default:
                    break;
            }
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            AdjustForm();
        }
        #endregion
        #region 控制無邊框拉伸
        private void Main_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)//左鍵按下移動,拖拽調整大小
            {
                // MousePosition的參考點是螢幕的左上角,表示滑鼠當前相對於螢幕左上角的座標。this.Left和this.Top的參考點也是螢幕
                if (Cursor == Cursors.SizeNWSE) // 傾斜拖拽
                {
                    // 改變窗體寬和高的程式碼,其寬高為滑鼠螢幕位置減去窗體的Left,Top距離
                    this.Width = MousePosition.X - this.Left;
                    this.Height = MousePosition.Y - this.Top;
                }
                else if (Cursor == Cursors.SizeWE) // 水平拖拽
                {
                    Width = MousePosition.X - this.Left;
                }
                else if (Cursor == Cursors.SizeNS) // 垂直拖拽
                {
                    Height = MousePosition.Y - this.Top;
                }
            }
            //滑鼠移動過程中,座標時刻在改變
            //當滑鼠移動時橫座標距離窗體右邊緣5畫素以內且縱座標距離下邊緣也在5畫素以內時,要將游標變為傾斜的箭頭形狀
            if (e.Location.X >= this.Width - 5 && e.Location.Y > this.Height - 5)
            {
                this.Cursor = Cursors.SizeNWSE; // 右下角 雙向對角線游標
            }
            //當滑鼠移動時橫座標距離窗體右邊緣5畫素以內時,要將游標變為雙向水平箭頭形狀
            else if (e.Location.X >= this.Width - 5)
            {
                this.Cursor = Cursors.SizeWE; // 雙向水平游標
            }
            //當滑鼠移動時縱座標距離窗體下邊緣5畫素以內時,要將游標變為垂直水平箭頭形狀
            else if (e.Location.Y >= this.Height - 5)
            {
                this.Cursor = Cursors.SizeNS; // 雙向垂直游標
            }
            //否則,以外的窗體區域,滑鼠星座均為單向箭頭(預設)
            else this.Cursor = Cursors.Arrow;
        }

        private void Main_Leave(object sender, EventArgs e)
        {
            Cursor = Cursors.Arrow;// 移出窗體變為正常
        }
        #endregion

    }
}

透過這種方式,當使用者將滑鼠懸停在窗體邊緣並拖動時,窗體會相應地改變其大小,提供了直觀的使用者體驗。這段程式碼沒有涉及到實際的拖動整個窗體的位置,僅關注於改變窗體的尺寸。如果需要實現窗體位置的拖動,還需要額外的邏輯來處理標題欄或其他指定區域的拖動行為。

相關文章