使无边界表格可移动吗?


109

有没有一种方法可以使没有边框(FormBorderStyle设置为“ none”)的窗体在鼠标上单击时就像出现边框一样移动?

Answers:


252

文章在CodeProject细节的技术。基本上可以归结为:

public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool ReleaseCapture();

private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{     
    if (e.Button == MouseButtons.Left)
    {
        ReleaseCapture();
        SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
    }
}

从窗口管理器的角度来看,这基本上与抓取窗口的标题栏完全相同


这根本不适合我。代码运行正常,一切都正确,并且我的窗口仍然坐在那里。有任何想法吗?
dbrree '16

8
@dbrree您可能复制了代码,因为Form1_MouseDown未分配给的实际MouseDown事件,该代码无法正常工作Form1
Remon Ramy

2
如果在要拖动表单的地方有标签或图标(或任何其他前景对象!),也请向这些项目添加mousedown事件。表单事件看不到表单对象。
保罗·尼尔森

1
您需要添加this.MouseDown += ...Main()功能形式
理查德·佩克

1
对我来说就像一个魅力!我不得不将事件处理移至我要代替表格的面板上,但它确实起作用了
贾斯汀(Justin)

54

让我们不要使事情变得比他们需要的困难。我遇到了太多的代码片段,这些代码片段使您可以拖动表单(或其他控件)。而且其中许多都有其自身的缺点/副作用。特别是那些诱使Windows认为表单上的控件是实际表单的控件。

话虽如此,这是我的摘录。我用它所有的时间。我还要指出,您不应使用this.Invalidate();。就像其他人喜欢的那样,因为它在某些情况下会导致表格闪烁。在某些情况下也是如此。刷新。使用this.Update,我没有任何闪烁的问题:

private bool mouseDown;
private Point lastLocation;

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        mouseDown = true;
        lastLocation = e.Location;
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if(mouseDown)
        {
            this.Location = new Point(
                (this.Location.X - lastLocation.X) + e.X, (this.Location.Y - lastLocation.Y) + e.Y);

            this.Update();
        }
    }

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

1
dang winforms ...寻找了将近两个小时的答案。...虽然对C#来说是新手,但我确实感到奇怪!
PrinceOfRavens 2015年

这正是我的想法,我唯一的问题是越野车,直到我读懂你是怎么做的。感谢队友
EasyBB 2015年

这对我来说比覆盖WndProc上方的令人惊叹的小片段更好。请注意,WndProc确实起作用了……它只是阻止了其他操作。谢谢!
毫米

这里可能是最好的选择,因为它可以很好地工作并且不依赖WndProc(可以使用Mono轻松移植到其他平台)。如果Y <10,则可以通过将状态更改为最大化/正常来改进
Sylverdrag

它在mono上不起作用,将.net 4.5.2更改为mono / net 4.5,仍然不起作用。现在尝试寻找解决方案。
罗杰·

35

做同一件事的另一种简单方法。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        // set this.FormBorderStyle to None here if needed
        // if set to none, make sure you have a way to close the form!
    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == WM_NCHITTEST)
            m.Result = (IntPtr)(HT_CAPTION);
    }

    private const int WM_NCHITTEST = 0x84;
    private const int HT_CLIENT = 0x1;
    private const int HT_CAPTION = 0x2;
}

1
任何使您可以通过握住特定工具(例如标签)来移动表格的人。
Thomas W

2
如果双击,该窗体将最大化。还要在此处阅读,以免单击鼠标右键。
塞巴斯蒂

19

使用MouseDown,MouseMove和MouseUp。您可以为此设置变量标志。我有一个示例,但我认为您需要进行修改。

我正在将鼠标动作编码到面板上。单击面板后,表单将随之移动。

//Global variables;
private bool _dragging = false;
private Point _offset;
private Point _start_point=new Point(0,0);


private void panel1_MouseDown(object sender, MouseEventArgs e)
{
   _dragging = true;  // _dragging is your variable flag
   _start_point = new Point(e.X, e.Y);
}

private void panel1_MouseUp(object sender, MouseEventArgs e)
{
   _dragging = false; 
}

private void panel1_MouseMove(object sender, MouseEventArgs e)
{
  if(_dragging)
  {
     Point p = PointToScreen(e.Location);
     Location = new Point(p.X - this._start_point.X,p.Y - this._start_point.Y);     
  }
}

它是。如在其他地方已经说过的那样,这依赖于仍然生成MouseMove事件的表单。作为一个简单的例子,假设您对最上方像素行的Form进行渐变并向上拖动。什么都不会发生,尽管一旦将鼠标移回窗体上,窗体就会跳来跳去。
乔伊

12

仅WPF


没有确切的代码,但是在最近的项目中,我认为我使用了MouseDown事件并将其简单地放入:

frmBorderless.DragMove();

Window.DragMove方法(MSDN)


2
那就是WPF。好的,OP并未完全指定这一点。
乔伊(Joey)

是的,这是我正在做的项目所忘记的。我只是看过Forms,它不可用。抱歉!
克里斯,2009年

3
@Chris在WPF项目中为我工作。感谢您不要删除答案。
Rembunator


4

没有属性可以翻转以使其神奇地发生。查看表单的事件,并通过设置this.Top和来实现此功能变得相当琐碎this.Left。具体来说,你会想看看MouseDownMouseUpMouseMove


我认为我将不得不使用这些事件,但是我不确定该如何处理。调用MouseDown事件时,如何允许移动表单?
用户

1
按下鼠标时,您将设置一个标志并存储基本坐标。鼠标移动时-如果设置了标志-您可以通过新鼠标坐标的偏移量调整顶部和左侧。在鼠标上移,清除标志。
Murph)

不过,您可以使用Windows API相当容易地完成此操作,而这并不取决于仍然获得鼠标事件。例如,如果您抓住表单最顶部的像素并向上拖动,则此方法会失败。
乔伊(Joey)

4
public Point mouseLocation;
private void frmInstallDevice_MouseDown(object sender, MouseEventArgs e)
{
  mouseLocation = new Point(-e.X, -e.Y);
}

private void frmInstallDevice_MouseMove(object sender, MouseEventArgs e)
{
  if (e.Button == MouseButtons.Left)
  {
    Point mousePos = Control.MousePosition;
    mousePos.Offset(mouseLocation.X, mouseLocation.Y);
    Location = mousePos;
  }
}

这可以解决您的问题。


您也可以使用“ e.Location”代替Control.MousePosition
Reza Taibur

4

https://social.msdn.microsoft.com/Forums/vstudio/zh-CN/d803d869-68e6-46ff-9ff1-fabf78d6393c/how-to-make-a-borderless-form-in-c?forum=csharpgeneral

在上面的链接中,这段代码对我而言是成功的秘诀:)

protected override void OnMouseDown(MouseEventArgs e)  

{
      base.OnMouseDown(e);
      if (e.Button == MouseButtons.Left)
      {
        this.Capture = false;
        Message msg = Message.Create(this.Handle, 0XA1, new IntPtr(2), IntPtr.Zero);
        this.WndProc(ref msg);
      }
}

4

我找到的最好方法(当然是修改的)

// This adds the event handler for the control
private void AddDrag(Control Control) { Control.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DragForm_MouseDown); }
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();

private void DragForm_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        ReleaseCapture();
        SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
        // Checks if Y = 0, if so maximize the form
        if (this.Location.Y == 0) { this.WindowState = FormWindowState.Maximized; }
    }
}

要将拖动应用于控件,只需将其插入InitializeComponent()之后

AddDrag(NameOfControl);

4

它为我工作。

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        _mouseLoc = e.Location;
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            int dx = e.Location.X - _mouseLoc.X;
            int dy = e.Location.Y - _mouseLoc.Y;
            this.Location = new Point(this.Location.X + dx, this.Location.Y + dy);
        }
    }

欢迎使用Stack Overflow- 导游,请检查“ 如何回答”。您必须提供有关此代码如何解决OP“原始海报”问题的说明。对于检查您答案的任何人来说,它都会更有帮助。
Mauricio Arias Olave

2

对于.NET Framework 4,

您可以使用this.DragMove()MouseDown您所使用的拖放组件(mainLayout在这个例子中)的事件。

private void mainLayout_MouseDown(object sender, MouseButtonEventArgs e)
{
    this.DragMove();
}

2

最简单的方法是:

首先创建一个名为label1的标签。转到label1的事件>鼠标事件> Label1_Mouse Move并编写以下代码:

if (e.Button == MouseButtons.Left){
    Left += e.X;
    Top += e.Y;`
}

2

我试图使包含WPF元素宿主控件和WPF用户控件的无边界窗口表单可移动。

我最终在WPF用户控件中创建了一个名为StackPanel的堆栈面板,这似乎是尝试单击移动的合理选择。当我缓慢移动鼠标时,尝试junmats的代码可以工作,但是如果我快速移动鼠标,则鼠标会从表格上移开,并且表格会在移动的中间卡住。

这改善了他对我使用CaptureMouse和ReleaseCaptureMouse的情况的答案,现在即使我快速移动鼠标,鼠标也不会在移动时离开窗体。

private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
    _start_point = e.GetPosition(this);
    StackPanel.CaptureMouse();
}

private void StackPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
    StackPanel.ReleaseMouseCapture();
}

private void StackPanel_MouseMove(object sender, MouseEventArgs e)
{
    if (StackPanel.IsMouseCaptured)
    {
        var p = _form.GetMousePositionWindowsForms();
        _form.Location = new System.Drawing.Point((int)(p.X - this._start_point.X), (int)(p.Y - this._start_point.Y));
    }
}

    //Global variables;
    private Point _start_point = new Point(0, 0);

2

由于某些答案不允许子控件可拖动,因此我创建了一个小帮助程序类。应该通过顶层表格。如果需要,可以使其更通用。

class MouseDragger
{
    private readonly Form _form;
    private Point _mouseDown;

    protected void OnMouseDown(object sender, MouseEventArgs e)
    {
        _mouseDown = e.Location;
    }

    protected void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            int dx = e.Location.X - _mouseDown.X;
            int dy = e.Location.Y - _mouseDown.Y;
            _form.Location = new Point(_form.Location.X + dx, _form.Location.Y + dy);
        }
    }
    public MouseDragger(Form form)
    {
        _form = form;

        MakeDraggable(_form);            
    }

    private void MakeDraggable(Control control)
    {
        var type = control.GetType();
        if (typeof(Button).IsAssignableFrom(type))
        {
            return;
        }

        control.MouseDown += OnMouseDown;
        control.MouseMove += OnMouseMove;

        foreach (Control child in control.Controls)
        {
            MakeDraggable(child);
        }
    }
}

1

同样,如果您需要DoubleClick并使您的Form更大或更小,则可以使用“第一个答案”,创建一个全局int变量,每次用户单击用于拖动的组件时,将其加1。如果这样,variable == 2则使您的表单更大或更小。也每半秒或一秒使用一个计时器使你的variable = 0;


1

添加一个 MouseLeftButtonDown事件处理程序到MainWindow对我有用。

在自动生成的事件函数中,添加以下代码:

base.OnMouseLeftButtonDown(e);
this.DragMove();

1

我正在用另一种方法ToolStrip1_MouseLeave处理jay_t55的解决方案,该方法可以处理鼠标快速移动并离开区域的事件。

private bool mouseDown;
private Point lastLocation;

private void ToolStrip1_MouseDown(object sender, MouseEventArgs e) {
    mouseDown = true;
    lastLocation = e.Location;
}

private void ToolStrip1_MouseMove(object sender, MouseEventArgs e) {
    if (mouseDown) {
        this.Location = new Point(
            (this.Location.X - lastLocation.X) + e.X, (this.Location.Y - lastLocation.Y) + e.Y);

        this.Update();
    }
}

private void ToolStrip1_MouseUp(object sender, MouseEventArgs e) {
    mouseDown = false;
}

private void ToolStrip1_MouseLeave(object sender, EventArgs e) {
    mouseDown = false;
}

0

我尝试了以下操作,然后更改了,我的透明窗口不再冻结到位,而是可以移动的!(丢弃上面所有其他复杂的解决方案...)

   private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        // Begin dragging the window
        this.DragMove();
    }

这个答案是针对WPF的,问题是关于WinForms的。

0

Form1(): new Moveable(control1, control2, control3);

类:

using System;
using System.Windows.Forms;

class Moveable
{
    public const int WM_NCLBUTTONDOWN = 0xA1;
    public const int HT_CAPTION = 0x2;
    [System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
    [System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
    public static extern bool ReleaseCapture();
    public Moveable(params Control[] controls)
    {
        foreach (var ctrl in controls)
        {
            ctrl.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture();
                    SendMessage(ctrl.FindForm().Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
                    // Checks if Y = 0, if so maximize the form
                    if (ctrl.FindForm().Location.Y == 0) { ctrl.FindForm().WindowState = FormWindowState.Maximized; }
                }
            };
        }
    }
}

0
   [DllImport("user32.DLL", EntryPoint = "ReleaseCapture")]
    private extern static void ReleaseCapture();

    [DllImport("user32.DLL", EntryPoint = "SendMessage")]
    private extern static void SendMessage(System.IntPtr hWnd, int Msg, int wParam, int lParam);
    private void panelTitleBar_MouseDown(object sender, MouseEventArgs e)
    {
        ReleaseCapture();
        SendMessage(this.Handle, 0x112, 0xf012, 0);
    }

如果您提供一些解释支持您的答案,那将是很好的:)
Mustafamg
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.