2

I am new to WPF.

I want to draw a circle on Canvas on a Mouse move event. I have already wrote logic to drag it around the canvas. But I wanted to create a circle when mouse clicks on my canvas and it should resize according to mouse move on canvas.

How can I accomplish this?

Here is my Code

    Ellipse elip = new Ellipse();
    private double pointx;
    private double pointy;        

    private void canvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        canvas.MouseMove -= MouseMove_NotDown;
        canvas.MouseMove += canvas_PreviewMouseMove;
        canvas.MouseUp += canvas_PreviewMouseUp;

        Point location = e.MouseDevice.GetPosition(canvas);
        elip = new Ellipse();
        elip.Height = 1;
        elip.Width = 1;
        elip.Stroke = Brushes.Black;
        elip.StrokeThickness = 2;
        Canvas.SetTop(elip, location.Y + 1);
        Canvas.SetLeft(elip, location.X + 1);
        pointx = location.X + 1;
        pointy = location.Y + 1;
        canvas.Children.Add(elip);
    }

    private void MouseMove_NotDown(object sender, MouseEventArgs e)
    {
        canvas.Cursor = Cursors.Hand;
    }

    private void canvas_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        try
        {
            Point location = e.MouseDevice.GetPosition(canvas);
            double height = location.Y - pointy;
            double width = location.X - pointx;
            if (height >= 0 && width >= 0)
            {
                elip.Height = height;
                elip.Width = width;
            }
            else if (height < 0 || width < 0)
            {
                if (height < 0)
                {
                    elip.Height = height + (-height) + 1;
                    elip.Width = width;
                }
                else if (width < 0)
                {
                    elip.Height = height;
                    elip.Width = width + (-width) + 1;
                }
            }
        }
        catch
        {

        }
    }

    private void canvas_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        elip.Stroke = Brushes.Black;
        elip.StrokeThickness = 2;

        canvas.MouseMove -= canvas_PreviewMouseMove;
        canvas.MouseMove += MouseMove_NotDown;
        canvas.MouseUp += canvas_PreviewMouseUp;
    }
Vinoth Kumar
  • 91
  • 1
  • 10
  • 2
    Please do post what you have tried so far. Some pointers, set your canvas Background to a Color (Transparent if you want no color), otherwise it won't get mouse click events. Then you'll be using MouseDown and MouseMove event handlers. – Tim Rutter Nov 21 '17 at 08:23
  • Thanks guys. I have updated my code please have a look. Thanks in advance. – Vinoth Kumar Nov 21 '17 at 08:28
  • 1
    a tip straight from reading that code ; all that attaching and removing of listeners means a *lot* can go wrong for something that should be this simple. try to avoid that as much as possible. – Timothy Groote Nov 21 '17 at 08:30
  • Thanks Groote. Could you please explain a little more that will help me solve my problem. I can able to draw up to 10 circles using the above code when i'm trying to draw the 11th circle it doesn't works. – Vinoth Kumar Nov 21 '17 at 08:33
  • I'll post an example in a minute. it would be easier to just attach the listeners once, when you initialize your control (or window). – Timothy Groote Nov 21 '17 at 08:35

1 Answers1

5

it's better to only attach your listeners once. this makes your application logic easier to understand and debug.

if WPF's canvas does not have a background set, it will not catch mouse events unless there is something in the canvas being clicked on, so give it a background color (white or transparent is fine)

XAML:

<Window x:Class="mausing.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:mausing"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Canvas x:Name="canvas" Background="White"></Canvas>
</Grid>

C# :

public partial class MainWindow : Window
{
    private Ellipse elip = new Ellipse();
    private Point anchorPoint;

    public MainWindow()
    {
        InitializeComponent();
        canvas.MouseMove += canvas_MouseMove;
        canvas.MouseUp += canvas_MouseUp;
        canvas.MouseDown += canvas_MouseDown;
    }

    private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
    {
        //capture the mouse on the canvas
        //(this also helps us keep track of whether or not we're drawing)
        canvas.CaptureMouse();

        anchorPoint = e.MouseDevice.GetPosition(canvas);
        elip = new Ellipse
        {
            Stroke = Brushes.Black,
            StrokeThickness = 2
        };
        canvas.Children.Add(elip);
    }

    private void canvas_MouseMove(object sender, MouseEventArgs e)
    {
        //if we are not drawing, we don't need to do anything when the mouse moves
        if (!canvas.IsMouseCaptured)
            return;

        Point location = e.MouseDevice.GetPosition(canvas);

        double minX = Math.Min(location.X, anchorPoint.X);
        double minY = Math.Min(location.Y, anchorPoint.Y);
        double maxX = Math.Max(location.X, anchorPoint.X);
        double maxY = Math.Max(location.Y, anchorPoint.Y);

        Canvas.SetTop(elip, minY);
        Canvas.SetLeft(elip, minX);

        double height = maxY - minY;
        double width = maxX - minX;

        elip.Height = Math.Abs(height);
        elip.Width = Math.Abs(width);       
    }

    private void canvas_MouseUp(object sender, MouseButtonEventArgs e)
    {
        // we are now no longer drawing
        canvas.ReleaseMouseCapture();
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
Timothy Groote
  • 8,614
  • 26
  • 52
  • @Clemens thanks, i was just about to get around to that :p – Timothy Groote Nov 21 '17 at 09:00
  • 2
    Instead of handling an `is_drawing` flag, better [capture the mouse](https://msdn.microsoft.com/en-us/library/system.windows.uielement.capturemouse(v=vs.110).aspx) and check if it [is captured](https://msdn.microsoft.com/en-us/library/system.windows.uielement.ismousecaptured(v=vs.110).aspx). [Release the capture](https://msdn.microsoft.com/en-us/library/system.windows.uielement.releasemousecapture(v=vs.110).aspx) on mouse up. – Clemens Nov 21 '17 at 09:00
  • @VinothKumar please keep in mind that the code for dragging ellipses around the canvas once you have drawn them is *not* in this example. – Timothy Groote Nov 21 '17 at 09:05
  • 1
    I may also add that instead of recalculating the top/left position of the ellipse, you may far better use a Path with an EllipseGeometry, which keeps its center, but that's perhaps out of scope here. – Clemens Nov 21 '17 at 09:08
  • 1
    @Clemens while i agree that that might be prettier, i'm not trying to re-write adobe illustrator here ;) let's have OP wrap their head around this first. – Timothy Groote Nov 21 '17 at 09:09
  • Thanks @Groote your code works fine for me. Thanks a lot. – Vinoth Kumar Nov 21 '17 at 09:23
  • 1
    As a final note, you usually attach the event handlers in XAML instead of code behind, like `` – Clemens Nov 21 '17 at 09:26