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.