winforms 边框设置为None的可调整大小的窗体,由Dock属性设置为Top的top“Panel”对象执行

gcmastyq  于 2023-04-07  发布在  其他
关注(0)|答案(3)|浏览(157)

我想用Python为我的项目做一个简单的C#(VS)弹出窗口,由于美观,我想使用VS。
(请记住我是C#新手)

我做了什么

所以我创建了这个项目,将主窗体的Border属性设置为None,并添加了我称为titleBarPanel的Panel对象。我已经启用了通过窗体的底部和侧面调整窗体大小,并启用了通过titleBarPanel拖动窗体。

我想要的

我想简单地使窗体能够通过顶部面板对象调整大小。

我尝试了什么

我已经搜索过stackoverflow asd以及在youtube上,但我找不到任何东西。我也试着问ChatGPT,但他不是那么聪明,在这种情况下,无法解决我的问题。

代码由于代码量小,我将给予整个代码。

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

namespace UI_Variables_Prompt
{
    public partial class Form1 : Form
    {
        private const int WM_NCHITTEST = 0x84;
        private const int HTCLIENT = 0x1;
        private const int HTCAPTION = 0x2;
        private const int HTBOTTOMLEFT = 0x10;
        private const int HTBOTTOMRIGHT = 0x11;
        private const int HTLEFT = 0xa;
        private const int HTRIGHT = 0xb;
        private const int HTBOTTOM = 0xf;
        private const int HTTOP = 0xc;
        private const int HTTOPLEFT = 0xd;
        private const int HTTOPRIGHT = 0xe;

        private bool isDragging = false;
        private Point dragStart;
        private bool isResizing = false;
        private int resizeDir = 0;

        public Form1()
        {
            InitializeComponent();
            titleBarPanel.MouseDown += new MouseEventHandler(titleBarPanel_MouseDown);
            titleBarPanel.MouseMove += new MouseEventHandler(titleBarPanel_MouseMove);
            titleBarPanel.MouseUp += new MouseEventHandler(titleBarPanel_MouseUp);
            this.MouseDown += new MouseEventHandler(Form1_MouseDown);
            this.MouseMove += new MouseEventHandler(Form1_MouseMove);
            this.MouseUp += new MouseEventHandler(Form1_MouseUp);

        }

        protected override void WndProc(ref Message m)
        {
            const int WM_NCHITTEST = 0x0084;
            const int HTCLIENT = 1;
            const int HTCAPTION = 2;
            const int HTTOP = 12;
            const int HTLEFT = 10;
            const int HTRIGHT = 11;
            const int HTBOTTOM = 15;
            const int HTTOPLEFT = 13;
            const int HTTOPRIGHT = 14;
            const int HTBOTTOMLEFT = 16;
            const int HTBOTTOMRIGHT = 17;

            if (m.Msg == WM_NCHITTEST)
            {
                base.WndProc(ref m);
                if (m.Result == (IntPtr)HTCLIENT)
                {
                    Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
                    pos = this.PointToClient(pos);
                    if (pos.Y < 10)
                    {
                        if (pos.X < 10)
                            m.Result = (IntPtr)HTTOPLEFT;
                        else if (pos.X > this.ClientSize.Width - 10)
                            m.Result = (IntPtr)HTTOPRIGHT;
                        else
                            m.Result = (IntPtr)HTTOP;
                    }
                    else if (pos.Y > this.ClientSize.Height - 10)
                    {
                        if (pos.X < 10)
                            m.Result = (IntPtr)HTBOTTOMLEFT;
                        else if (pos.X > this.ClientSize.Width - 10)
                            m.Result = (IntPtr)HTBOTTOMRIGHT;
                        else
                            m.Result = (IntPtr)HTBOTTOM;
                    }
                    else if (pos.X < 10)
                        m.Result = (IntPtr)HTLEFT;
                    else if (pos.X > this.ClientSize.Width - 10)
                        m.Result = (IntPtr)HTRIGHT;
                }
                else if (m.Result == (IntPtr)HTCAPTION)
                {
                    m.Result = (IntPtr)HTCLIENT;
                }
            }
            else
            {
                base.WndProc(ref m);
            }
        }

        private int ResizeDirection(Point cursorPos, Rectangle formBounds)
        {
            const int HTLEFT = 10;
            const int HTRIGHT = 11;
            const int HTTOP = 12;
            const int HTTOPLEFT = 13;
            const int HTTOPRIGHT = 14;
            const int HTBOTTOM = 15;
            const int HTBOTTOMLEFT = 16;
            const int HTBOTTOMRIGHT = 17;

            if (cursorPos.Y < formBounds.Top + 10)
            {
                if (cursorPos.X < formBounds.Left + 10)
                    return HTTOPLEFT;
                else if (cursorPos.X > formBounds.Right - 10)
                    return HTTOPRIGHT;
                else
                    return HTTOP;
            }
            else if (cursorPos.Y > formBounds.Bottom - 10)
            {
                if (cursorPos.X < formBounds.Left + 10)
                    return HTBOTTOMLEFT;
                else if (cursorPos.X > formBounds.Right - 10)
                    return HTBOTTOMRIGHT;
                else
                    return HTBOTTOM;
            }
            else if (cursorPos.X < formBounds.Left + 10)
                return HTLEFT;
            else if (cursorPos.X > formBounds.Right - 10)
                return HTRIGHT;

            return 0;
        }

        private void titleBarPanel_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                isDragging = true;
                dragStart = new Point(e.X, e.Y);
            }
        }

        private void titleBarPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                Point p = PointToScreen(e.Location);
                Location = new Point(p.X - this.dragStart.X, p.Y - this.dragStart.Y);
            }
            else if (isResizing)
            {
                switch (resizeDir)
                {
                    case HTTOP:
                        this.Height = Math.Max(this.MinimumSize.Height, this.dragStart.Y - e.Y + this.Height);
                        break;
                    case HTTOPLEFT:
                        this.Width = Math.Max(this.MinimumSize.Width, this.dragStart.X - e.X + this.Width);
                        this.Height = Math.Max(this.MinimumSize.Height, this.dragStart.Y - e.Y + this.Height);
                        this.Left = Math.Min(this.Left + e.X - this.dragStart.X, this.Left + this.Width - this.MinimumSize.Width);
                        break;
                    case HTTOPRIGHT:
                        this.Width = Math.Max(this.MinimumSize.Width, e.X - this.dragStart.X + this.Width);
                        this.Height = Math.Max(this.MinimumSize.Height, this.dragStart.Y - e.Y + this.Height);
                        break;
                    case HTLEFT:
                        this.Width = Math.Max(this.MinimumSize.Width, this.dragStart.X - e.X + this.Width);
                        this.Left = Math.Min(this.Left + e.X - this.dragStart.X, this.Left + this.Width - this.MinimumSize.Width);
                        break;
                    case HTRIGHT:
                        this.Width = Math.Max(this.MinimumSize.Width, e.X - this.dragStart.X + this.Width);
                        break;
                    case HTBOTTOM:
                        this.Height = Math.Max(this.MinimumSize.Height, e.Y - this.dragStart.Y + this.Height);
                        break;
                    case HTBOTTOMLEFT:
                        this.Width = Math.Max(this.MinimumSize.Width, this.dragStart.X - e.X + this.Width);
                        this.Height = Math.Max(this.MinimumSize.Height, e.Y - this.dragStart.Y + this.Height);
                        this.Left = Math.Min(this.Left + e.X - this.dragStart.X, this.Left + this.Width - this.MinimumSize.Width);
                        break;
                    case HTBOTTOMRIGHT:
                        this.Width = Math.Max(this.MinimumSize.Width, e.X - this.dragStart.X + this.Width);
                        this.Height = Math.Max(this.MinimumSize.Height, e.Y - this.dragStart.Y + this.Height);
                        break;
                }
            }
        }

        private void titleBarPanel_MouseUp(object sender, MouseEventArgs e)
        {
            isDragging = false;
            isResizing = false;
            resizeDir = 0;
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            isResizing = false;
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && !isDragging)
            {
                this.dragStart = new Point(e.X, e.Y);
                isResizing = true;
                resizeDir = ResizeDirection(e.Location, this.Bounds);
            }
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (isResizing)
            {
                int cursorType = 0;
                switch (resizeDir)
                {
                    case HTTOP:
                    case HTBOTTOM:
                        cursorType = Cursors.SizeNS.Handle.ToInt32();
                        break;
                    case HTLEFT:
                    case HTRIGHT:
                        cursorType = Cursors.SizeWE.Handle.ToInt32();
                        break;
                    case HTTOPLEFT:
                    case HTBOTTOMRIGHT:
                        cursorType = Cursors.SizeNWSE.Handle.ToInt32();
                        break;
                    case HTTOPRIGHT:
                    case HTBOTTOMLEFT:
                        cursorType = Cursors.SizeNESW.Handle.ToInt32();
                        break;
                }

                Cursor.Current = new Cursor(new IntPtr(cursorType));
            }
            else
            {
                Cursor.Current = Cursors.Default;
            }
        }

    }
}

非常感谢你提前!

b4lqfgs4

b4lqfgs41#

标题栏Panel是这里的问题,它接收鼠标输入,而不是From。因此,窗体的移动和调整大小例程在面板区域中不起作用,这使得您复制几乎相同的代码来单独处理面板的鼠标输入。
好吧,你不需要这样做,一个简单的替代方法是子类化面板的本机窗口,使其对鼠标输入透明,并将它们传递给父控件进行处理。对面板的非点击子控件(如果有的话)也这样做(即显示图标的PictureBox和显示标题/标题的Label...)。
要子类化,将Control传递给NativeWindow的派生类,分配其句柄并重写WndProc方法以捕获WM_NCHITTEST消息,并将HTTRANSPARENT指针分配给Result属性。
这里的例子是一个无边框的Form,它包含一个停靠在顶部的PanelPanel本身托管一个显示图像的PictureBox和一个显示标题的Label

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Win32;

//Within your namespace...

public partial class BorderlessForm : Form
{        
    const int WM_NCHITTEST = 0x84;
    const int HTCAPTION = 2;
    const int HTLEFT = 10;
    const int HTRIGHT = 11;
    const int HTTOP = 12;
    const int HTTOPLEFT = 13;
    const int HTTOPRIGHT = 14;
    const int HTBOTTOM = 15;
    const int HTBOTTOMLEFT = 16;
    const int HTBOTTOMRIGHT = 17;
    const int gripSize = 10;

    public BorderlessForm()
    {
        InitializeComponent();

        new NcHitTestTransparentControl(pnlTitle);
        new NcHitTestTransparentControl(picImage);
        new NcHitTestTransparentControl(lblTitle);

        SetMaxSize();

        SystemEvents.DisplaySettingsChanged += (s, e) => SetMaxSize();            
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.Style |= 0x20000; // WS_MINIMIZEBOX;
            return cp;
        }
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_NCHITTEST && WindowState != FormWindowState.Maximized)
        {
            var pt = PointToClient(new Point(m.LParam.ToInt32()));
            var cs = ClientSize;

            if (pt.X >= cs.Width - gripSize && pt.Y >= cs.Height - gripSize)
            {
                m.Result = (IntPtr)HTBOTTOMRIGHT;
                return;
            }
            if (pt.X <= gripSize && pt.Y >= cs.Height - gripSize)
            {
                m.Result = (IntPtr)HTBOTTOMLEFT;
                return;
            }
            if (pt.X <= gripSize && pt.Y <= gripSize)
            {
                m.Result = (IntPtr)HTTOPLEFT;
                return;
            }
            if (pt.X >= cs.Width - gripSize && pt.Y <= gripSize)
            {
                m.Result = (IntPtr)HTTOPRIGHT;
                return;
            }
            if (pt.Y <= gripSize)
            {
                m.Result = (IntPtr)HTTOP;
                return;
            }
            if (pt.Y >= cs.Height - gripSize)
            {
                m.Result = (IntPtr)HTBOTTOM;
                return;
            }
            if (pt.X <= gripSize)
            {
                m.Result = (IntPtr)HTLEFT;
                return;
            }
            if (pt.X >= cs.Width - gripSize)
            {
                m.Result = (IntPtr)HTRIGHT;
                return;
            }

            var r = new Rectangle(gripSize, gripSize, 
                Width - gripSize * 2, pnlTitle.Height);

            // Remove this condition if you want to move the Form
            // from any point.
            if (r.Contains(pt))
            {
                m.Result = (IntPtr)HTCAPTION;
                return;
            }
        }

        base.WndProc(ref m);
    }

    private void SetMaxSize()
    {
        MaximumSize = Screen.FromControl(this).WorkingArea.Size;
    }

    private class NcHitTestTransparentControl : NativeWindow
    {
        const int WM_NCHITTEST = 0x84;
        const int HTTRANSPARENT = -1;

        internal NcHitTestTransparentControl(Control ctrl)
        {
            if (ctrl.Handle != IntPtr.Zero) AssignHandle(ctrl.Handle);
            ctrl.HandleCreated += (s, e) => AssignHandle(ctrl.Handle);
            ctrl.HandleDestroyed += (s, e) => ReleaseHandle();
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_NCHITTEST)
                m.Result = new IntPtr(HTTRANSPARENT);
            else
                base.WndProc(ref m);
        }
    }       
}

备注

  • 要在单击无边框Form的任务栏图标时最小化和还原该Form,必须重写CreateParams属性以添加WS_MINIMIZEBOX样式。
  • 调用SetMaxSize方法来设置窗体的MaximumSize是为了在最大化窗体时不覆盖任务栏区域。
  • 您不应该忽略设计器的警告消息,也不应该缩放开发环境的显示。100%显示用于设计窗体和布局控件。请确保您的应用程序是DpiAware
  • 通过设置相应的属性(LocationSize)来避免重新定位和调整Form的大小。这样做会在您移动或调整Form的大小时发送连续的重绘请求。最好使用本机方法,如图所示,或者通过p/invoke相关函数来“吞下”一些重绘调用者。
  • 当你已经将局部域/常量声明为类域/常量时,你不需要重新声明它们。
tcomlyy6

tcomlyy62#

我测试了你的代码,应该是拖动和调整大小状态处理得不好,拖动太滞后了。
我重新写了代码,大家可以参考一下,之前需要将面板的dock属性调整为Top,拖动时请慢一点,否则无法分辨是移动还是调整大小,如果想修复这个bug,可以在panel1_MouseMove中将所有-10改为-20或更大,但这样做不会很美观。

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

namespace PanelResize1
{
    public partial class Form1 : Form
    {
        private bool isMouseDown = false;  // Indicates whether the mouse is currently pressed, the initial value is No

        MouseDirection direction = MouseDirection.None; //Indicates the direction of dragging, starting with None, indicating no dragging

        private Point mPoint; //mouse coordinates

        private bool state = true; // Whether to change the size of the form, true is not, false is yes

        public enum MouseDirection  //Define an enumeration to indicate the dragging direction
        {
            Herizontal,  
            Vertical,
            Declining,
            None
        }
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void panel1_MouseDown(object sender, MouseEventArgs e)
        {
            mPoint = new Point(e.X, e.Y);
            isMouseDown = true;
        }

        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            //When the mouse is moving, the coordinates are changing all the time
            //When the mouse moves, the abscissa is within 10 pixels from the right edge of the window and the ordinate is within 10 pixels from the bottom edge, the cursor should be changed into an oblique arrow shape, and the dragging direction should be set to MouseDirection.Declining
            if (e.Location.X >= this.Width - 10 && e.Location.Y > panel1.Height - 10)
            {
                this.Cursor = Cursors.SizeNWSE;
                direction = MouseDirection.Declining;
                state = false;
            }
            else if (e.Location.X >= this.Width - 10)
            //When the abscissa is within 10 pixels from the right edge of the window when the mouse moves, the cursor should be changed into an oblique arrow shape, and the dragging direction should be set to MouseDirection.Herizontal else if (e.Location.X >= this. Width - 10)
            {
                this.Cursor = Cursors.SizeWE;
                direction = MouseDirection.Herizontal;
                state = false;
            }
            else if (e.Location.Y > this.Height - 10)
            //Similarly, when the ordinate is within 10 pixels from the bottom edge of the form when the mouse moves, the cursor should be changed into an oblique arrow shape, and the dragging direction should be set to MouseDirection.Vertical else if (e.Location.Y >= this .Height - 10)
            {
                this.Cursor = Cursors.SizeNS;
                direction = MouseDirection.Vertical;
                state = false;
            }
            else if(e.Location.X >= panel1.Width-10 )
            {
                this.Cursor = Cursors.SizeWE;
                direction = MouseDirection.Herizontal;
                state = false;
            }
            else if ( e.Location.Y > panel1.Height - 10)
            {
                this.Cursor = Cursors.SizeNS;
                direction = MouseDirection.Vertical;
                state = false;
            }
            else
            //Otherwise, the mouse constellations are all one-way arrows in other window areas (default) else
            {
                this.Cursor = Cursors.Arrow;
                state = true;
            }
            // After setting the direction, call the following method to change the size of the window
            if (state)
            {
                if (e.Button == MouseButtons.Left)
                {
                    this.Location = new Point(this.Location.X + e.X - mPoint.X, this.Location.Y + e.Y - mPoint.Y);
                    return;
                }
            }
            else
            {
                ResizeWindow();
            }
        }

        private void panel1_MouseUp(object sender, MouseEventArgs e)
        {
            isMouseDown = false;
            direction = MouseDirection.None;
        }

        private void ResizeWindow()
        {
            if (!isMouseDown)
                return;
            if (direction == MouseDirection.Declining)
            {
                this.Cursor = Cursors.SizeNWSE;
                this.Width = MousePosition.X - this.Left;
                this.Height = MousePosition.Y - panel1.Top;
                panel1.Height = MousePosition.Y - this.Top;
            }
            if (direction == MouseDirection.Herizontal)
            {
                this.Cursor = Cursors.SizeWE;
                this.Width = MousePosition.X - this.Left;
            }
            else if (direction == MouseDirection.Vertical)
            {
                this.Cursor = Cursors.SizeNS;
                this.Height = MousePosition.Y - panel1.Top;
                panel1.Height = MousePosition.Y - this.Top;
            }
            {
                this.Cursor = Cursors.Arrow;
            }
        }
    }
}
bhmjp9jg

bhmjp9jg3#

据我所知,您希望在titleBarPanel上拖动鼠标并调整窗体的大小(而不是正常的 * 移动 * 窗体的行为)。为了实现这一点,请考虑实现IMessageFilter来挂钩鼠标事件,而不管单击的焦点是哪个控件(例如titleBarPanel)。现在您需要做的就是:

  • 确定鼠标位置是否在titleBarPanel矩形中
  • 如果是,捕获鼠标按下SizeLocation
  • 当鼠标移动时,根据鼠标的移动量重新计算主窗体Size
  • 别忘了用鼠标 *
public partial class Form1 : Form, IMessageFilter
{
    private Point _dragStartScreen;
    private Size _dragStartSize;
    private bool _dragResizing = false;

    public Form1()
    {
        InitializeComponent();
        Application.AddMessageFilter(this);
        Disposed += (sender, e) =>Application.RemoveMessageFilter(this);
        MinimumSize = new Size(50, 50); // Make sure it can't disappear!
    }
    const int WM_LBUTTONDOWN = 0x0201; 
    const int WM_MOUSEMOVE = 0x0200;
    const int WM_LBUTTONUP = 0x0202;

    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_LBUTTONDOWN:
                var client = PointToClient(MousePosition);
                if (titleBarPanel.ClientRectangle.Contains(client))
                {
                    _dragResizing = true;
                    _dragStartScreen = MousePosition;
                    _dragStartSize = Size;
                    Capture = true;
                    m.Result = (IntPtr)1;
                    return true;
                }
                break;
            case WM_MOUSEMOVE:
                if (_dragResizing)
                {
                    int
                        deltaX = MousePosition.X - _dragStartScreen.X,
                        deltaY = MousePosition.Y - _dragStartScreen.Y;
                    BeginInvoke(new Action(() =>
                    {
                        Size = new Size(_dragStartSize.Width + deltaX, _dragStartSize.Height + deltaY);
                    }));
                    m.Result = (IntPtr)1;
                    return true;
                }
                break;
            case WM_LBUTTONUP:
                _dragResizing = false;
                Capture = false;
                break;
        }
        return false;
    }
}

上面的代码片段地址:

我想实现的-我想简单地使窗体能够通过顶部面板对象调整大小。

我用来测试这个答案的完整代码是在标题栏上放置调整大小-移动图标。

相关问题