How to Drag a Usercontrol Inside a Canvas

How to drag a UserControl inside a Canvas

This is done in silverlight and not in WPF, but it should work the same.

Create two private properties on the control:

protected bool isDragging;  
private Point clickPosition;

Then attatch some event handlers in the constructor of the control:

this.MouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(Control_MouseMove);

Now create those methods:

private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isDragging = true;
var draggableControl = sender as UserControl;
clickPosition = e.GetPosition(this);
draggableControl.CaptureMouse();
}

private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
var draggable = sender as UserControl;
draggable.ReleaseMouseCapture();
}

private void Control_MouseMove(object sender, MouseEventArgs e)
{
var draggableControl = sender as UserControl;

if (isDragging && draggableControl != null)
{
Point currentPosition = e.GetPosition(this.Parent as UIElement);

var transform = draggableControl.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
draggableControl.RenderTransform = transform;
}

transform.X = currentPosition.X - clickPosition.X;
transform.Y = currentPosition.Y - clickPosition.Y;
}
}

A few things to note here:

1. This does not have to be in a canvas. It can be in a stackpanel, or grid as well.

2. This makes the entire control draggable, that means if you click anywhere in the control and drag it will drag the whole control. Not sure if thats exactly what you want.

Edit-

Expanding on some of the specifics in your question:
The best way that I would implement this is to create a class that inherits from UserControl, maybe called DraggableControl that is built with this code, then all draggable controls should extend the DraggableControl.

Edit 2 - There is small issue when you have a datagrid in this control. If you sort a column in the datagrid the MouseLeftButtonUp event never fires. I have updated the code so that isDragging is protected. I found the best solution is to tie this anonymous method to the LostMouseCapture event of the datagrid:

this.MyDataGrid.LostMouseCapture += (sender, e) => { this.isDragging = false; };

Drag item from listvew in one usercontrol to Canvas in another usercontrol

The following link gave me solution my problem.

Click here

How to make a user control draggable on screen like a window

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.

Moving a Control on a Canvas on Drag & Drop

You must not use Transform...but only

draggableControl.SetValue(Canvas.LeftProperty, X);
draggableControl.SetValue(Canvas.TopProperty, Y);

because transform affect only rendering

C# WPF Draggable UserControls in ListBox on Canvas

I was finally able to figure out how to solve this problem, or atleast how to make a workaround. The problem seems to be that ListBox also captures the mouse within its select code. So what I changed was to add e.Handled = true so the ListBox wouldn't get the event and thus override the mousecapture. This did render the select mechanism unusable but to workaround this I moved my code to only listen to MouseRight event which means that when I leftclick I select and when I rightclick I can drag.

private static void element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
if (element == null) return;
Debug.WriteLine(element);

isDragging = true;
element.CaptureMouse();
offset = e.GetPosition(element);
e.Handled = true;
}

Drage and move on click a control on Canvas wpf

You should use e.GetPosition(parentControl) to get the position from the event args.

Binding the position and size of a UserControl inside a Canvas in WPF

Have you tried using a Mode=TwoWay binding?

<YourUserControl 
Canvas.Top="{Binding TopProperty, Mode=TwoWay}"
Canvas.Left={Binding LeftProperty, Mode=TwoWay}"
Height="{Binding HeightProperty, Mode=TwoWay}"
Width="{Binding WidthProperty, Mode=TwoWay}" />

I'm not convinced two-way binding will work with resize or drag and drop operations, but there's only one way to find out.



Related Topics



Leave a reply



Submit