none
WPF Image Drag

    Question

  • Hello,

    I'm getting crazy with a thing that probably is really easy to make.

    Well, I have 1 or more images in a Windows C# Form. 

    Simply I want to allow the users to drag this images around the Form (as Microsoft Sourface does) and release them where the user release the mouse.

     

    I tried with many controls like the ScrollViewer and i tried to move it using a ViewPort3D and 2D without succeed...

     

    Someone has any idea? I've looked on the net too but i didn't found anything .

     

    Thanks,

    Andy

    Monday, January 21, 2008 3:31 PM

Answers

  • Mouse dragging logic is fairly straightforward:  In the OnMouseDown handler, you save the position of both the object you want to drag and the mouse pointer, and you call CaptureMouse.  In OnMouseMove, you calculate the difference between the coordinates of the current mouse pointer position and the saved position, and add that to the original object position. (If you're on a Canvas, you can move the object by calling Canvas.SetLeft and Canvas.SetTop for the object; otherwise you can adjust a TranslateTransform object set to the object's RenderTransform property.)  In OnMouseUp, you call ReleaseCapture.

     

    Because your app can lose the mouse capture in other ways (such as the appearance of a system modal message box), you'll also want to override OnLostMouseCapture to abort the dragging operation (if it hasn't terminated with OnMouseUp) and perform cleanup.  You might also want to override OnTextInput to abort the drag if the user presses the Escape key.

     

    All of this logic is included in the DrawCircles program in Chapter 9 of my book Applications = Code + Markup.  You can, of course, download the code without buying the book, but you'll have to live with the guilt.

     

     

    Monday, January 21, 2008 4:59 PM
  •  

    WoW I've made it!!!

    Here's my source (XAML & C#), if someone needs something similar

     

    Code Block

    <Window x:Class="MyProject.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Window1" Height="500" Width="500">

    <Grid>

    <Canvas x:Name="mycanv">

    <Image Width="150" x:Name="myimg" Source="some_source.jpg"/>

    </Canvas>

    </Grid>

    </Window>

     

     

    Code Block

    //C#

    private Point mouseClick;

    private double canvasLeft;

    private double canvasTop;

     

    public Window1()

    {

    InitializeComponent();

    foreach (object obj in mycanv.Children)

    {

    try

    {

    Image img = (Image)obj;

    img.PreviewMouseDown += new MouseButtonEventHandler(myimg_MouseDown);

    img.PreviewMouseMove += new MouseEventHandler(myimg_MouseMove);

    img.PreviewMouseUp += new MouseButtonEventHandler(myimg_MouseUp);

    img.TextInput += new TextCompositionEventHandler(myimg_TextInput);

    img.LostMouseCapture += new MouseEventHandler(myimg_LostMouseCapture);

    img.SetValue(Canvas.LeftProperty, 0.0);

    img.SetValue(Canvas.TopProperty, 0.0);

    }

    catch

    {

    }

    }

    }

    void myimg_LostMouseCapture(object sender, MouseEventArgs e)

    {

    ((Image)sender).ReleaseMouseCapture();

    }

    void myimg_TextInput(object sender, TextCompositionEventArgs e)

    {

    ((Image)sender).ReleaseMouseCapture();

    }

    void myimg_MouseUp(object sender, MouseButtonEventArgs e)

    {

    ((Image)sender).ReleaseMouseCapture();

    }

    void myimg_MouseMove(object sender, MouseEventArgs e)

    {

    if (((Image)sender).IsMouseCaptured)

    {

    Point mouseCurrent = e.GetPosition(null);

    double Left = mouseCurrent.X - canvasLeft;

    double Top = mouseCurrent.Y - canvasTop;

    ((Image)sender).SetValue(Canvas.LeftProperty, canvasLeft + Left);

    ((Image)sender).SetValue(Canvas.TopProperty, canvasTop + Top);

    canvasLeft = Canvas.GetLeft(((Image)sender));

    canvasTop = Canvas.GetTop(((Image)sender));

    }

    }

    void myimg_MouseDown(object sender, MouseButtonEventArgs e)

    {

    mouseClick = e.GetPosition(null);

    canvasLeft = Canvas.GetLeft(((Image)sender));

    canvasTop = Canvas.GetTop(((Image)sender));

    ((Image)sender).CaptureMouse();

    }

     

     

    Thanks again!

    Monday, January 21, 2008 6:30 PM

All replies

  • Mouse dragging logic is fairly straightforward:  In the OnMouseDown handler, you save the position of both the object you want to drag and the mouse pointer, and you call CaptureMouse.  In OnMouseMove, you calculate the difference between the coordinates of the current mouse pointer position and the saved position, and add that to the original object position. (If you're on a Canvas, you can move the object by calling Canvas.SetLeft and Canvas.SetTop for the object; otherwise you can adjust a TranslateTransform object set to the object's RenderTransform property.)  In OnMouseUp, you call ReleaseCapture.

     

    Because your app can lose the mouse capture in other ways (such as the appearance of a system modal message box), you'll also want to override OnLostMouseCapture to abort the dragging operation (if it hasn't terminated with OnMouseUp) and perform cleanup.  You might also want to override OnTextInput to abort the drag if the user presses the Escape key.

     

    All of this logic is included in the DrawCircles program in Chapter 9 of my book Applications = Code + Markup.  You can, of course, download the code without buying the book, but you'll have to live with the guilt.

     

     

    Monday, January 21, 2008 4:59 PM
  • Thank u

    It seems really the correct way to do that, of course it is!

    The only thing I don't know is: how can i see the position of an Image object?

     

    Thanks again

    Monday, January 21, 2008 5:36 PM
  •  

    WoW I've made it!!!

    Here's my source (XAML & C#), if someone needs something similar

     

    Code Block

    <Window x:Class="MyProject.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Window1" Height="500" Width="500">

    <Grid>

    <Canvas x:Name="mycanv">

    <Image Width="150" x:Name="myimg" Source="some_source.jpg"/>

    </Canvas>

    </Grid>

    </Window>

     

     

    Code Block

    //C#

    private Point mouseClick;

    private double canvasLeft;

    private double canvasTop;

     

    public Window1()

    {

    InitializeComponent();

    foreach (object obj in mycanv.Children)

    {

    try

    {

    Image img = (Image)obj;

    img.PreviewMouseDown += new MouseButtonEventHandler(myimg_MouseDown);

    img.PreviewMouseMove += new MouseEventHandler(myimg_MouseMove);

    img.PreviewMouseUp += new MouseButtonEventHandler(myimg_MouseUp);

    img.TextInput += new TextCompositionEventHandler(myimg_TextInput);

    img.LostMouseCapture += new MouseEventHandler(myimg_LostMouseCapture);

    img.SetValue(Canvas.LeftProperty, 0.0);

    img.SetValue(Canvas.TopProperty, 0.0);

    }

    catch

    {

    }

    }

    }

    void myimg_LostMouseCapture(object sender, MouseEventArgs e)

    {

    ((Image)sender).ReleaseMouseCapture();

    }

    void myimg_TextInput(object sender, TextCompositionEventArgs e)

    {

    ((Image)sender).ReleaseMouseCapture();

    }

    void myimg_MouseUp(object sender, MouseButtonEventArgs e)

    {

    ((Image)sender).ReleaseMouseCapture();

    }

    void myimg_MouseMove(object sender, MouseEventArgs e)

    {

    if (((Image)sender).IsMouseCaptured)

    {

    Point mouseCurrent = e.GetPosition(null);

    double Left = mouseCurrent.X - canvasLeft;

    double Top = mouseCurrent.Y - canvasTop;

    ((Image)sender).SetValue(Canvas.LeftProperty, canvasLeft + Left);

    ((Image)sender).SetValue(Canvas.TopProperty, canvasTop + Top);

    canvasLeft = Canvas.GetLeft(((Image)sender));

    canvasTop = Canvas.GetTop(((Image)sender));

    }

    }

    void myimg_MouseDown(object sender, MouseButtonEventArgs e)

    {

    mouseClick = e.GetPosition(null);

    canvasLeft = Canvas.GetLeft(((Image)sender));

    canvasTop = Canvas.GetTop(((Image)sender));

    ((Image)sender).CaptureMouse();

    }

     

     

    Thanks again!

    Monday, January 21, 2008 6:30 PM
  • Hi, Thanks a lot for this code. You saved me a lot of time.
    I just wanted to add something.
    If you run this code, when you first click on the image in the centre, suddenly the image shifts and the top left of the image comes under the mouse. I mean, the moving of the image jerks when you start. The reason is that you are doing nothing with the variable " Point mouseClick".
    So, if you change the following two lines from your code
    double Left = mouseCurrent.X - canvasLeft;

    double Top = mouseCurrent.Y - canvasTop;

    and rather put in

    double Left = mouseCurrent.X - mouseClick.X;

    double Top = mouseCurrent.Y - mouseClick.Y;

    mouseClick=e.GetPosition(null);

    there is no more jerky motion.





    Friday, September 12, 2008 3:57 AM
  • Hi, Thanks a lot for this code. You saved me a lot of time.
    I just wanted to add something.
    If you run this code, when you first click on the image in the centre, suddenly the image shifts and the top left of the image comes under the mouse. I mean, the moving of the image jerks when you start. The reason is that you are doing nothing with the variable " Point mouseClick".
    So, if you change the following two lines from your code
    double Left = mouseCurrent.X - canvasLeft;

    double Top = mouseCurrent.Y - canvasTop;

    and rather put in

    double Left = mouseCurrent.X - mouseClick.X;

    double Top = mouseCurrent.Y - mouseClick.Y;

    mouseClick=e.GetPosition(null);

    there is no more jerky motion.





    Friday, September 12, 2008 3:57 AM
  • Hi, Thanks a lot for this code. You saved me a lot of time.
    I just wanted to add something.
    If you run this code, when you first click on the image in the centre, suddenly the image shifts and the top left of the image comes under the mouse. I mean, the moving of the image jerks when you start. The reason is that you are doing nothing with the variable " Point mouseClick".
    So, if you change the following two lines from your code
    double Left = mouseCurrent.X - canvasLeft;

    double Top = mouseCurrent.Y - canvasTop;

    and rather put in

    double Left = mouseCurrent.X - mouseClick.X;

    double Top = mouseCurrent.Y - mouseClick.Y;

    mouseClick=e.GetPosition(null);

    there is no more jerky motion.





    Friday, September 12, 2008 3:57 AM
  • Additionally to keep the images in bounds replace the myimg_MouseUp function with this:

     

            void myimg_MouseUp(object sender, MouseButtonEventArgs e)
            {
                mouseClick = e.GetPosition(null);
    
                canvasLeft = Canvas.GetLeft(((Image)sender));
                canvasTop = Canvas.GetTop(((Image)sender));
    
                if (canvasLeft < 0)
                {
                    canvasLeft = 0;
                }
    
                if (canvasTop < 0)
                {
                    canvasTop = 0;
                }
    
                if (canvasLeft > mycanv.ActualWidth)
                {
                    canvasLeft = mycanv.ActualWidth - ((Image)sender).ActualWidth;
                }
    
                if (canvasTop > mycanv.ActualHeight)
                {
                    canvasTop = mycanv.ActualHeight - ((Image)sender).ActualHeight;
                }
    
                ((Image)sender).SetValue(Canvas.LeftProperty, canvasLeft);
                ((Image)sender).SetValue(Canvas.TopProperty, canvasTop);
    
                ((Image)sender).ReleaseMouseCapture();
            }

    • Edited by Linknum23 Tuesday, April 06, 2010 11:14 PM forgot to put code in pretty code block
    Tuesday, April 06, 2010 11:13 PM
  • Wow! Charles Petzold posts here!

    Charles, thanks for your book "Applications = Code + Markup"! It's the first WPF book that got me to really understand WPF.  Not just the "how", but the "why".  I completely agree with his method: you need to understand what the code is doing before you get into the markup.  Just like any markup, you're going to need to understand what's going on under the hood before you can really get in and troubleshoot.

    CZuhars

    Wednesday, July 14, 2010 5:01 AM