Last active
May 23, 2023 06:14
-
-
Save cabrel/5025552 to your computer and use it in GitHub Desktop.
Drag & Drop in C# w/ WPF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// WPF Canvas Object | |
// Canvas LayoutCanvas; | |
// | |
// Events Required | |
// | |
// Canvas_PreviewMouseLeftButtonDown | |
// Canvas_PreviewMouseMove | |
// Canvas_PreviewMouseLeftButtonUp | |
// Parent_PreviewKeyDown | |
// | |
// LayoutCanvas.PreviewMouseLeftButtonDown += LayoutCanvas_PreviewMouseLeftButtonDown; | |
// LayoutCanvas.PreviewMouseMove += LayoutCanvas_PreviewMouseMove; | |
// LayoutCanvas.PreviewMouseLeftButtonUp += LayoutCanvas_PreviewMouseLeftButtonUp; | |
// WindowOrPage.PreviewKeyDown += WindowOrPage_PreviewKeyDown; | |
// | |
// Parameters Required | |
// | |
// For capturing the mouse position: | |
// Point ddStartPoint; | |
// | |
// The top left corner of the child object (left = x, top = y) | |
// double ddOriginalLeft; | |
// double ddOriginalTop; | |
// | |
// Properties for managing the state of the drag & drop process: | |
// bool ddIsMouseDown; | |
// bool ddIsBeingDragged; | |
// | |
// Our original UI element (in my case the children are all Image objects) | |
// UIElement ddOriginalElement; | |
// | |
// The container of the above element when we are in dragging mode | |
// System.Windows.Shapes.Rectangle ddOverlay; | |
// | |
// | |
// Canvas_PreviewMouseLeftButtonDown | |
// | |
// We assign this to our Canvas object as it will control | |
// catching whether or not we are clicking on the canvas itself | |
// or on one of its children | |
// | |
private void LayoutCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) | |
{ | |
// If the source of the click is our canvas object then we want | |
// to exit because we are looking for it's children to drag | |
// and not the canvas itself. | |
if(e.Source == LayoutCanvas) | |
{ | |
return; | |
} | |
// Identifies that we have started dragging | |
ddIsMouseDown = true; | |
// Captures the mouse position in the layout canvas | |
ddStartPoint = e.GetPosition(LayoutCanvas); | |
// Sets up our element that we will be dragging | |
ddOriginalElement = (UIElement)e.Source; | |
// Tells the Window to give the mouse to the LayoutCanvas | |
// object. | |
LayoutCanvas.CaptureMouse(); | |
e.Handled = true; | |
} | |
// | |
// Canvas_PreviewMouseMove | |
// | |
// Our event handler for updating the position of our | |
// dragged element | |
// | |
// This introduces two helper methods DragStarted() and DragMoved() | |
// They will be covered later on in the code. | |
// | |
private void LayoutCanvas_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e) | |
{ | |
if(ddIsMouseDown) | |
{ | |
if(!ddIsBeingDragged) | |
{ | |
// Capture our mouse position relative to the LayoutCanvas object | |
var mousePosition = e.GetPosition(LayoutCanvas); | |
// Creates a transparent rectangle around our current drag point and we want to | |
// check here that we are within that rectangle | |
if(Math.Abs(mousePosition.X - ddStartPoint.X) > SystemParameters.MinimumHorizontalDragDistance && | |
Math.Abs(mousePosition.Y - ddStartPoint.Y) > SystemParameters.MinimumVeritcalDragDistance) | |
{ | |
DragStarted(); | |
} | |
} | |
else | |
{ | |
DragMoved(); | |
} | |
} | |
} | |
// | |
// Canvas_PreviewMouseLeftButtonUp | |
// | |
// Controls the functionality for finishing our drag and drop process. | |
// This will also introduce the call to DragFinished(bool state); | |
// | |
private void LayoutCanvas_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) | |
{ | |
// This is a fairly simple check. If we are still dragging | |
// or starting to drag which means ddIsMouseDown would be 'True' | |
// then we don't stop the drag | |
if(ddIsMouseDown) | |
{ | |
DragFinished(false); | |
} | |
} | |
// | |
// Page_PreviewKeydown | |
// | |
// In my code I have my canvas in a Page, you can do this with a Window object as well. | |
// | |
private void Page_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e) | |
{ | |
if(e.Key == Key.Escape && ddIsBeingDragged) | |
{ | |
DragFinished = true; | |
} | |
} | |
// Helper Methods | |
// | |
// DragStarted() | |
// | |
// | |
private void DragStarted() | |
{ | |
// Capture our last remaining properties | |
// needed to support our drag and drop | |
// process | |
ddIsBeingDragged = true; | |
ddOriginalLeft = Canvas.GetLeft(ddOriginalElement); | |
ddOriginalTop = Canvas.GetTop(ddOriginalElement); | |
// What we are doing here is creating a semi-transparent | |
// mirror image of the object we are dragging. | |
// | |
// This allows us to visually see that we have selected | |
// an object and are dragging it. | |
var brush = new VisualBrush(ddOriginalElement); | |
brush.Opacity = 0.5; | |
ddOverlay = new System.Windows.Shapes.Rectangle(); | |
ddOverlay.Width = ddOriginalElement.RenderSize.Width; | |
ddOverlay.Height = ddOriginalElement.RenderSize.Height; | |
ddOverlay.Fill = brush; | |
// Finally add the overlay to the LayoutCanvas for displaying | |
LayoutCanvas.Children.Add(ddOverlay); | |
} | |
// | |
// DragMoved(); | |
// | |
private void DragMoved() | |
{ | |
// Capture the current mouse position, this will be used | |
// to redraw the overlay element we created in DragStarted() | |
var currentPosition = System.Windows.Input.Mouse.GetPosition(LayoutCanvas); | |
var elementLeft = (currentPosition.X - ddStartPoint.X) + ddOriginalLeft; | |
var elementTop = (currentPosition.Y - ddStartPoint.Y) + ddOriginalTop; | |
// We update the overlay's position on the LayoutCanvas | |
// by setting it's top left corner position below | |
Canvas.SetLeft(ddOverlay, elementLeft); | |
Canvas.SetTop(ddOverlay, elementTop); | |
} | |
// | |
// DragFinished(); | |
// | |
private void DragFinished(bool canceled) | |
{ | |
if(ddOverlay != null) | |
{ | |
// capture our current position | |
var topLeft = Canvas.GetLeft(ddOverlay); | |
var top = Canvas.GetTop(ddOverlay); | |
if(ddIsBeingDragged) | |
{ | |
LayoutCanvas.Children.Remove(ddOverlay); | |
// If it wasn't prematurely canceled, then | |
// move the element to the current mouse position | |
if(!canceled) | |
{ | |
Canvas.SetLeft(ddOriginalElement, topLeft); | |
Canvas.SetTop(ddOriginalElement, top); | |
} | |
// Release the mouse from the layoutcanvas. | |
// This is very important. If you do not release the mouse | |
// you have to set the focus of the mouse to another application | |
// and then back again to regain mouse control | |
LayoutCanvas.ReleaseMouseCapture(); | |
// Reset our drag & drop properties | |
ddOverlay = null; | |
ddIsMouseBeingDragged = false; | |
ddIsMouseDown = false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment