How to make a user control draggable on screen like a window
You can simply use MouseDragElementBehavior.
UPD
Important thing about MouseDragElementBehavior
behavior:
The MouseDragElementBehavior behavior doesn't work for any controls that handle MouseClick events (Button, TextBox, and ListBox controls, for example). If you need the ability to drag a control of one of these types, make that control a child of a control that can be dragged (a border, for example). You can then apply the MouseDragElementBehavior behavior to the parent element.
You can also implement your own drag behavior like this:
public class DragBehavior : Behavior<UIElement>
{
private Point elementStartPosition;
private Point mouseStartPosition;
private TranslateTransform transform = new TranslateTransform();
protected override void OnAttached()
{
Window parent = Application.Current.MainWindow;
AssociatedObject.RenderTransform = transform;
AssociatedObject.MouseLeftButtonDown += (sender, e) =>
{
elementStartPosition = AssociatedObject.TranslatePoint( new Point(), parent );
mouseStartPosition = e.GetPosition(parent);
AssociatedObject.CaptureMouse();
};
AssociatedObject.MouseLeftButtonUp += (sender, e) =>
{
AssociatedObject.ReleaseMouseCapture();
};
AssociatedObject.MouseMove += (sender, e) =>
{
Vector diff = e.GetPosition( parent ) - mouseStartPosition;
if (AssociatedObject.IsMouseCaptured)
{
transform.X = diff.X;
transform.Y = diff.Y;
}
};
}
}
Based upon information in @DmitryMartovoi's answer, I have come up with a way to make this work. I'm still giving Dmitry a +1 as I wouldn't have been able to figure this out without his contribution.
What I did was I created a TranslateTransform
in my UserControl's
constructor and assigned it to its RenderTransform
property:
RenderTransform = new TranslateTransform();
In the XAML, I named the Border
control that the user clicks on to drag the whole control:
<Border Background="{DynamicResource PopupBackground}"
BorderBrush="{DynamicResource PopupBorder}"
BorderThickness="5,5,5,0"
MouseLeftButtonDown="Grid_MouseLeftButtonDown"
MouseLeftButtonUp="Grid_MouseLeftButtonUp"
MouseMove="Grid_MouseMove"
Name="TitleBorder">
. . .
</Border>
Finally, I modified the various Mouse event handlers as follows:
private void Grid_MouseLeftButtonDown( object sender, MouseButtonEventArgs e ) {
CurrentMousePosition = e.GetPosition( Parent as Window );
TitleBorder.CaptureMouse();
}
private void Grid_MouseLeftButtonUp( object sender, MouseButtonEventArgs e ) {
if ( TitleBorder.IsMouseCaptured ) {
TitleBorder.ReleaseMouseCapture();
}
}
private void Grid_MouseMove( object sender, MouseEventArgs e ) {
Vector diff = e.GetPosition( Parent as Window ) - CurrentMousePosition;
if ( TitleBorder.IsMouseCaptured ) {
( RenderTransform as TranslateTransform ).X = diff.X;
( RenderTransform as TranslateTransform ).Y = diff.Y;
}
}
This works beautifully. The entire UserControl
and all of its contents move smoothly when you drag the Border
, keeping up with the mouse. And the entire UserControl
does not move if you click anywhere else on its surface.
Thanks again to @DmitryMartovoi for the code he supplied.
EDIT: I am editing this answer because the above code, while it worked, wasn't perfect. Its flaw is that the control would pop back to its original location on screen when you clicked on the title bar area and before you started dragging. This was annoying and totally wrong.
The approach I came up with that actually worked flawlessly involved first putting the control in a Canvas
. It's important that the parent of the control be a Canvas
or the following code won't work. I also stopped using the RenderTransform
. I added a private property called canvas
of type Canvas
. I added a Loaded
event handler to the popup control to do some important initialization:
private void KeyboardPopup_Loaded( object sender, RoutedEventArgs e ) {
canvas = Parent as Canvas;
if ( canvas == null ) {
throw new InvalidCastException( "The parent of a KeyboardPopup control must be a Canvas." );
}
}
With all of this done, here are the modified Mouse event handlers:
private void TitleBorder_MouseLeftButtonDown( object sender, MouseButtonEventArgs e ) {
StartMousePosition = e.GetPosition( canvas );
TitleBorder.CaptureMouse();
}
private void TitleBorder_MouseLeftButtonUp( object sender, MouseButtonEventArgs e ) {
if ( TitleBorder.IsMouseCaptured ) {
Point mousePosition = e.GetPosition( canvas );
Canvas.SetLeft( this, Canvas.GetLeft( this ) + mousePosition.X - StartMousePosition.X );
Canvas.SetTop ( this, Canvas.GetTop ( this ) + mousePosition.Y - StartMousePosition.Y );
canvas.ReleaseMouseCapture();
}
}
private void TitleBorder_MouseMove( object sender, MouseEventArgs e ) {
if ( TitleBorder.IsMouseCaptured && e.LeftButton == MouseButtonState.Pressed ) {
Point mousePosition = e.GetPosition( canvas );
// Compute the new Left & Top coordinates of the control
Canvas.SetLeft( this, Canvas.GetLeft( this ) + mousePosition.X - StartMousePosition.X );
Canvas.SetTop ( this, Canvas.GetTop ( this ) + mousePosition.Y - StartMousePosition.Y );
StartMousePosition = mousePosition;
}
}
The control stays where you dropped it when you click on the title bar to move it a second time, and it only moves when you click on the title bar. Clicking anywhere else in the control does nothing, and dragging is smooth and responsive.
http://www.codeproject.com/Tips/442276/Drag-and-Drop-WPF-Controls This is the awesome solution I got after spending lot of time. Although example shown here are normal controls but after some changes you can make it work for user controls also.