如何让用户在 WPF 中使用画布绘制线条

Posted

技术标签:

【中文标题】如何让用户在 WPF 中使用画布绘制线条【英文标题】:How to let a user draw a line using a canvas in WPF 【发布时间】:2014-02-28 13:35:50 【问题描述】:

我正在尝试制作一个简单的图像编辑器,用户可以在其中加载图像并在其上绘制箭头、文本和矩形。

现在我有一个带有一些按钮的窗口和一个带有画布和图像的视图框。

基本上,用户会单击箭头按钮,单击画布上的某个位置,移动显示线条的鼠标,然后单击其他位置以实际绘制线条。

如何告诉画布仅在单击绘图按钮后才开始监听鼠标单击?这是我目前所拥有的。

<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>

用这个作为后面的代码:

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)
        

        
    

【问题讨论】:

【参考方案1】:

下面的代码为我提供了你可以适应为你工作的功能。

枚举是您希望用户能够绘制的所有不同形状的列表。您已实现的按钮应设置 activeShapeType 值。将绘制矩形、圆形等的方法添加到 Canvas_MouseLeftButtonDown 事件中的 switch 语句。

右键单击事件将允许用户在第二次单击之前取消他们正在绘制的线条。

    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;
    

此代码应在移动鼠标时添加临时行。

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;
            
        
    

【讨论】:

谢谢你,我最后只用了一次修改。对于画布,IsHitTestVisible 在您单击按钮之前设置为 false,并在添加形状后设置回 false 以防止过早捕获点。【参考方案2】:

我使用 InkCanvas、绘图和向画布添加符号做了类似的事情。

但是我用布尔值控制我的大部分东西,所以当你点击一个按钮时你知道你处于哪个状态。

然后,当您单击画布时,该状态用于表示好的,我处于方形/箭头状态,这就是我应该绘制的。然而。我认为您正在寻找的是

向下,向上移动功能。

当你点击画布 mousedown 时,你会得到你想要开始你的图形的位置,当你按下鼠标移动时,你可以做一个拖动动画,或者跳过它直接到你释放的地方画布。获取另一个位置,然后从这些位置画线..

x1,y1(鼠标在画布上的位置)x2,y2(鼠标在画布上)我现在真的没有时间提供一个例子,因为我正在回家的路上,但这应该不会太难,只是从您点击之前的位置到点击后您想去的地方,将其分成小步骤。

希望对您有所帮助。

【讨论】:

以上是关于如何让用户在 WPF 中使用画布绘制线条的主要内容,如果未能解决你的问题,请参考以下文章

如何在画布上动画绘制线条

如何使用画布和javascript同时绘制线条在鼠标指针顶部显示x和y坐标

如何使用 C# 在画布上的 WPF 中绘制透明 PNG

绘制画布后淡出线条?

C#wpf里面怎么绘制线条

如何让用户在Android中随意绘制长画布?