0

I'm looking for the best way to move a control with the mouse, in a situation quite specific. I want that when the user clicks outside the control, and drag the mouse on top of a control, this begins to move from the moment that the mouse is over it. In other words, I want the object to be moved whenever the mouse is over it, either when the click is made ​​on, or when it is carried out and during movement pass over him.

What is the best solution?

Jorge Oliveira
  • 345
  • 1
  • 2
  • 13
  • Have you tried anything so far? Do you know how to attach an event handler? Do you know about `Control.MouseMove`, `Control.MouseDown` and `Control.MouseUp`? What exact problem are you facing while trying to implement this yourself? – vgru May 23 '14 at 11:13
  • Hello @Groo, yes, in fact i have already worked with event handler, and i am already moving objects with the mouse. You can check some more info of what i'm doing in this topic: http://stackoverflow.com/questions/23631103/mouse-down-between-two-pieces-of-a-puzzle My only problem is how to start moving an object, when the user starts is intention to move outside that object, got it? When the user puts the mouse down outside the object and after that moves the mouse over that object(control). – Jorge Oliveira May 23 '14 at 11:18
  • What if a different control is already being dragged? (since your [previous question](http://stackoverflow.com/questions/23631103/mouse-down-between-two-pieces-of-a-puzzle) indicates it's a puzzle game) – vgru May 23 '14 at 13:12
  • if so, nothing else happens to the other controls... While the mouse button still down, it only takes care of the control you are already dragging. – Jorge Oliveira May 23 '14 at 14:01

3 Answers3

0

This can be a little tricky, but you can subscribe to the MouseMove event of the control that contains the one you wish to move.

In that event, test for the position being within the bounds of the control you want to move, and that the Left button is down:

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);
}

The position adjustment - 5 is to ensure that as the mouse moves, the pointer stays within the control's bounds. You should deal with this in a better way in practice.

DonBoitnott
  • 10,787
  • 6
  • 49
  • 68
0

You would have to monitor/handle the mouse over event of the control and then determine if the mouse button is down or not. Same logic can/will apply when they click to drag the control.

user3036342
  • 1,023
  • 7
  • 15
  • Many thanks for your answer. But when you have the mouse already down outside the control, and you move it over a control, how can you handle the mouse_move over the control? – Jorge Oliveira May 23 '14 at 11:05
  • 1
    You need to read up on event handlers and how to access them: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.mousehover(v=vs.110).aspx – user3036342 May 23 '14 at 11:11
0

General idea is to handle parent control's events (whether it's a Form or a Panel containing your child Piece controls).

So, the pseudo-algorithm is:

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

So the actual code might look something like the following.

You will need to keep track of three things (that come to my mind right now):

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

Next, you need to add MouseDown, MouseUp and MouseMove handlers to the parent control - not the Piece control.

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;
}

To get all the pieces, you probably already have a list somewhere, or you can iterate through all child controls of type 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);
}

Searching and moving methods are also trivial to implement:

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;
}

To make the whole thing work when child controls are clicked also, the simplest way is probably to wire all the events to these same event handlers:

// '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;
}

Of course, if you're adding and removing pieces dynamically, make sure that you attach and detach handlers at the right time and place.

vgru
  • 49,838
  • 16
  • 120
  • 201