本文接上一篇WPF源码浏览 -- InkCanvas抉择模式,本文介绍笔迹的抉择过程及选中后的高亮显示办法,文中若有了解谬误的中央,欢送大家斧正。抉择成果如下图所示:

InkCanvas是WPF中用于墨迹书写的控件,其具备书写、抉择、擦除等模式。依据上图,能够看出笔迹的抉择性能由如下三局部组成:

  1. 抉择笔迹(Lasso Stroke)
  2. 动静抉择
  3. 选中后高亮显示

本文将首先介绍抉择模式的激活过程,而后介绍如上三局部内容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));    }    // ...}