WPF中Canvas和InkCanvas

2023-07-11 14:08:20 浏览数 (2)

前言

WPF中绘图有两种方式CanvasInkCanvas

  • Canvas需要完全由自己实现。
  • InkCanvas已经默认为我们实现了基本的绘制,同时效果也比较好。

InkCanvas

推荐使用InkCanvas,使用它绘制线的时候会自动优化转折的地方,会变得平滑。

InkCanvas本身已经支持使用鼠标或者触屏来画线,下面的示例是使用代码进行画线。

画线

如下在BlackboardCanvas中绘制一条直线

代码语言:javascript复制
private void DrawTest()
{
    // 创建一条直线
    List<Point> pointList = new List<Point>();
    pointList.Add(new Point(50, 50));
    pointList.Add(new Point(200, 200));
    StylusPointCollection points = new StylusPointCollection(pointList);
    Stroke stroke = new Stroke(points);

    // 设置笔刷属性
    DrawingAttributes drawingAttributes = new DrawingAttributes
    {
        Color = Colors.Red,
        Width = 3
    };
    stroke.DrawingAttributes = drawingAttributes;

    // 添加到InkCanvas的Strokes集合中
    BlackboardCanvas.Strokes.Add(stroke);
}

我们也可以在Stroke中添加新的点

代码语言:javascript复制
stroke.StylusPoints.Add(new StylusPoint(300, 200));

鼠标事件绘制

在实际绘制中我们可以在鼠标按下时添加对象

代码语言:javascript复制
Stroke stroke = new Stroke(new StylusPointCollection(new[] { new Point(100, 100) }));

// 设置笔刷属性
DrawingAttributes drawingAttributes = new DrawingAttributes
{
    Color = Colors.Red,
    Width = 3
};
stroke.DrawingAttributes = drawingAttributes;

// 添加到InkCanvas的Strokes集合中
BlackboardCanvas.Strokes.Add(stroke);

鼠标移动时添加点

代码语言:javascript复制
BlackboardCanvas.Strokes.Last().StylusPoints.Add(new StylusPoint(300, 200));

示例代码

代码语言:javascript复制
public void MouseDown(double x, double y)
{
    Stroke stroke = new Stroke(new StylusPointCollection(new[] { new Point(x, y) }));

    // 设置笔刷属性
    DrawingAttributes drawingAttributes = new DrawingAttributes
    {
        Color = _pencolor,
        Width = _pensize
    };
    stroke.DrawingAttributes = drawingAttributes;

    // 添加到InkCanvas的Strokes集合中
    _mCanvas.Strokes.Add(stroke);
}

public void MouseMove(double x, double y)
{
    _mCanvas.Strokes.Last().StylusPoints.Add(new StylusPoint(x, y));
}

鼠标事件

代码语言:javascript复制
_mCanvas.MouseMove  = Canvas_MouseMove;
_mCanvas.AddHandler
(
    UIElement.MouseLeftButtonDownEvent,
    new MouseButtonEventHandler(Canvas_MouseDown),
    true
);
_mCanvas.AddHandler
(
    UIElement.MouseLeftButtonUpEvent,
    new MouseButtonEventHandler(Canvas_MouseUp),
    true
);

private bool _isMouseDown;
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
    _isMouseDown = true;
    var position = e.GetPosition(_mCanvas);
    Console.WriteLine($@"X:{position.X} Y:{position.Y}");
}

private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
    if (_isMouseDown)
    {
        var position = e.GetPosition(_mCanvas);
        Console.WriteLine($@"X:{position.X} Y:{position.Y}");
    }
}

private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{
    _isMouseDown = false;
    var position = e.GetPosition(_mCanvas);
    Console.WriteLine($@"X:{position.X} Y:{position.Y}");
}

注意

MouseDownMouseUp事件无法正常工作,因为它是由InkCanvas处理的,并且没有被冒泡。

解决方法有两种

  1. 我们可以使用PreviewMouseDown/PreviewMouseLeftButtonDownPreviewMouseUp/PreviewMouseLeftButtonUp来代替,因为他们是隧道事件,并且在冒泡事件之前首先运行。
  2. 使用AddHandler

推荐

建议使用使用AddHandler,因为PreviewMouseUp实际是在事件执行之前触发,本来我们要在这个事件中要保存已绘制的笔迹,但是实际上会少了最后的一笔,因为最后一笔的绘制还没执行。

方式1

代码语言:javascript复制
_mCanvas.PreviewMouseLeftButtonDown  = Canvas_MouseDown;
_mCanvas.MouseMove  = Canvas_MouseMove;
_mCanvas.PreviewMouseLeftButtonUp  = Canvas_MouseUp;

方式2

使用AddHandler.aspx):

代码语言:javascript复制
_mCanvas.MouseMove  = Canvas_MouseMove;
_mCanvas.AddHandler
(
    UIElement.MouseLeftButtonDownEvent,
    new MouseButtonEventHandler(Canvas_MouseDown),
    true
);
_mCanvas.AddHandler
(
    UIElement.MouseLeftButtonUpEvent,
    new MouseButtonEventHandler(Canvas_MouseUp),
    true
);

工具类

代码语言:javascript复制
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using Z.Utils.Common;

namespace SchoolClient.Utils
{
    using System;

    public interface IMouseEvent
    {
        void CanvasMouseDown(double x, double y);
        void CanvasMouseMove(double x, double y);
        void CanvasMouseUp(double x, double y);
    }

    internal enum ZPenType : byte
    {
        Pen = 1,
        Erase = 2
    };

    internal class ZbbPage
    {
        public List<ZbbPageStep> Lines { get; set; }
        public List<ZbbPageStep> LinesHistoty { get; set; }

        public ZbbPage()
        {
            Lines = new List<ZbbPageStep>();
            LinesHistoty = new List<ZbbPageStep>();
        }
    }

    internal class ZbbPageStep
    {
        public StrokeCollection LinesCurr { get; set; }
        public StrokeCollection LinesAdd { get; set; }
        public StrokeCollection LinesRemove { get; set; }

        public ZbbPageStep()
        {
            LinesCurr = new StrokeCollection();
            LinesAdd = new StrokeCollection();
            LinesRemove = new StrokeCollection();
        }
    }

    public class ZjBlackboardNew
    {
        private InkCanvas _mCanvas;

        private IMouseEvent _mouseEvent;

        //private ZPenType type = ZPenType.Pen;
        private int _pagenum;

        private const int ERASESIZE = 64;
        private int _pensize = 3;
        private int _undoOrRedo; //是否在进行撤销恢复操作
        private Color _pencolor;

        private readonly List<ZbbPage> _strokesPageAll = new List<ZbbPage>();

        // 添加这个变量是因为在用橡皮擦时 一次操作会触发多次StrokesChanged回掉 这里是把多次回掉合并在一起
        private ZbbPageStep _step;

        public ZjBlackboardNew(InkCanvas canvas)
        {
            Init(canvas, Colors.White);
        }

        public void SetMouseEvent(IMouseEvent mEvent)
        {
            _mouseEvent = mEvent;
        }

        public ZjBlackboardNew(InkCanvas canvas, Color pencolor)
        {
            Init(canvas, pencolor);
        }

        private void Init(InkCanvas canvas, Color pencolor)
        {
            _mCanvas = canvas;
            _pencolor = pencolor;
            ZbbPage page = new ZbbPage();
            page.Lines.Add(new ZbbPageStep());
            _strokesPageAll.Add(page);
            if (_mCanvas != null)
            {
                _mCanvas.EditingMode = InkCanvasEditingMode.Ink;
                _mCanvas.UseCustomCursor = true;
                _mCanvas.Cursor = Cursors.Pen;
                DrawingAttributes drawingAttributes = new DrawingAttributes();
                _mCanvas.DefaultDrawingAttributes = drawingAttributes;
                drawingAttributes.Width = _pensize;
                drawingAttributes.Height = _pensize;
                drawingAttributes.Color = _pencolor;
                drawingAttributes.FitToCurve = true;
                drawingAttributes.IgnorePressure = false;
                _mCanvas.Strokes.StrokesChanged  = Strokes_StrokesChanged;
                _mCanvas.StrokeCollected  = Canvas_StrokeCollected;
                _mCanvas.StrokeErasing  = Canvas_StrokeErasing;
                _mCanvas.StrokeErased  = Canvas_StrokeErased;
                _mCanvas.MouseMove  = Canvas_MouseMove;
                _mCanvas.AddHandler
                (
                    UIElement.MouseLeftButtonDownEvent,
                    new MouseButtonEventHandler(Canvas_MouseDown),
                    true
                );
                _mCanvas.AddHandler
                (
                    UIElement.MouseLeftButtonUpEvent,
                    new MouseButtonEventHandler(Canvas_MouseUp),
                    true
                );
            }
        }

        private void Canvas_StrokeErasing(object sender, InkCanvasStrokeErasingEventArgs e)
        {
            _undoOrRedo = 0;
        }

        private void Canvas_StrokeErased(object sender, RoutedEventArgs e)
        {
        }

        private void Canvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
        {
        }

        private bool _isMouseDown;

        private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            _isMouseDown = true;
            var position = e.GetPosition(_mCanvas);
            if (_mouseEvent != null)
            {
                _mouseEvent.CanvasMouseDown(position.X, position.Y);
            }
        }

        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isMouseDown)
            {
                var position = e.GetPosition(_mCanvas);
                if (_mouseEvent != null)
                {
                    _mouseEvent.CanvasMouseMove(position.X, position.Y);
                }
            }
        }

        private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (_isMouseDown)
            {
                _isMouseDown = false;
                var position = e.GetPosition(_mCanvas);
                if (_mouseEvent != null)
                {
                    _mouseEvent.CanvasMouseUp(position.X, position.Y);
                }
            }
            if (_step != null)
            {
                ZbbPage page = _strokesPageAll[_pagenum];
                if (page != null)
                {
                    _step.LinesCurr.Add(_mCanvas.Strokes);
                    page.Lines.Add(_step);
                    _step = null;
                }
            }
        }

        private void Strokes_StrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
        {
            if (_undoOrRedo > 0)
            {
                _undoOrRedo -= 1;
                return;
            }

            if (_step == null)
            {
                _step = new ZbbPageStep();
            }

            // 笔模式
            if (e.Added.Count > 0 && e.Removed.Count == 0)
            {
                _step.LinesAdd.Add(e.Added);
            }
            // 橡皮模式 会多次进入回掉
            else if (e.Removed.Count > 0)
            {
                _step.LinesAdd.Add(e.Added);
                for (int i = 0; i < e.Removed.Count; i  )
                {
                    Stroke removeItem = e.Removed[i];
                    try
                    {
                        if (_step.LinesAdd.Contains(removeItem))
                        {
                            _step.LinesAdd.Remove(removeItem);
                        }
                        else
                        {
                            _step.LinesRemove.Add(removeItem);
                        }
                    }
                    catch (Exception ex)
                    {
                        ZLogHelper.WriteErrLog("【笔迹添加移除事件】(Strokes_StrokesChanged)"   ex.Message, ex);
                    }
                }
            }
        }

        // public方法 笔
        public void change_pen(Color color, int size)
        {
            _pencolor = color;
            _pensize = size;
            //this.type = ZPenType.Pen;
            DrawingAttributes drawingAttributes = new DrawingAttributes();
            _mCanvas.DefaultDrawingAttributes = drawingAttributes;
            drawingAttributes.Color = color;
            drawingAttributes.Width = size;
            drawingAttributes.Height = size;
            drawingAttributes.FitToCurve = true;
            drawingAttributes.IgnorePressure = false;
            _mCanvas.EditingMode = InkCanvasEditingMode.Ink;
            _mCanvas.UseCustomCursor = true;
            _mCanvas.Cursor = Cursors.Pen;
        }

        // 橡皮
        public void change_erase()
        {
            //this.type = ZPenType.Erase;
            _mCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
            _mCanvas.UseCustomCursor = false;
            _mCanvas.EraserShape = new EllipseStylusShape
            (
                ERASESIZE,
                ERASESIZE,
                0
            );
        }

        // 撤销
        public void Undo()
        {
            ZbbPage page = _strokesPageAll[_pagenum];

            if (page != null && _mCanvas.Strokes.Count > 0 && page.Lines.Count > 1)
            {
                ZbbPageStep last = page.Lines.Last();
                page.Lines.Remove(last);
                page.LinesHistoty.Add(last);
                if (page.Lines.Last().LinesCurr.Count > 0)
                {
                    _undoOrRedo = 2;
                }
                else
                {
                    _undoOrRedo = 1;
                }

                _mCanvas.Strokes.Clear();
                _mCanvas.Strokes.Add(page.Lines.Last().LinesCurr);
            }
        }

        // 恢复
        public void Redo()
        {
            ZbbPage page = _strokesPageAll[_pagenum];
            if (page != null && page.LinesHistoty.Count > 0)
            {
                ZbbPageStep line = page.LinesHistoty[page.LinesHistoty.Count - 1];

                page.Lines.Add(line);
                page.LinesHistoty.Remove(line);
                _undoOrRedo = page.Lines.Last().LinesCurr.Count > 0 ? 2 : 1;
                _mCanvas.Strokes.Clear();
                _mCanvas.Strokes.Add(page.Lines.Last().LinesCurr);
            }
        }

        // 清空
        public void Clear()
        {
            ZbbPage page = _strokesPageAll[_pagenum];
            int num = page.Lines.Count;
            for (int i = 0; i < num; i  )
            {
                page = _strokesPageAll[_pagenum];
                if (page != null && _mCanvas.Strokes.Count > 0 && page.Lines.Count > 1)
                {
                    ZbbPageStep last = page.Lines.Last();
                    page.Lines.Remove(last);
                    page.LinesHistoty.Add(last);
                    if (page.Lines.Last().LinesCurr.Count > 0)
                    {
                        _undoOrRedo = 2;
                    }
                    else
                    {
                        _undoOrRedo = 1;
                    }

                    _mCanvas.Strokes.Clear();
                    _mCanvas.Strokes.Add(page.Lines.Last().LinesCurr);
                }
            }
        }

        public void Changepage(int mpagenum)
        {
            if (_pagenum != mpagenum)
            {
                _pagenum = mpagenum;
                if (_pagenum >= _strokesPageAll.Count)
                {
                    int numadd = _pagenum - _strokesPageAll.Count   1;
                    for (int i = 0; i < numadd; i  )
                    {
                        ZbbPage pagetemp = new ZbbPage();
                        pagetemp.Lines.Add(new ZbbPageStep());
                        _strokesPageAll.Add(pagetemp);
                    }
                }

                ZbbPage page = _strokesPageAll[_pagenum];
                if (page != null)
                {
                    if (page.Lines.Last().LinesCurr.Count > 0)
                    {
                        _undoOrRedo  = 1;
                    }

                    if (_mCanvas.Strokes.Count > 0)
                    {
                        _undoOrRedo  = 1;
                        _mCanvas.Strokes.Clear();
                    }

                    _mCanvas.Strokes.Add(page.Lines.Last().LinesCurr);
                }
            }
        }

        public void MouseDown(double x, double y)
        {
            Stroke stroke = new Stroke(new StylusPointCollection(new[] { new Point(x, y) }));

            // 设置笔刷属性
            DrawingAttributes drawingAttributes = new DrawingAttributes
            {
                Color = _pencolor,
                Width = _pensize
            };
            stroke.DrawingAttributes = drawingAttributes;

            // 添加到InkCanvas的Strokes集合中
            _mCanvas.Strokes.Add(stroke);
        }

        public void MouseMove(double x, double y)
        {
            _mCanvas.Strokes.Last().StylusPoints.Add(new StylusPoint(x, y));
        }

        public void MouseUp(double x, double y)
        {
            if (_step != null)
            {
                ZbbPage page = _strokesPageAll[_pagenum];
                if (page != null)
                {
                    _step.LinesCurr.Add(_mCanvas.Strokes);
                    page.Lines.Add(_step);
                    _step = null;
                }
            }
        }
    }
}

Canvas

工具类

代码语言:javascript复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Resources;
using System.Windows.Shapes;

namespace SchoolClient.Utils
{
    internal enum PenType : byte
    {
        Pen = 1,
        Erase = 2
    };

    internal class ZjbbPage
    {
        public List<Polyline> polylines { get; set; }
        public List<Polyline> undoHistory { get; set; }

        public ZjbbPage()
        {
            polylines = new List<Polyline>();
            undoHistory = new List<Polyline>();
        }
    }

    public class ZjBlackboard
    {
        private readonly Canvas _mCanvas;
        private readonly Image _mEraseImg;
        private PenType _type = PenType.Pen;
        private int _page = 0;
        private int _erasesize = 32;

        private readonly TextBox _mTextbox;

        // 只在mouse事件时生效
        private bool _sketching;

        // 定义下面的变量来解决在部分电脑上touch同时也会触发mouse事件
        private bool _istouch = false;

        private bool _ismouse;

        private readonly List<ZjbbPage> _strokesPageAll = new List<ZjbbPage>();

        public ZjBlackboard
        (
            Canvas canvas,
            Image image,
            TextBox textbox
        )
        {
            _mCanvas = canvas;
            _mEraseImg = image;
            _mTextbox = textbox;
            _strokesPageAll.Add(new ZjbbPage());
            if (canvas != null)
            {
                Panel.SetZIndex(_mEraseImg, int.MaxValue);
                canvas.MouseLeftButtonDown  = m_mousedown;
                canvas.MouseMove  = Mousemove;
                canvas.MouseLeftButtonUp  = m_mouseup;
            }
        }

        private void SetCursor()
        {
            if (_type == PenType.Pen)
            {
                _mCanvas.Cursor = Cursors.Arrow;
            }
            else
            {
                StreamResourceInfo sri = Application.GetResourceStream(new Uri(@"curerase.cur", UriKind.Relative));
                if (sri != null)
                {
                    _mCanvas.Cursor = new Cursor(sri.Stream);
                }
            }
        }

        private void m_mousedown(object sender, MouseEventArgs e)
        {
            if (!_istouch)
            {
                _ismouse = true;
                if (_ismouse)
                {
                    if (_mTextbox != null)
                    {
                        _mTextbox.Text  = "mousedown"   (e.GetPosition(_mCanvas).ToString())   "n";
                    }
                    if (!_sketching)
                    {
                        Polyline curvePolyline = new Polyline();
                        if (_type == PenType.Pen)
                        {
                            curvePolyline.Stroke = Brushes.White;
                            curvePolyline.StrokeThickness = 2;
                        }
                        else
                        {
                            curvePolyline.Stroke = _mCanvas.Background;
                            curvePolyline.StrokeThickness = _erasesize;
                        }
                        curvePolyline.StrokeLineJoin = PenLineJoin.Round;
                        PointCollection points = new PointCollection { e.GetPosition(_mCanvas) };
                        curvePolyline.Points = points;
                        _strokesPageAll[_page].polylines.Add(curvePolyline);
                        _mCanvas.Children.Add(curvePolyline);
                        _sketching = true;
                    }
                }
            }
        }

        private void Mousemove(object sender, MouseEventArgs e)
        {
            Point point = e.GetPosition(_mCanvas);
            _mEraseImg.SetValue(Canvas.LeftProperty, point.X);
            _mEraseImg.SetValue(Canvas.TopProperty, point.Y);
            if (_mEraseImg != null)
            {
            }
            if (!_istouch)
            {
                if (_ismouse)
                {
                    if (_mTextbox != null)
                    {
                        _mTextbox.Text  = "mousemove"   (e.GetPosition(_mCanvas).ToString())   "n";
                    }
                    if (_sketching)
                    {
                        if (_strokesPageAll[_page].polylines.Count > 0)
                        {
                            Polyline curvePolyline = _strokesPageAll[_page].polylines.Last();
                            curvePolyline.Points.Add(point);
                        }
                    }
                }
            }
        }

        private void m_mouseup(object sender, MouseEventArgs e)
        {
            if (!_istouch)
            {
                if (_ismouse)
                {
                    if (_mTextbox != null)
                    {
                        _mTextbox.Text  = "mouseup"   (e.GetPosition(_mCanvas).ToString())   "n";
                    }
                    _sketching = false;
                    _ismouse = false;
                }
            }
        }

        // public方法

        // 笔
        public void change_pen()
        {
            _type = PenType.Pen;
            SetCursor();
        }

        // 橡皮
        public void change_erase()
        {
            _type = PenType.Erase;
            SetCursor();
        }

        // 撤销
        public void Undo()
        {
            ZjbbPage zjbbPage = _strokesPageAll[_page];
            if (zjbbPage != null && zjbbPage.polylines.Count > 0)
            {
                Polyline lastline = zjbbPage.polylines.Last();
                zjbbPage.polylines.Remove(lastline);
                zjbbPage.undoHistory.Add(lastline);
                _mCanvas.Children.Remove(lastline);
            }
        }

        // 恢复
        public void Redo()
        {
            ZjbbPage page = _strokesPageAll[_page];
            if (page != null && page.undoHistory.Count > 0)
            {
                Polyline lastline = page.undoHistory.Last();
                page.polylines.Add(lastline);
                page.undoHistory.Remove(lastline);
                _mCanvas.Children.Add(lastline);
            }
        }

        // 清空
        public void Clear()
        {
            ZjbbPage page = _strokesPageAll[_page];
            if (page != null)
            {
                page.polylines.Clear();
                page.undoHistory.Clear();
                _mCanvas.Children.Clear();
            }
        }
    }
}

0 人点赞