Title: Let the user move and resize a rectangle in WPF and C#
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 the example to experiment with it and to see additional details.
|