這是個簡單的支持多點觸摸的畫板控件, 繪制功能基於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的屬性設置