这是个简单的支持多点触摸的画板控件, 绘制功能基于WPF InkCanvas,也是我drawTool系列文章的开篇。
阅读该文章后可能产生一些问题:
1. 如果对生成的笔迹对象进行控制
如果要对生成的stroke笔迹进行控制,这里需要单独用一个基于UIElement的对象关联到笔迹对象,例如Polyline元素的points绑定到stroke的点集合,这样对笔记的对象控制就转化为对UIlement对象的控制了
2. 如何给笔迹对象添加控制边框
在1的基础上给对象添加边框其实就变成给UIElement对象添加边框了,在WPF中可以使用装饰器来实现,包括对对象的控制,例如旋转,拉伸等等
ps:1,2问题会在后面文章实现~ 上个简单的图~
/// PenType = 画笔类型,支持普通画笔,荧光笔,点擦除,后面我扩充到支持图像笔,纹理笔以及flash笔,至于排笔的实现其实只要上何止width和height不同就可以了 /// PenColor = 画笔的颜色 /// EraseWidth = 橡皮擦粗细
直接上源码,代码理解起来还是很简单的(MultiTouchCanvas)
/// <summary> /// 支持多点触摸的InkCanvas /// </summary public class MultiTouchCanvas : FrameworkElement { private InkCanvasProxy _inkCanvas = new InkCanvasProxy(); private Grid transparentOverlay = new Grid(); private StrokeType _strokeType = StrokeType.Stroke; private Dictionary<object, StrokeCollection> _strokes = new Dictionary<object, StrokeCollection>(); private Dictionary<object, Stroke> _currentStroke = new Dictionary<object, Stroke>(); private Color _penColor = Colors.Green; public Color PenColor{ get{ return this._inkCanvas.DefaultDrawingAttributes.Color; } set{ this._penColor = value; this._inkCanvas.DefaultDrawingAttributes.Color = value; if (this.PenType == StrokeType.HighlighterStroke) { value.ScA = System.Convert.ToSingle(0.5); this._inkCanvas.DefaultDrawingAttributes.Color = value; } } } private double _penWidth = 4.0; public double PenWidth { get { return this._penWidth; } set { this._penWidth = value; this._inkCanvas.DefaultDrawingAttributes.Width = value; this._inkCanvas.DefaultDrawingAttributes.Height = value; } } private double _eraseWidth = 30.0; public double EraseWidth { get { return this._eraseWidth; } set { this._eraseWidth = value; this._inkCanvas.DefaultDrawingAttributes.Height = value; this._inkCanvas.DefaultDrawingAttributes.Width = value; } } public StrokeType PenType { get { return this._strokeType; } set { this._strokeType = value; if (this._strokeType == StrokeType.Stroke || this._strokeType == StrokeType.HighlighterStroke) { this._inkCanvas.EditingMode = InkCanvasEditingMode.Ink; this._inkCanvas.DefaultDrawingAttributes.Width = this.PenWidth; this._inkCanvas.DefaultDrawingAttributes.Height = this.PenWidth; this._inkCanvas.DefaultDrawingAttributes.Color = this.PenColor; } if (this._strokeType == StrokeType.HighlighterStroke) { Color dColor = this.PenColor; dColor.ScA = System.Convert.ToSingle(0.5); this._inkCanvas.DefaultDrawingAttributes.Color = dColor; } if (this._strokeType == StrokeType.EraseByPoint) { this._inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint; this._inkCanvas.DefaultDrawingAttributes.Width = this.EraseWidth; this._inkCanvas.DefaultDrawingAttributes.Height = this.EraseWidth; } } } public Brush Background { get { return this._inkCanvas.Background; } set { this._inkCanvas.Background = value; } } protected override int VisualChildrenCount { get { return 2; //grid + inkcanvas } } public MultiTouchCanvas(){ base.IsManipulationEnabled = true; this.transparentOverlay.Background = Brushes.Transparent; base.IsEnabled =true; this.InitInkCanvasPropertys(); this._inkCanvas.DefaultDrawingAttributes = new DrawingAttributes(); this._inkCanvas.DefaultDrawingAttributes.Color = Colors.Green; this._inkCanvas.DefaultDrawingAttributes.Width = 4; this._inkCanvas.DefaultDrawingAttributes.Height = 4; this.PenType = StrokeType.Stroke; } public void ClearStrokes(object device) { if (this._strokes.ContainsKey(device) && this._inkCanvas.Strokes != null && this._inkCanvas.Strokes.Count > 0) { StrokeCollection sc = this._strokes[device]; this._inkCanvas.Strokes.Remove(sc); this._strokes.Remove(device); } } public StrokeCollection GetStrokes(object device) { return this._strokes.ContainsKey(device) ? this._strokes[device] : null; } #region Event handle protected override void OnPreviewTouchDown(TouchEventArgs e) { TouchPoint tp = e.GetTouchPoint(this); if (this._inkCanvas.EditingMode == InkCanvasEditingMode.Ink) { this._startStroke(e.Device, tp.Position); } else { if (this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) { this._removeStroke(e.Device, tp.Position); } } e.Handled = true; base.Focusable = true; base.Focus(); base.Focusable = false; e.TouchDevice.Capture(this); } protected override void OnPreviewTouchMove(TouchEventArgs e) { _handleTouchMove(e); } protected override void OnTouchUp(TouchEventArgs e) { e.TouchDevice.Capture(null); // } protected override void OnMouseDown(MouseButtonEventArgs e) { if (base.Visibility == System.Windows.Visibility.Visible) { if (this._inkCanvas.EditingMode == InkCanvasEditingMode.Ink) { this._startStroke(e.Device, e.GetPosition(this)); } else { if (this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) { this._removeStroke(e.Device, e.GetPosition(this)); } } e.MouseDevice.Capture(this); } } protected override void OnMouseMove(MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) { if (this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) { this._removeStroke(e.Device, e.GetPosition(this)); return; } if (this._strokes.ContainsKey(e.Device) && this._currentStroke.ContainsKey(e.Device)) { this._addPointToStroke(e.Device, e.GetPosition(this)); } else { this._startStroke(e.Device, e.GetPosition(this)); } } } protected override void OnMouseUp(MouseButtonEventArgs e) { e.MouseDevice.Capture(null); } #endregion protected override Visual GetVisualChild(int index) { switch (index) { case 0: return this._inkCanvas; case 1: return this.transparentOverlay; default: throw new ArgumentOutOfRangeException("index"); } } protected override Size MeasureOverride(Size availableSize) { this._inkCanvas.Measure(availableSize); this.transparentOverlay.Measure(availableSize); return this._inkCanvas.DesiredSize; } protected override Size ArrangeOverride(Size finalSize) { this._inkCanvas.Arrange(new Rect(finalSize)); this.transparentOverlay.Arrange(new Rect(finalSize)); return base.ArrangeOverride(finalSize); } protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); base.AddVisualChild(this._inkCanvas); base.AddVisualChild(this.transparentOverlay); } private void _handleTouchMove(TouchEventArgs e) { if(this._inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint){ this._removeStroke(e.Device , e.GetTouchPoint(this).Position); e.Handled = true; return ; } if(this._strokes.ContainsKey(e.Device) && this._currentStroke.ContainsKey(e.Device)) { Stroke stroke = this._currentStroke[e.Device]; StylusPointCollection sps = stroke.StylusPoints; if(sps != null){ TouchPoint tp = e.GetTouchPoint(this); Point p = tp.Position; this._addPointToStroke( e.Device , p); } } else { TouchPoint tp = e.GetTouchPoint(this); this._startStroke( e.Device ,tp.Position); } e.Handled = true; } private void _addPointToStroke(object device, Point position) { Stroke stroke = this._currentStroke[device]; if (stroke != null) { StylusPointCollection spc = stroke.StylusPoints; if (spc != null) { spc.Add(new StylusPoint(position.X, position.Y, 0.5f)); } } } private void _removeStroke(object device, Point position) { for (int i = 0; i < this._inkCanvas.Strokes.Count; i++) { Stroke stroke = this._inkCanvas.Strokes[i]; StrokeCollection sc = stroke.GetEraseResult(new Rect(position.X, position.Y, this._inkCanvas.DefaultDrawingAttributes.Width, this._inkCanvas.DefaultDrawingAttributes.Height)); this._inkCanvas.Strokes.Replace(stroke, sc); } } private void _startStroke(object device, Point inputPosition) { StylusPointCollection stylusPointCollection = new StylusPointCollection(); stylusPointCollection.Add(new StylusPoint(inputPosition.X, inputPosition.Y, 0.5f)); if (stylusPointCollection.Count > 0) { Stroke stroke = new Stroke(stylusPointCollection); stroke.DrawingAttributes.Width = this._inkCanvas.DefaultDrawingAttributes.Width; stroke.DrawingAttributes.Height = this._inkCanvas.DefaultDrawingAttributes.Height; stroke.DrawingAttributes.Color = this._inkCanvas.DefaultDrawingAttributes.Color; this._inkCanvas.Strokes.Add(stroke);//添加到canvas if (this._currentStroke.ContainsKey(device)) { this._currentStroke.Remove(device); } this._currentStroke.Add(device, stroke); if (this._strokes.ContainsKey(device)) { this._strokes[device].Add(this._currentStroke[device]); return; } this._strokes.Add(device, new StrokeCollection { this._currentStroke[device] }); } } private void InitInkCanvasPropertys(){ this._inkCanvas.Focusable = base.Focusable; this._inkCanvas.Background = Brushes.Transparent; this._inkCanvas.EditingMode = InkCanvasEditingMode.Ink; this._inkCanvas.UseCustomCursor = true; } }
InkCanvasProxy和StrokeType的代码
public enum StrokeType { Stroke, HighlighterStroke, EraseByPoint } public class InkCanvasProxy : InkCanvas { public InkCanvasProxy() : base() { // base.IsHitTestVisible = false; // base.StylusPlugIns.Remove(base.DynamicRenderer); } }
实例demo,调用: 新建一个WPF程序,然后在mainwindow.xaml添加
<Window x:Class="Metro.G.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="600" Width="600" xmlns:ll="clr-namespace:Metro.G"> <Canvas Name="container" > <ll:MultiTouchCanvas Width="600" Height="600" x:Name="_canvas"></ll:MultiTouchCanvas> </Canvas> </Window>
画笔的类型,颜色以及粗细通过MultiTouchCanvas的属性设置