Domanda

I am trying to make a simple image editor where users can load an image and draw arrows, text, and rectangles on it.

Right now I have a window with some buttons and a viewbox with a canvas and an image inside it.

Basically a user would click the arrow button, click somewhere on the canvas, move the mouse which would show a line, and then click somewhere else to actually draw the line.

How do I tell the canvas to start listening for a mouse click only after the draw button is clicked? Here is what I have so far.

<Window x:Class="ImageEditor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="600" Width="800">
    <Window.CommandBindings>
        <CommandBinding Command="local:CapturePointsCommand" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute"/>
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Button Grid.Row="0" Name="_drawArrow" Height="69" Width="69" Click="_drawArrow_Click" HorizontalAlignment="Left" Margin="5,5,0,5">
            <Image Source="Media/arrow.png"/>
        </Button>
        <Button Grid.Row="0" Name="_drawBox" Height="69" Width="69" Click="_drawBox_Click" HorizontalAlignment="Left" Margin="79,5,0,5">
            <Image Source="Media/rectangle.png"/>
        </Button>
        <Button Grid.Row="0" Name="_drawText" Height="69" Width="69" Click="_drawText_Click" HorizontalAlignment="Left" Margin="153,5,0,5">
            <Image Source="Media/text.png"/>
        </Button>
        <Viewbox Grid.Row="1">
            <Canvas Name="_canvas" Height="{Binding Height, ElementName=_picture}" Width="{Binding Width, ElementName=_picture}" MouseLeftButtonUp="_canvas_MouseLeftButtonUp">
                <Image  Name="_picture" Source="{Binding Image}" Height="488" Width="800"/>
            </Canvas>
        </Viewbox>
        <Button Grid.Row="2" HorizontalAlignment="Right" Name="_Load_Button" Content="Load" Margin="0,5,5,5" Width="75" Height="23" Click="_Load_Button_Click" />
        <Button Grid.Row="2" HorizontalAlignment="Right" Name="_Save_Button" Content="Save" Margin="0,5,85,5" Width="75" Height="23" Click="_Save_Button_Click" />
        <Button Grid.Row="2" HorizontalAlignment="Right" Name="_Cancel_Button" Content="Cancel" Margin="0,5,165,5" Width="75" Height="23" Click="_Cancel_Button_Click" />
        <Button Grid.Row="2" HorizontalAlignment="Right" Name="_Reset_Button" Content="Reset" Margin="0,5,245,5" Width="75" Height="23" Click="_Reset_Button_Click" />
    </Grid>
</Window>

With this as code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms;
using System.IO;

namespace ImageEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public static readonly RoutedCommand CapturePointsCommand = new RoutedCommand();

        private ImageSource _image;

        public MainWindow()
        {
            InitializeComponent();
        }

        public ImageSource Image
        {
            get
            {
                return this._image;
            }
            set
            {
                this._image = value;
            }
        }

        private List<Point> _points = new List<Point>();

        public List<Point> Points
        {
            get
            {
                return this._points;
            }
            set
            {
                this._points = value;
            }
        }

        private void _Save_Button_Click(object sender, RoutedEventArgs e)
        {
            if (Image == null)
                System.Windows.Forms.MessageBox.Show("There is nothing to save");
            else
            {
                Image = this._picture.Source;
                this.Close();
            }
        }

        private void _Reset_Button_Click(object sender, RoutedEventArgs e)
        {
            this._picture.Source = Image;
        }

        private void _Cancel_Button_Click(object sender, RoutedEventArgs e)
        {
            Image = null;
            this._picture.Source = null;
            this.Close();
        }

        private void _Load_Button_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            string path;
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                path = ofd.FileName;
                Uri pathUri = new Uri(path);
                PngBitmapDecoder decoder = new PngBitmapDecoder(pathUri, BitmapCreateOptions.None, BitmapCacheOption.None);
                BitmapSource pathSrc = decoder.Frames[0];
                Image = pathSrc;
                this._picture.Source = Image;
            }
            else
            {
                path = null;
            }
        }

        private void _drawArrow_Click(object sender, RoutedEventArgs e)
        {
            Line line = new Line();
            line.Stroke = Brushes.Black;
            line.X1 = Points[0].X;
            line.Y1 = Points[0].Y;
            line.X2 = Points[1].X;
            line.Y2 = Points[1].Y;

            this._canvas.Children.Add(line);
        }

        private void _drawBox_Click(object sender, RoutedEventArgs e)
        {

        }

        private void _drawText_Click(object sender, RoutedEventArgs e)
        {

        }

        private void _canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Point p = Mouse.GetPosition(_canvas);
            Points.Add(p);
        }

        private void _drawArrow_MouseDown(object sender, MouseButtonEventArgs e)
        {

        }

        private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {

        }

        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {

        }
    }
}
È stato utile?

Soluzione

The code below provided the functionality for me which you can adapt to work for you.

The enumeration is a list of all the different shapes you want the user to be able to draw. The buttons you have implemented should set the activeShapeType value. Add your methods from drawing rectangles, circle, etc to the switch statement in the Canvas_MouseLeftButtonDown event.

The right click event will allow the user to cancel a line they are drawing before the second click.

    public bool IsFirstPoint = true;
    public Point StartPoint;
    public enum ShapeType
    {
        line,
        circle, 
        rectangle
    }
    public ShapeType activeShapeType = ShapeType.line;


    private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (IsFirstPoint)
        {
            StartPoint = (Mouse.GetPosition(Surface));
            IsFirstPoint = false;
        }
        else
        {
            switch (activeShapeType)
            {
                case ShapeType.line:
                    Line line = new Line() { X1 = StartPoint.X, Y1 = StartPoint.Y, X2 = Mouse.GetPosition(Surface).X, Y2 = Mouse.GetPosition(Surface).Y, Stroke = Brushes.Black };
                    Surface.Children.Add(line);
                    break;
                case ShapeType.rectangle:
                   /*Your code to draw rectangle here*/
                   break;
            }
            IsFirstPoint = true;
        }
    }

    private void Surface_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        IsFirstPoint = true;
    }

This code should add the temporary line while moving the mouse.

private void Surface_MouseMove(object sender, MouseEventArgs e)
    {
        if (!IsFirstPoint)
        {

            if (Surface.Children.Count > 0)
            {
                var child = (from c in Surface.Children.OfType<FrameworkElement>()
                             where "tempLine".Equals(c.Tag)
                             select c).First();
                if (child != null)
                {
                    Surface.Children.Remove(child);
                }
            }


            switch (activeShapeType)
            {
                case ShapeType.line:
                    Line line = new Line() { Tag="tempLine", X1 = StartPoints.X, Y1 = StartPoints.Y, X2 = Mouse.GetPosition(Surface).X, Y2 = Mouse.GetPosition(Surface).Y, Stroke = Brushes.Black };
                    Surface.Children.Add(line);                      


                    return;
            }
        }
    }

Altri suggerimenti

I did something like this using InkCanvas, drawing, and adding symbols to the canvas.

However I control most of my things with bools, so you know which state you are in when you are clicking on a button.

Then when you click on the canvas, the state is used to say okay i'm in a square/arrow state, this is what im supposed to draw. However. What i think you are looking for is the

Down, move up functionality.

When you click the canvas mousedown, you get the position of where you want to start your figure, and when you move the mouse while pressed, you could do a dragging animation, or skip that and go straight to where you release on the canvas. Get the other position, and then from those positions draw the line..

x1,y1 (mousedown position on canvas) x2,y2 (mouseup on canvas) I don't really have time to provide an example now since i'm on my way home, but it should not be tooooooo difficult, just break it up in small steps from where you are before you click to where you wanna go when you have clicked.

Hope this is of any help.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top