当鼠标下移开始在对象外部时,如何使用鼠标移动控件

本文关键字:鼠标 何使用 移动控件 外部 对象 开始 | 更新日期: 2023-09-27 18:04:50

我正在寻找在非常具体的情况下用鼠标移动控件的最佳方法。我希望当用户单击控件外部并将鼠标拖动到控件顶部时,从鼠标在其上的那一刻起,它就开始移动。换句话说,我希望每当鼠标在对象上时,无论是在单击时,还是在执行时以及在移动过程中经过对象时,对象都被移动。

最好的解决方案是什么?

当鼠标下移开始在对象外部时,如何使用鼠标移动控件

总体思路是处理父控件的事件(无论是Form还是Panel包含您的子Piece控件)

因此,伪算法为:
if mouse button was pressed, set a certain flag
if mouse was moved
    if flag isn't set, do nothing
    if flag is set
       if we haven't started dragging, try to find an overlapping piece
       if we have an overlapping piece, move it
if mouse button was released, clear the flag

那么实际的代码可能看起来像下面这样:

你需要记住三件事(我现在想到的):

private bool _mouseHeldDown = false; // this is the 'flag'
private Piece _draggedPiece = null;
private Point _draggedOffset;

接下来,您需要将MouseDown, MouseUpMouseMove处理程序添加到父控件-而不是Piece控件。

private void ParentControl_MouseDown(object sender, MouseEventArgs e)
{
    // this is pretty obvious
    _mouseHeldDown = true;
}
private void ParentControl_MouseMove(object sender, MouseEventArgs e)
{
    if (!_mouseHeldDown)
        return;
    // get the cursor location in Form-relative coordinates
    var cursor = this.PointToClient(Cursor.Position);
    // only search for pieces if we haven't yet started dragging
    if (_draggedPiece == null)
        FindOverlappingPiece(cursor);
    // if we are currently dragging a piece, update its position
    if (_draggedPiece != null)
        MoveDraggedPiece(cursor);
}
private void ParentControl_MouseUp(object sender, MouseEventArgs e)
{
    // cleanup
    _mouseHeldDown = false;
    _draggedPiece = null;
}

要获得所有的部分,您可能已经在某个地方有一个列表,或者您可以遍历类型为Piece的所有子控件:

private IEnumerable<Piece> GetAllPieces()
{
    // enumerate all child controls which are of type 'Piece'
    return this
        .Controls
        .Cast<Control>()
        .Select(c => c as Piece)
        .Where(c => c != null);
}

搜索和移动方法也很容易实现:

private void FindOverlappingPiece(Point cursor)
{
    // get the first piece which overlaps with cursor position
    _draggedPiece = GetAllPieces()
        .FirstOrDefault(p => p.Bounds.Contains(cursor));
    // get the offset from the piece top-left corner
    // (we need to know where we 'entered' the piece)
    if (_draggedPiece != null)
    {
        _draggedPiece.BringToFront();
        _draggedOffset = _draggedPiece.Location;
        _draggedOffset.Offset(-cursor.X, -cursor.Y);
    }
}
private void MoveDraggedPiece(Point cursor)
{
    // now we take the initial piece position into account
    var newLocation = cursor;
    newLocation.Offset(_draggedOffset);
    _draggedPiece.Location = newLocation;
}

当子控件也被单击时,要使整个事情工作,最简单的方法可能是将所有事件连接到这些相同的事件处理程序:

// 'this' is obviously the parent form/control
this.MouseDown += ParentControl_MouseDown;
this.MouseUp += ParentControl_MouseUp;
this.MouseMove += ParentControl_MouseMove;
// attach all child piece controls also
foreach (var piece in GetAllPieces())
{
    piece.MouseDown += ParentControl_MouseDown;
    piece.MouseUp += ParentControl_MouseUp;
    piece.MouseMove += ParentControl_MouseMove;
}

当然,如果要动态地添加和删除片段,请确保在正确的时间和地点附加和分离处理程序。

您必须监视/处理控件的鼠标经过事件,然后确定鼠标按钮是否按下。当他们点击拖动控件时,同样的逻辑可以/将会应用。

这可能有点棘手,但是您可以订阅包含您希望移动的控件的MouseMove事件。

在这种情况下,测试位置是否在您想要移动的控件范围内,并且Left按钮是否按下:

private void containerControl_MouseMove(Object sender, MouseEventArgs e)
{
    if (ctrlToMove.Bounds.Contains(e.Location) && e.Button == MouseButtons.Left)
        ctrlToMove.Location = new Point(e.Location.X - 5, e.Location.Y - 5);
}

位置调整- 5是为了确保当鼠标移动时,指针保持在控件的边界内。您应该在实践中以更好的方式处理这个问题。