前言
WPF中绘图有两种方式Canvas
和InkCanvas
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}");
}
注意
MouseDown
和MouseUp
事件无法正常工作,因为它是由InkCanvas处理的,并且没有被冒泡。
解决方法有两种
- 我们可以使用
PreviewMouseDown/PreviewMouseLeftButtonDown
和PreviewMouseUp/PreviewMouseLeftButtonUp
来代替,因为他们是隧道事件,并且在冒泡事件之前首先运行。 - 使用
AddHandler
。
推荐
建议使用使用
AddHandler
,因为PreviewMouseUp
实际是在事件执行之前触发,本来我们要在这个事件中要保存已绘制的笔迹,但是实际上会少了最后的一笔,因为最后一笔的绘制还没执行。
方式1
代码语言:javascript复制_mCanvas.PreviewMouseLeftButtonDown = Canvas_MouseDown;
_mCanvas.MouseMove = Canvas_MouseMove;
_mCanvas.PreviewMouseLeftButtonUp = Canvas_MouseUp;
方式2
使用AddHandler
.aspx):
_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();
}
}
}
}