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

时间:2014-05-23 10:48:27

标签: c# winforms mousemove

我正在寻找使用鼠标移动控件的最佳方法,这种情况非常具体。我希望当用户在控件外部单击并将鼠标拖动到控件顶部时,它会从鼠标悬停在它上方的那一刻开始移动。换句话说,我希望只要鼠标在它上面,就可以移动对象,无论是在点击时,还是在执行时以及在移动过程中移动它时。

什么是最佳解决方案?

3 个答案:

答案 0 :(得分:0)

这可能有点棘手,但您可以订阅包含您要移动的控件的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是为了确保当鼠标移动时,指针停留在控件的范围内。你应该在实践中更好地处理这个问题。

答案 1 :(得分:0)

您必须在控件事件上监视/处理鼠标,然后确定鼠标按钮是否已关闭。当他们点击拖动控件时,可以/将应用相同的逻辑。

答案 2 :(得分:0)

一般的想法是处理父控件的事件(无论是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;

接下来,您需要将MouseDownMouseUpMouseMove处理程序添加到父控件 - 而不是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;
}

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