本文接上一篇WPF源码浏览 -- InkCanvas抉择模式,本文介绍笔迹的抉择过程及选中后的高亮显示办法,文中若有了解谬误的中央,欢送大家斧正。抉择成果如下图所示:
InkCanvas是WPF中用于墨迹书写的控件,其具备书写、抉择、擦除等模式。依据上图,能够看出笔迹的抉择性能由如下三局部组成:
- 抉择笔迹(Lasso Stroke)
- 动静抉择
- 选中后高亮显示
本文将首先介绍抉择模式的激活过程,而后介绍如上三局部内容WPF是如何实现的。
抉择模式的激活
从图中能够看出,切换到抉择模式后,鼠标按下挪动绘制的成果为黄色点状虚线(Lasso),依据Lasso及肯定的算法进行笔迹的选中与勾销选中。
先看InkCanvas切换到抉择模式后的动作。切换到抉择模式后,EditingMode扭转,调用OnEditingModeChanged办法,该办法调用RaiseEditingModeChanged办法。RaiseEditingModeChanged办法中,调用了_editingCoordinator.UpdateEditingState办法,并通过OnEditingModeChanged引发事件。
切换到EditingCoordinator类,能够看到顺次调用UpdateEditingState() -> ChangeEditingBehavior() -> PushEditingBehavior()。UpdateEditingState办法调用GetBehavior办法拿到新Behavior(SelectionEditor)。PushEditingBehavior办法会撤消之前的Behavior,并激活新的Behavior,即SelectionEditor会被激活。
切换到SelectionEditor类,在其OnActivate办法中监听了事件,在OnAdornerMouseButtonDownEvent办法中,调用了EditingCoordinator.ActivateDynamicBehavior办法激活了LassoSelectionBehavior。至此,抉择模式已激活,并将随着设施挪动绘制Lasso。
// InkCanvasprivate static void OnEditingModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){ ( (InkCanvas)d ).RaiseEditingModeChanged( new RoutedEventArgs( InkCanvas.EditingModeChangedEvent, d));}private void RaiseEditingModeChanged(RoutedEventArgs e){ Debug.Assert(e != null, "EventArg can not be null"); _editingCoordinator.UpdateEditingState(false /* EditingMode */); this.OnEditingModeChanged(e);}// EditingCoordinatorinternal void UpdateEditingState(bool inverted){ // ... EditingBehavior newBehavior = GetBehavior(ActiveEditingMode); ... ChangeEditingBehavior(newBehavior); // ...}private void ChangeEditingBehavior(EditingBehavior newBehavior){ // ... PushEditingBehavior(newBehavior); // ...}private void PushEditingBehavior(EditingBehavior newEditingBehavior){ // ... behavior.Deactivate(); ... newEditingBehavior.Activate();}// SelectionEditorprivate void OnAdornerMouseButtonDownEvent(object sender, MouseButtonEventArgs args){ // ... EditingCoordinator.ActivateDynamicBehavior(EditingCoordinator.LassoSelectionBehavior, args.StylusDevice != null ? args.StylusDevice : args.Device);}
抉择笔迹(Lasso Stroke)
LassoSelectionBehavior继承自StylusEditingBehavior,随着设施的挪动,会调用AddStylusPoints办法。该办法会调用StylusInputBegin、StylusInputContinue办法。StylusInputBegin会调用StartLasso办法,该办法创立了LassoHelper对象,该对象将绘制Lasso。
// StylusEditingBehaviorvoid IStylusEditing.AddStylusPoints(StylusPointCollection stylusPoints, bool userInitiated){ // ... if ( !EditingCoordinator.UserIsEditing ) { EditingCoordinator.UserIsEditing = true; StylusInputBegin(stylusPoints, userInitiated); } else StylusInputContinue(stylusPoints, userInitiated);}// LassoSelectionBehaviorprivate void StartLasso(List<Point> points){ // ... _lassoHelper = new LassoHelper(); // ...}// LassoHelperpublic const double MinDistanceSquared = 49.0;const double DotRadius = 2.5;const double DotCircumferenceThickness = 0.5;const double ConnectLineThickness = 0.75;const double ConnectLineOpacity = 0.75;static readonly Color DotColor = Colors.Orange;static readonly Color DotCircumferenceColor = Colors.White;private void AddLassoPoint(Point lassoPoint){ // ... dc.DrawEllipse(_brush, _pen, lassoPoint, DotRadius, DotRadius); // ...}
动静抉择
LassoSelectionBehavior中有一个IncrementalLassoHitTester对象,该对象实现Lasso的hit-testing。入选中的笔迹有变动时,会引发其SelectionChanged事件,该事件参数LassoSelectionChangedEventArgs蕴含SelectedStrokes汇合(动静选中的笔迹)与DeselectedStrokes汇合(动静勾销选中的笔迹)。该事件响应函数调用InkCanvas的UpdateDynamicSelection办法,进而实现笔迹的动静选中与勾销选中。
// LassoSelectionBehaviorprivate void StartLasso(List<Point> points){ // ... _incrementalLassoHitTester = this.InkCanvas.Strokes.GetIncrementalLassoHitTester(_percentIntersectForInk); _incrementalLassoHitTester.SelectionChanged += new LassoSelectionChangedEventHandler(OnSelectionChanged); // ... if (0 != lassoPoints.Length) _incrementalLassoHitTester.AddPoints(lassoPoints); // ...}private void OnSelectionChanged(object sender, LassoSelectionChangedEventArgs e){ this.InkCanvas.UpdateDynamicSelection(e.SelectedStrokes, e.DeselectedStrokes);}// InkCanvasinternal void UpdateDynamicSelection(StrokeCollection strokesToDynamicallySelect, StrokeCollection strokesToDynamicallyUnselect){ // ... if (strokesToDynamicallySelect != null) { foreach (Stroke s in strokesToDynamicallySelect) { _dynamicallySelectedStrokes.Add(s); s.IsSelected = true; } } if (strokesToDynamicallyUnselect != null) { foreach (Stroke s in strokesToDynamicallyUnselect) { System.Diagnostics.Debug.Assert(_dynamicallySelectedStrokes.Contains(s)); _dynamicallySelectedStrokes.Remove(s); s.IsSelected = false; } }}
选中高亮
从上文中能够看出,选中后的笔迹其IsSelected属性为true。间接看Stroke的DrawCore办法,如下代码中的_drawAsHollow的值由IsSelected决定。能够看出WPF绘制笔迹高亮的形式其实是绘制了两次Geometry,先绘制宽点的,再绘制窄点的,两个Geometry重叠后就造成了高亮成果。依据WPF正文形容,采纳这种办法的效率是GetOutlinePathGeometry办法的五倍。
protected virtual void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes){ //... if (_drawAsHollow == true) { Matrix innerTransform, outerTransform; DrawingAttributes selectedDA = drawingAttributes.Clone(); selectedDA.Height = Math.Max(selectedDA.Height, DrawingAttributes.DefaultHeight); selectedDA.Width = Math.Max(selectedDA.Width, DrawingAttributes.DefaultWidth); CalcHollowTransforms(selectedDA, out innerTransform, out outerTransform); selectedDA.StylusTipTransform = outerTransform; SolidColorBrush brush = new SolidColorBrush(drawingAttributes.Color); brush.Freeze(); drawingContext.DrawGeometry(brush, null, GetGeometry(selectedDA)); selectedDA.StylusTipTransform = innerTransform; drawingContext.DrawGeometry(Brushes.White, null, GetGeometry(selectedDA)); } // ...}