Let the user move and resize a rectangle in WPF and C#

[move and resize a rectangle]

This example shows how you can let the user move and resize a Rectangle object at run time in a WPF program. The program’s XAML code defines a Canvas object that contains the Rectangle. The program catches the Canvas object’s MouseDown, MouseMove, and MouseUp events. One oddity of this is that the Canvas must have a non-null background to make it generate mouse events. In this example, I set its background to Transparent so it’s still invisible, but it’s not null.

The program uses the following code to track the resizing operation.

// The part of the rectangle the mouse is over.
private enum HitType
{
    None, Body, UL, UR, LR, LL
};

// The part of the rectangle under the mouse.
HitType MouseHitType = HitType.None;

// True if a drag is in progress.
private bool DragInProgress = false;

// The drag's last point.
private Point LastPoint;

The HitType enumeration indicates parts of the rectangle. The variable MouseHitType indicates the part of the rectangle under the mouse at various times. The variable DragInProgress is true when the user is moving or resizing the rectangle. LastPoint is the last recorded position of the mouse during a drag.

The program uses two helper methods. The SetHitType method shown in the following code returns a HitType value to indicate what part of the rectangle is below the mouse.

// Return a HitType value to indicate what is at the point.
private HitType SetHitType(Rectangle rect, Point point)
{
    double left = Canvas.GetLeft(rectangle1);
    double top = Canvas.GetTop(rectangle1);
    double right = left + rectangle1.Width;
    double bottom = top + rectangle1.Height;
    if (point.X < left) return HitType.None;
    if (point.X > right) return HitType.None;
    if (point.Y < top) return HitType.None;
    if (point.Y > bottom) return HitType.None;

    const double GAP = 10;
    if (point.X - left < GAP)
    {
        // Left edge.
        if (point.Y - top < GAP) return HitType.UL;
        if (bottom - point.Y < GAP) return HitType.LL;
        return HitType.L;
    }
    else if (right - point.X < GAP)
    {
        // Right edge.
        if (point.Y - top < GAP) return HitType.UR;
        if (bottom - point.Y < GAP) return HitType.LR;
        return HitType.R;
    }
    if (point.Y - top < GAP) return HitType.T;
    if (bottom - point.Y < GAP) return HitType.B;
    return HitType.Body;
}

This method uses the Canvas class's GetLeft and GetTop methods to get the rectangle's left and top coordinates. (The example assumes you set those values for the rectangle.) Those are attached properties provided by the Canvas control for the Rectangle control, so you need to use the Canvas control's methods to get and set their values.

The code first compares the left and top values to the rectangle's edges. If the mouse lies outside of the rectangle, the method returns None.

Next the code uses the mouse's coordinates and the rectangle's coordinates to decide whether the mouse is over a rectangle corner, edge, or body, and it returns the correct HitType.

The following code shows the SetMouseCursor helper method.

// Set a mouse cursor appropriate for the current hit type.
private void SetMouseCursor()
{
    // See what cursor we should display.
    Cursor desired_cursor = Cursors.Arrow;
    switch (MouseHitType)
    {
        case HitType.None:
            desired_cursor = Cursors.Arrow;
            break;
        case HitType.Body:
            desired_cursor = Cursors.ScrollAll;
            break;
        case HitType.UL:
        case HitType.LR:
            desired_cursor = Cursors.SizeNWSE;
            break;
        case HitType.LL:
        case HitType.UR:
            desired_cursor = Cursors.SizeNESW;
            break;
        case HitType.T:
        case HitType.B:
            desired_cursor = Cursors.SizeNS;
            break;
        case HitType.L:
        case HitType.R:
            desired_cursor = Cursors.SizeWE;
            break;
    }

    // Display the desired cursor.
    if (Cursor != desired_cursor) Cursor = desired_cursor;
}

This code simply sets the cursor that is appropriate for the mouse's hit type.

The following code executes when the user presses the mouse down over the Canvas.

// Start dragging.
private void canvas1_MouseDown(object sender,
    MouseButtonEventArgs e)
{
    MouseHitType = SetHitType(rectangle1,
        Mouse.GetPosition(canvas1));
    SetMouseCursor();
    if (MouseHitType == HitType.None) return;

    LastPoint = Mouse.GetPosition(canvas1);
    DragInProgress = true;
}

This method calls SetHitType to see whether the mouse is over the rectangle. If it is not, the method returns.

Otherwise the method saves the mouse's current location in the LastPoint variable and sets DragInProgress to true.

When the user moves the mouse, the following code executes.

// If a drag is in progress, continue the drag.
// Otherwise display the correct cursor.
private void canvas1_MouseMove(object sender, MouseEventArgs e)
{
    if (DragInProgress)
    {
        // See how much the mouse has moved.
        Point point = Mouse.GetPosition(canvas1);
        double offset_x = point.X - LastPoint.X;
        double offset_y = point.Y - LastPoint.Y;

        // Get the rectangle's current position.
        double new_x = Canvas.GetLeft(rectangle1);
        double new_y = Canvas.GetTop(rectangle1);
        double new_width = rectangle1.Width;
        double new_height = rectangle1.Height;

        // Update the rectangle.
        switch (MouseHitType)
        {
            case HitType.Body:
                new_x += offset_x;
                new_y += offset_y;
                break;
            case HitType.UL:
                new_x += offset_x;
                new_y += offset_y;
                new_width -= offset_x;
                new_height -= offset_y;
                break;
            case HitType.UR:
                new_y += offset_y;
                new_width += offset_x;
                new_height -= offset_y;
                break;
            case HitType.LR:
                new_width += offset_x;
                new_height += offset_y;
                break;
            case HitType.LL:
                new_x += offset_x;
                new_width -= offset_x;
                new_height += offset_y;
                break;
            case HitType.L:
                new_x += offset_x;
                new_width -= offset_x;
                break;
            case HitType.R:
                new_width += offset_x;
                break;
            case HitType.B:
                new_height += offset_y;
                break;
            case HitType.T:
                new_y += offset_y;
                new_height -= offset_y;
                break;
        }

        // Don't use negative width or height.
        if ((new_width > 0) && (new_height > 0))
        {
            // Update the rectangle.
            Canvas.SetLeft(rectangle1, new_x);
            Canvas.SetTop(rectangle1, new_y);
            rectangle1.Width = new_width;
            rectangle1.Height = new_height;

            // Save the mouse's new location.
            LastPoint = point;
        }
    }
    else
    {
        MouseHitType = SetHitType(rectangle1,
            Mouse.GetPosition(canvas1));
        SetMouseCursor();
    }
}

This code does one of two things depending on whether a drag is in progress.

If a drag is not in progress, the method calls SetHitType to see whether the mouse is over the rectangle and SetMouseCursor to display an appropriate cursor.

If a drag is in progress, the code gets the mouse's current position and subtracts its coordinates from LastPoint to see how far the mouse has moved since that point was recorded. It gets the rectangle's current position, and then initializes variables to hold the rectangle's new size and position.

Then depending on the part of the rectangle that was under the mouse, the method updates the rectangle's new size and position.

The method ensures that the rectangle's new width and height aren't less than 0, and it updates the rectangle's size and position. Finally the code saves the mouse's current location for next time.

The following code shows the last piece of the program.

// Stop dragging.
private void canvas1_MouseUp(object sender, MouseButtonEventArgs e)
{
    DragInProgress = false;
}

This code simply stops the drag.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in controls, graphics, wpf, XAML and tagged , , , , , , , , , , , . Bookmark the permalink.

24 Responses to Let the user move and resize a rectangle in WPF and C#

  1. Nathan says:

    Love your work… again!

  2. Sinan says:

    I need to get a good sample for blinking Label in WPF, have you any article for that subject?

  3. Evan Silverstein says:

    I just wanted to thank you for posting this. I was trying to figure out how to do this exact same thing (except I needed the user to draw the rectangle first) and this helped me out immensely. As a novice programmer, I have been struggling with trying to figure this out for a long time since I had to start from scratch but your code was so simple it allowed me to incorporate it into what I am doing very easily.

    Thanks again.

  4. Disha says:

    Sir, I need to move a small rectangle box over a curved surface in c# in visual studio 2008. Am new to C#. Actually that box would be a train and the curved path a track.

  5. Soleil says:

    Hello,
    thanks and good job, It was a good inspiration; though I factorized the code and I put the events over the rectangle, so I can build a custom control and edit several rectangle without all the code behind mess.

    Also a little tip: capture the mouse on the rectangle so when we move the mouse fast and close to the edges, we don’t loose the mouse resizing/moving with:
    Mouse.Capture(sender as UIElement);

    in the OnClick delegate and
    Mouse.Capture(null);

    in the OnLeftButtonUp delegate. Also I used
    MouseDevice.SetCursor(desiredCursor);

    in SetMouseCursor(object sender, MouseEventArgs e) so I can reuse this in a class.

    Best

  6. Alexander says:

    Hello,

    great solution !
    Thanks a lot for offering your work.

    I´m trying to use your UserControl as Selsction or image processing, to adjust the ROI (Region of Interest) of an picture.

    The problem I have is, that i would like to overlay the user control over the Live-view of the camer.

    i can´t set the Background to Transparent respectively it will not disapear if I do it.

    Do you have any hint for me?

    Thanks a lot.

    Regards,
    Alexander

    • RodStephens says:

      Sorry but I don’t have any experience with working on live views. I could imagine the controls not working they way you want them to.

      However, you could try making a brush out of the live view and then working with a rectangle filled with the brush. See the SaveControlImage method described in this post:

      Save WPF control images in C#

      I hope that helps.

    • Evan Silverstein says:

      Hi there. I was actually able to implement exactly what you are asking for the project I am working on. I didn’t have to mess with transparency for the rectangle or image. Rather, in the XAML, I created a canvas that referenced the MouseDown, MouseMove, MouseUp commands and then added an image to that canvas which would be displaying the current frame from the camera. In doing so, the rectangle is always on top of the frame.

      I’m a bit of an amateur with C# so I hope that I conveyed my process correctly.

  7. Danilo says:

    I’m having trouble with this code, I need to leave the transparent canvas, but it lose resize.
    How to solve this?

    Thanks!

  8. Ravi says:

    i am not able to run this code. while running i am getting some thick four sided dictional cursor. i am not able to drag also…

  9. Harsha says:

    I want a similar functionality for textbox I want to move drag and resize textbox but its not working can you please help

  10. Milan says:

    Thank you a lot, im making a game maker in visual c#, this was just the thing i was looking for!

  11. Prashant says:

    Hi,

    This is perfect code for resizing the control with move action, but here use static rectangle with name “rectangle1” that used by you in code statically. But I am looking for something dynamically i.e we can able to add multiple control rectangle or whatever dynamically and able to move and resize it by selection.

    Do you have any solution or reference for the same?

  12. Pingback: Move and resize multiple rectangles in WPF and C# - C# HelperC# Helper

Leave a Reply

Your email address will not be published. Required fields are marked *