File: System\Windows\Controls\InkCanvas.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//#define DEBUG_LASSO_FEEDBACK // DO NOT LEAVE ENABLED IN CHECKED IN CODE
//
// Description:
//      Defines an inkable canvas that represents the primary api for
//      editing ink
//
 
using MS.Internal.Commands;
using MS.Internal.Controls;
using MS.Internal.Ink;
using MS.Internal.KnownBoxes;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Markup; // IAddChild, ContentPropertyAttribute
using System.Windows.Threading;
using System.Windows.Automation.Peers;
 
using BuildInfo = MS.Internal.PresentationFramework.BuildInfo;
 
namespace System.Windows.Controls
{
    /// <summary>
    /// InkCanvas is used to allow inking on a canvas
    /// </summary>
    [ContentProperty("Children")]
    public class InkCanvas : FrameworkElement, IAddChild
    {
        #region Constructors / Initialization
 
        /// <summary>
        /// The static constructor
        /// </summary>
        static InkCanvas()
        {
            Type ownerType = typeof(InkCanvas);
 
            //
            // We should add the following listener as the class handler which will be guarantied to receive the
            // notification before the handler on the instances. So we won't be trapped in the bad state due to the
            // event routing.
            // Listen to stylus events which will be redirected to the current StylusEditingBehavior
 
            //Down
            EventManager.RegisterClassHandler(ownerType, Stylus.StylusDownEvent,
                new StylusDownEventHandler(_OnDeviceDown<StylusDownEventArgs>));
            EventManager.RegisterClassHandler(ownerType, Mouse.MouseDownEvent,
                new MouseButtonEventHandler(_OnDeviceDown<MouseButtonEventArgs>));
 
            //Up
            EventManager.RegisterClassHandler(ownerType, Stylus.StylusUpEvent,
                new StylusEventHandler(_OnDeviceUp<StylusEventArgs>));
            EventManager.RegisterClassHandler(ownerType, Mouse.MouseUpEvent,
                new MouseButtonEventHandler(_OnDeviceUp<MouseButtonEventArgs>));
 
 
 
            EventManager.RegisterClassHandler(ownerType, Mouse.QueryCursorEvent,
                new QueryCursorEventHandler(_OnQueryCursor), true);
 
            // Set up the commanding handlers
            _RegisterClipboardHandlers();
            CommandHelpers.RegisterCommandHandler(ownerType, ApplicationCommands.Delete,
                new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled));
 
            CommandHelpers.RegisterCommandHandler(ownerType, ApplicationCommands.SelectAll,
                Key.A, ModifierKeys.Control, new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled));
 
            CommandHelpers.RegisterCommandHandler(ownerType, InkCanvas.DeselectCommand,
                new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled),
                KeyGesture.CreateFromResourceStrings(InkCanvasDeselectKey, nameof(SR.InkCanvasDeselectKeyDisplayString)));
 
            //
            //set our clipping
            //
            ClipToBoundsProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(BooleanBoxes.TrueBox));
 
            //
            //enable input focus
            //
            FocusableProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(BooleanBoxes.TrueBox));
 
            // The default InkCanvas style
            Style defaultStyle = new Style(ownerType);
            // The background - Window Color
            defaultStyle.Setters.Add(new Setter(InkCanvas.BackgroundProperty,
                            new DynamicResourceExtension(SystemColors.WindowBrushKey)));
            // Default InkCanvas to having flicks disabled by default.
            defaultStyle.Setters.Add(new Setter(Stylus.IsFlicksEnabledProperty, false));
            // Default InkCanvas to having tap feedback disabled by default.
            defaultStyle.Setters.Add(new Setter(Stylus.IsTapFeedbackEnabledProperty, false));
            // Default InkCanvas to having touch feedback disabled by default.
            defaultStyle.Setters.Add(new Setter(Stylus.IsTouchFeedbackEnabledProperty, false));
 
            // Set MinWidth to 350d if Width is set to Auto
            Trigger trigger = new Trigger();
            trigger.Property = WidthProperty;
            trigger.Value = double.NaN;
            Setter setter = new Setter();
            setter.Property = MinWidthProperty;
            setter.Value = 350d;
            trigger.Setters.Add(setter);
            defaultStyle.Triggers.Add(trigger);
 
            // Set MinHeight to 250d if Height is set to Auto
            trigger = new Trigger();
            trigger.Property = HeightProperty;
            trigger.Value = double.NaN;
            setter = new Setter();
            setter.Property = MinHeightProperty;
            setter.Value = 250d;
            trigger.Setters.Add(setter);
            defaultStyle.Triggers.Add(trigger);
 
            // Seal the default style
            defaultStyle.Seal();
 
            StyleProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(defaultStyle));
            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(typeof(InkCanvas)));
 
            FocusVisualStyleProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata((object)null /* default value */));
        }
 
        /// <summary>
        /// Public constructor.
        /// </summary>
        public InkCanvas() : base()
        {
            Initialize();
        }
 
        /// <summary>
        /// Private initialization method used by the constructors
        /// </summary>
        private void Initialize()
        {
            //
            // instance the DynamicRenderer and add it to the StylusPlugIns
            //
            _dynamicRenderer = new DynamicRenderer();
            _dynamicRenderer.Enabled = false;
            this.StylusPlugIns.Add(_dynamicRenderer);
 
            //
            // create and initialize an editing coordinator
            //
            _editingCoordinator = new EditingCoordinator(this);
            _editingCoordinator.UpdateActiveEditingState();
 
 
            // connect the attributes event handler after setting the stylus shape to avoid unnecessary
            //      calls into the RTI service
            DefaultDrawingAttributes.AttributeChanged += new PropertyDataChangedEventHandler(DefaultDrawingAttributes_Changed);
 
            //
            //
            // We must initialize this here (after adding DynamicRenderer to Sytlus).
            //
            this.InitializeInkObject();
 
            _rtiHighContrastCallback = new RTIHighContrastCallback(this);
 
            // Register rti high contrast callback. Then check whether we are under the high contrast already.
            HighContrastHelper.RegisterHighContrastCallback(_rtiHighContrastCallback);
            if ( SystemParameters.HighContrast )
            {
                _rtiHighContrastCallback.TurnHighContrastOn(SystemColors.WindowTextColor);
            }
        }
 
        /// <summary>
        /// Private helper used to change the Ink objects.  Used in the constructor
        /// and the Ink property.
        ///
        /// NOTE -- Caller is responsible for clearing any selection!  (We can't clear it
        ///         here because the Constructor calls this method and it would end up calling
        ///         looking like it could call a virtual method and FxCop doesn't like that!)
        ///
        /// </summary>
        private void InitializeInkObject()
        {
            // Update the RealTimeInking PlugIn for the Renderer changes.
            UpdateDynamicRenderer();
 
            // Initialize DefaultPacketDescription
            _defaultStylusPointDescription = new StylusPointDescription();
        }
        #endregion Constructors  / Initialization
 
        #region Protected Overrides
 
        /// <summary>
        /// MeasureOverride
        /// </summary>
        /// <param name="availableSize"></param>
        /// <returns></returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            // No need to invoke VerifyAccess since _localAdornerDecorator.Measure should check it.
            if ( _localAdornerDecorator == null )
            {
                ApplyTemplate();
            }
 
            _localAdornerDecorator.Measure(availableSize);
 
            return  _localAdornerDecorator.DesiredSize;
        }
 
        /// <summary>
        /// ArrangeOverride
        /// </summary>
        /// <param name="arrangeSize"></param>
        /// <returns></returns>
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            // No need to invoke VerifyAccess since _localAdornerDecorator.Arrange should check it.
 
            if ( _localAdornerDecorator == null )
            {
                ApplyTemplate();
            }
 
            _localAdornerDecorator.Arrange(new Rect(arrangeSize));
 
            return arrangeSize;
        }
 
 
        /// <summary>
        /// HitTestCore implements precise hit testing against render contents
        /// </summary>
        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParams)
        {
            VerifyAccess();
 
            Rect r = new Rect(new Point(), RenderSize);
            if (r.Contains(hitTestParams.HitPoint))
            {
                return new PointHitTestResult(this, hitTestParams.HitPoint);
            }
 
            return null;
        }
 
        /// <summary>
        /// OnPropertyChanged
        /// </summary>
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
 
            if (e.IsAValueChange || e.IsASubPropertyChange)
            {
                if (e.Property == UIElement.RenderTransformProperty ||
                    e.Property == FrameworkElement.LayoutTransformProperty)
                {
                    EditingCoordinator.InvalidateTransform();
 
                    Transform transform = e.NewValue as Transform;
                    if (transform != null && !transform.HasAnimatedProperties)
                    {
                        TransformGroup transformGroup = transform as TransformGroup;
                        if ( transformGroup != null )
                        {
                            //walk down the tree looking for animated transforms
                            Stack<Transform> transforms = new Stack<Transform>();
                            transforms.Push(transform);
                            while ( transforms.Count > 0 )
                            {
                                transform = transforms.Pop();
                                if ( transform.HasAnimatedProperties )
                                {
                                    return;
                                }
                                transformGroup = transform as TransformGroup;
                                if ( transformGroup != null )
                                {
                                    for ( int i = 0; i < transformGroup.Children.Count; i++ )
                                    {
                                        transforms.Push(transformGroup.Children[i]);
                                    }
                                }
                            }
                        }
 
                        //
                        // only invalidate when there is not an animation on the xf,
                        // or we could wind up creating thousands of new cursors.  That's bad.
                        //
                        _editingCoordinator.InvalidateBehaviorCursor(_editingCoordinator.InkCollectionBehavior);
                        EditingCoordinator.UpdatePointEraserCursor();
                    }
                }
                if (e.Property == FrameworkElement.FlowDirectionProperty)
                {
                    //flow direction only affects the inking cursor.
                    _editingCoordinator.InvalidateBehaviorCursor(_editingCoordinator.InkCollectionBehavior);
                }
            }
        }
 
        /// <summary>
        /// Called when the Template's tree is about to be generated
        /// </summary>
        internal override void OnPreApplyTemplate()
        {
            // No need for calling VerifyAccess since we call the method on the base here.
 
            base.OnPreApplyTemplate();
 
            // Build our visual tree here.
            // <InkCanvas>
            //     <AdornerDecorator>
            //         <InkPresenter>
            //             <InnerCanvas />
            //             <ContainerVisual />              <!-- Ink Renderer static visual -->
            //             <HostVisual />                   <!-- Ink Dynamic Renderer -->
            //         </InkPresenter>
            //         <AdornerLayer>                       <!-- Create by AdornerDecorator automatically -->
            //             <InkCanvasSelectionAdorner />
            //             <InkCanvasFeedbackAdorner />     <!-- Dynamically hooked up when moving/sizing the selection -->
            //         </AdornerLayer>
            //     </AdornerDecorator>
            //  </InkCanvas>
 
            if ( _localAdornerDecorator == null )
            {
                //
                _localAdornerDecorator = new AdornerDecorator();
                InkPresenter inkPresenter = InkPresenter;
 
                // Build the visual tree top-down
                AddVisualChild(_localAdornerDecorator);
                _localAdornerDecorator.Child = inkPresenter;
                inkPresenter.Child = InnerCanvas;
 
                // Add the SelectionAdorner after Canvas is added.
                _localAdornerDecorator.AdornerLayer.Add(SelectionAdorner);
            }
        }
 
        /// <summary>
        /// Returns the Visual children count.
        /// </summary>
        protected override int VisualChildrenCount
        {
            get { return (_localAdornerDecorator == null) ? 0 : 1; }
        }
 
        /// <summary>
        /// Returns the child at the specified index.
        /// </summary>
        protected override Visual GetVisualChild(int index)
        {
            if (    (_localAdornerDecorator == null)
                ||  (index != 0))
            {
                throw new ArgumentOutOfRangeException("index", index, SR.Visual_ArgumentOutOfRange);
            }
 
            return _localAdornerDecorator;
        }
 
        /// <summary>
        /// UIAutomation support
        /// </summary>
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new InkCanvasAutomationPeer(this);
        }
 
        #endregion Protected Overrides
 
        #region Public Properties
 
        /// <summary>
        ///     The DependencyProperty for the Background property.
        /// </summary>
        public static readonly DependencyProperty BackgroundProperty =
                Panel.BackgroundProperty.AddOwner(
                        typeof(InkCanvas),
                        new FrameworkPropertyMetadata(
                                null,
                                FrameworkPropertyMetadataOptions.AffectsRender));
 
        /// <summary>
        ///     An object that describes the background.
        /// </summary>
        [Bindable(true), Category("Appearance")]
        public Brush Background
        {
            get { return (Brush) GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }
 
        /// <summary>
        /// Top DependencyProperty
        /// </summary>
        public static readonly DependencyProperty TopProperty =
                DependencyProperty.RegisterAttached("Top", typeof(double), typeof(InkCanvas),
                    new FrameworkPropertyMetadata(Double.NaN, new PropertyChangedCallback(OnPositioningChanged)),
                    new ValidateValueCallback(System.Windows.Shapes.Shape.IsDoubleFiniteOrNaN));
 
        /// <summary>
        /// Reads the attached property Top from the given element.
        /// </summary>
        /// <param name="element">The element from which to read the Top attached property.</param>
        /// <returns>The property's value.</returns>
        /// <seealso cref="InkCanvas.TopProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetTop(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(TopProperty);
        }
 
        /// <summary>
        /// Writes the attached property Top to the given element.
        /// </summary>
        /// <param name="element">The element to which to write the Top attached property.</param>
        /// <param name="length">The length to set</param>
        /// <seealso cref="InkCanvas.TopProperty" />
        public static void SetTop(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(TopProperty, length);
        }
 
        /// <summary>
        /// The Bottom DependencyProperty
        /// </summary>
        public static readonly DependencyProperty BottomProperty =
                DependencyProperty.RegisterAttached("Bottom", typeof(double), typeof(InkCanvas),
                    new FrameworkPropertyMetadata(Double.NaN, new PropertyChangedCallback(OnPositioningChanged)),
                    new ValidateValueCallback(System.Windows.Shapes.Shape.IsDoubleFiniteOrNaN));
 
        /// <summary>
        /// Reads the attached property Bottom from the given element.
        /// </summary>
        /// <param name="element">The element from which to read the Bottom attached property.</param>
        /// <returns>The property's Length value.</returns>
        /// <seealso cref="InkCanvas.BottomProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetBottom(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(BottomProperty);
        }
 
        /// <summary>
        /// Writes the attached property Bottom to the given element.
        /// </summary>
        /// <param name="element">The element to which to write the Bottom attached property.</param>
        /// <param name="length">The Length to set</param>
        /// <seealso cref="InkCanvas.BottomProperty" />
        public static void SetBottom(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(BottomProperty, length);
        }
 
        /// <summary>
        /// The Left DependencyProperty
        /// </summary>
        public static readonly DependencyProperty LeftProperty =
                DependencyProperty.RegisterAttached("Left", typeof(double), typeof(InkCanvas),
                    new FrameworkPropertyMetadata(Double.NaN, new PropertyChangedCallback(OnPositioningChanged)),
                    new ValidateValueCallback(System.Windows.Shapes.Shape.IsDoubleFiniteOrNaN));
 
        /// <summary>
        /// Reads the attached property Left from the given element.
        /// </summary>
        /// <param name="element">The element from which to read the Left attached property.</param>
        /// <returns>The property's value.</returns>
        /// <seealso cref="InkCanvas.LeftProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetLeft(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(LeftProperty);
        }
 
        /// <summary>
        /// Writes the attached property Left to the given element.
        /// </summary>
        /// <param name="element">The element to which to write the Left attached property.</param>
        /// <param name="length">The length to set</param>
        /// <seealso cref="InkCanvas.LeftProperty" />
        public static void SetLeft(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(LeftProperty, length);
        }
 
        /// <summary>
        /// The Right DependencyProperty
        /// </summary>
        public static readonly DependencyProperty RightProperty =
                DependencyProperty.RegisterAttached("Right", typeof(double), typeof(InkCanvas),
                    new FrameworkPropertyMetadata(Double.NaN, new PropertyChangedCallback(OnPositioningChanged)),
                    new ValidateValueCallback(System.Windows.Shapes.Shape.IsDoubleFiniteOrNaN));
 
        /// <summary>
        /// Reads the attached property Right from the given element.
        /// </summary>
        /// <param name="element">The element from which to read the Right attached property.</param>
        /// <returns>The property's Length value.</returns>
        /// <seealso cref="InkCanvas.RightProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetRight(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(RightProperty);
        }
 
        /// <summary>
        /// Writes the attached property Right to the given element.
        /// </summary>
        /// <param name="element">The element to which to write the Right attached property.</param>
        /// <param name="length">The Length to set</param>
        /// <seealso cref="InkCanvas.RightProperty" />
        public static void SetRight(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(RightProperty, length);
        }
 
        /// <summary>
        /// OnPositioningChanged
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnPositioningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement uie = d as UIElement;
            if ( uie != null )
            {
                // Make sure the UIElement is a child of InkCanvasInnerCanvas.
                InkCanvasInnerCanvas p = VisualTreeHelper.GetParent(uie) as InkCanvasInnerCanvas;
                if ( p != null )
                {
                    if ( e.Property == InkCanvas.LeftProperty
                        || e.Property == InkCanvas.TopProperty )
                    {
                        // Invalidate measure for Left and/or Top.
                        p.InvalidateMeasure();
                    }
                    else
                    {
                        Debug.Assert(e.Property == InkCanvas.RightProperty || e.Property == InkCanvas.BottomProperty,
                            string.Format(System.Globalization.CultureInfo.InvariantCulture, "Unknown dependency property detected - {0}.", e.Property));
 
                        // Invalidate arrange for Right and/or Bottom.
                        p.InvalidateArrange();
                    }
                }
            }
        }
 
        /// <summary>
        ///     The DependencyProperty for the Strokes property.
        /// </summary>
        public static readonly DependencyProperty StrokesProperty =
                InkPresenter.StrokesProperty.AddOwner(
                        typeof(InkCanvas),
                        new FrameworkPropertyMetadata(
                                new StrokeCollectionDefaultValueFactory(),
                                new PropertyChangedCallback(OnStrokesChanged)));
 
        /// <summary>
        /// Gets/Sets the Strokes property.
        /// </summary>
        public StrokeCollection Strokes
        {
            get { return (StrokeCollection)GetValue(StrokesProperty); }
            set { SetValue(StrokesProperty, value); }
        }
 
        private static void OnStrokesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            InkCanvas inkCanvas = (InkCanvas)d;
            StrokeCollection oldValue = (StrokeCollection)e.OldValue;
            StrokeCollection newValue = (StrokeCollection)e.NewValue;
 
            //
            // only change the prop if it's a different object.  We don't
            // want to be doing this for no reason
            //
            if ( !object.ReferenceEquals(oldValue, newValue) )
            {
                // Clear the selected strokes without raising event.
                inkCanvas.CoreChangeSelection(new StrokeCollection(), inkCanvas.InkCanvasSelection.SelectedElements, false);
 
                inkCanvas.InitializeInkObject();
 
                InkCanvasStrokesReplacedEventArgs args =
                    new InkCanvasStrokesReplacedEventArgs(newValue, oldValue); //new, previous
 
                //raise the StrokesChanged event through our protected virtual
                inkCanvas.OnStrokesReplaced(args);
            }
        }
 
        /// <summary>
        /// Returns the SelectionAdorner
        /// </summary>
        internal InkCanvasSelectionAdorner SelectionAdorner
        {
            get
            {
                // We have to create our visual at this point.
                if ( _selectionAdorner == null )
                {
                    // Create the selection Adorner.
                    _selectionAdorner = new InkCanvasSelectionAdorner(InnerCanvas);
 
                    // Bind the InkCanvas.ActiveEditingModeProperty
                    // to SelectionAdorner.VisibilityProperty.
                    Binding activeEditingModeBinding = new Binding();
                    activeEditingModeBinding.Path = new PropertyPath(InkCanvas.ActiveEditingModeProperty);
                    activeEditingModeBinding.Mode = BindingMode.OneWay;
                    activeEditingModeBinding.Source = this;
                    activeEditingModeBinding.Converter = new ActiveEditingMode2VisibilityConverter();
                    _selectionAdorner.SetBinding(UIElement.VisibilityProperty, activeEditingModeBinding);
                }
 
                return _selectionAdorner;
            }
        }
 
        /// <summary>
        /// Returns the FeedbackAdorner
        /// </summary>
        internal InkCanvasFeedbackAdorner FeedbackAdorner
        {
            get
            {
                VerifyAccess();
 
                if ( _feedbackAdorner == null )
                {
                    _feedbackAdorner = new InkCanvasFeedbackAdorner(this);
                }
 
                return _feedbackAdorner;
            }
        }
 
        /// <summary>
        /// Read/Write access to the EraserShape property.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsGestureRecognizerAvailable
        {
            get
            {
                //this property will verify access
                return this.GestureRecognizer.IsRecognizerAvailable;
            }
        }
 
 
        /// <summary>
        /// Emulate Panel's Children property.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public UIElementCollection Children
        {
            get
            {
                // No need to invoke VerifyAccess since the call is forwarded.
 
                return InnerCanvas.Children;
            }
        }
 
        /// <summary>
        /// The DependencyProperty for the DefaultDrawingAttributes property.
        /// </summary>
        public static readonly DependencyProperty DefaultDrawingAttributesProperty =
                DependencyProperty.Register(
                        "DefaultDrawingAttributes",
                        typeof(DrawingAttributes),
                        typeof(InkCanvas),
                        new FrameworkPropertyMetadata(
                                new DrawingAttributesDefaultValueFactory(),
                                new PropertyChangedCallback(OnDefaultDrawingAttributesChanged)),
                        (ValidateValueCallback)delegate(object value)
                            { return value != null; });
 
        /// <summary>
        /// Gets/Sets the DefaultDrawingAttributes property.
        /// </summary>
        public DrawingAttributes DefaultDrawingAttributes
        {
            get { return (DrawingAttributes)GetValue(DefaultDrawingAttributesProperty); }
            set { SetValue(DefaultDrawingAttributesProperty, value); }
        }
 
        private static void OnDefaultDrawingAttributesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            InkCanvas inkCanvas = (InkCanvas)d;
 
            DrawingAttributes oldValue = (DrawingAttributes)e.OldValue;
            DrawingAttributes newValue = (DrawingAttributes)e.NewValue;
 
            // This can throw, so call it first
            inkCanvas.UpdateDynamicRenderer(newValue);
 
            // We only fire Changed event when there is an instance change.
            if ( !object.ReferenceEquals(oldValue, newValue) )
            {
                //we didn't throw, change our backing value
                oldValue.AttributeChanged -= new PropertyDataChangedEventHandler(inkCanvas.DefaultDrawingAttributes_Changed);
                DrawingAttributesReplacedEventArgs args =
                    new DrawingAttributesReplacedEventArgs(newValue, oldValue);
 
                newValue.AttributeChanged += new PropertyDataChangedEventHandler(inkCanvas.DefaultDrawingAttributes_Changed);
                inkCanvas.RaiseDefaultDrawingAttributeReplaced(args);
            }
        }
 
        /// <summary>
        /// Read/Write access to the EraserShape property.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public StylusShape EraserShape
        {
            get
            {
                VerifyAccess();
                if (_eraserShape == null)
                {
                    _eraserShape = new RectangleStylusShape(8f, 8f);
                }
                return _eraserShape;
            }
            set
            {
                VerifyAccess();
                ArgumentNullException.ThrowIfNull(value);
 
                // Invoke getter since this property is lazily created.
                StylusShape oldShape = EraserShape;
 
                _eraserShape = value;
 
 
                if ( oldShape.Width != _eraserShape.Width || oldShape.Height != _eraserShape.Height
                    || oldShape.Rotation != _eraserShape.Rotation || oldShape.GetType() != _eraserShape.GetType())
                {
                    EditingCoordinator.UpdatePointEraserCursor();
                }
            }
        }
 
 
        /// <summary>
        /// ActiveEditingMode
        /// </summary>
        internal static readonly DependencyPropertyKey ActiveEditingModePropertyKey =
                DependencyProperty.RegisterReadOnly(
                        "ActiveEditingMode",
                        typeof(InkCanvasEditingMode),
                        typeof(InkCanvas),
                        new FrameworkPropertyMetadata(InkCanvasEditingMode.Ink));
 
        /// <summary>
        /// ActiveEditingModeProperty Dependency Property
        /// </summary>
        public static readonly DependencyProperty ActiveEditingModeProperty = ActiveEditingModePropertyKey.DependencyProperty;
 
        /// <summary>
        /// Gets the ActiveEditingMode
        /// </summary>
        public InkCanvasEditingMode ActiveEditingMode
        {
            get { return (InkCanvasEditingMode)GetValue(ActiveEditingModeProperty); }
        }
 
        /// <summary>
        /// The DependencyProperty for the EditingMode property.
        /// </summary>
        public static readonly DependencyProperty EditingModeProperty =
                DependencyProperty.Register(
                        "EditingMode",
                        typeof(InkCanvasEditingMode),
                        typeof(InkCanvas),
                        new FrameworkPropertyMetadata(
                                InkCanvasEditingMode.Ink,
                                new PropertyChangedCallback(OnEditingModeChanged)),
                        new ValidateValueCallback(ValidateEditingMode));
 
 
        /// <summary>
        /// Gets/Sets EditingMode
        /// </summary>
        public InkCanvasEditingMode EditingMode
        {
            get { return (InkCanvasEditingMode)GetValue(EditingModeProperty); }
            set { SetValue(EditingModeProperty, value); }
        }
 
 
        private static void OnEditingModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ( (InkCanvas)d ).RaiseEditingModeChanged(
                                new RoutedEventArgs(InkCanvas.EditingModeChangedEvent, d));
        }
 
        /// <summary>
        /// The DependencyProperty for the EditingModeInverted property.
        /// </summary>
        public static readonly DependencyProperty EditingModeInvertedProperty =
                DependencyProperty.Register(
                        "EditingModeInverted",
                        typeof(InkCanvasEditingMode),
                        typeof(InkCanvas),
                        new FrameworkPropertyMetadata(
                                InkCanvasEditingMode.EraseByStroke,
                                new PropertyChangedCallback(OnEditingModeInvertedChanged)),
                        new ValidateValueCallback(ValidateEditingMode));
 
        /// <summary>
        /// Gets/Sets EditingMode
        /// </summary>
        public InkCanvasEditingMode EditingModeInverted
        {
            get { return (InkCanvasEditingMode)GetValue(EditingModeInvertedProperty); }
            set { SetValue(EditingModeInvertedProperty, value); }
        }
 
        private static void OnEditingModeInvertedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ( (InkCanvas)d ).RaiseEditingModeInvertedChanged(
                new RoutedEventArgs(InkCanvas.EditingModeInvertedChangedEvent, d));
        }
 
        private static bool ValidateEditingMode(object value)
        {
            return EditingModeHelper.IsDefined((InkCanvasEditingMode)value);
        }
 
        /// <summary>
        /// This flag indicates whether the developer is using a custom mouse cursor.
        ///
        /// If this flag is true, we will never change the current cursor on them. Not
        /// on edit mode change.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool UseCustomCursor
        {
            get
            {
                VerifyAccess();
                return _useCustomCursor;
            }
            set
            {
                VerifyAccess();
 
                if ( _useCustomCursor != value )
                {
                    _useCustomCursor = value;
                    UpdateCursor();
                }
            }
        }
 
        /// <summary>
        /// Gets or set if moving of selection is enabled
        /// </summary>
        /// <value>bool</value>
        public bool MoveEnabled
        {
            get
            {
                VerifyAccess();
                return _editingCoordinator.MoveEnabled;
            }
            set
            {
                VerifyAccess();
                bool oldValue = _editingCoordinator.MoveEnabled;
 
                if (oldValue != value)
                {
                    _editingCoordinator.MoveEnabled = value;
                }
            }
        }
 
        /// <summary>
        /// Gets or set if resizing selection is enabled
        /// </summary>
        /// <value>bool</value>
        public bool ResizeEnabled
        {
            get
            {
                VerifyAccess();
                return _editingCoordinator.ResizeEnabled;
            }
            set
            {
                VerifyAccess();
                bool oldValue = _editingCoordinator.ResizeEnabled;
 
                if (oldValue != value)
                {
                    _editingCoordinator.ResizeEnabled = value;
                }
            }
        }
 
 
        /// <summary>
        /// Read/Write access to the DefaultPacketDescription property.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public StylusPointDescription DefaultStylusPointDescription
        {
            get
            {
                VerifyAccess();
 
                return _defaultStylusPointDescription;
            }
            set
            {
                VerifyAccess();
 
                //
                // no nulls allowed
                //
                ArgumentNullException.ThrowIfNull(value);
 
                _defaultStylusPointDescription = value;
            }
        }
 
        /// <summary>
        /// Read/Write the enabled ClipboardFormats
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEnumerable<InkCanvasClipboardFormat> PreferredPasteFormats
        {
            get
            {
                VerifyAccess();
 
                return ClipboardProcessor.PreferredFormats;
            }
            set
            {
                VerifyAccess();
 
                // Cannot be null
                ArgumentNullException.ThrowIfNull(value);
 
                ClipboardProcessor.PreferredFormats = value;
            }
        }
 
        #endregion Public Properties
 
        #region Public Events
 
        /// <summary>
        ///     The StrokeErased Routed Event
        /// </summary>
        public static readonly RoutedEvent StrokeCollectedEvent =
            EventManager.RegisterRoutedEvent("StrokeCollected", RoutingStrategy.Bubble, typeof(InkCanvasStrokeCollectedEventHandler), typeof(InkCanvas));
 
        /// <summary>
        ///     Add / Remove StrokeCollected handler
        /// </summary>
        [Category("Behavior")]
        public event InkCanvasStrokeCollectedEventHandler StrokeCollected
        {
            add
            {
                AddHandler(InkCanvas.StrokeCollectedEvent, value);
            }
 
            remove
            {
                RemoveHandler(InkCanvas.StrokeCollectedEvent, value);
            }
        }
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">InkCanvasStrokeCollectedEventArgs to raise the event with</param>
        protected virtual void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
 
            RaiseEvent(e);
        }
 
        /// <summary>
        /// Allows the InkCollectionBehavior to raise the StrokeCollected event via the protected virtual
        /// </summary>
        /// <param name="e">InkCanvasStrokeCollectedEventArgs to raise the event with</param>
        /// <param name="userInitiated">true only if 100% of the stylusPoints that makes up the stroke
        /// came from eventargs with the UserInitiated flag set to true</param>
        internal void RaiseGestureOrStrokeCollected(InkCanvasStrokeCollectedEventArgs e, bool userInitiated)
        {
            Debug.Assert(e != null, "EventArg can not be null");
            bool addStrokeToInkCanvas = true; // Initialize our flag.
 
            // The follow code raises Gesture event
            // The out-side code could throw exception in the their handlers. We use try/finally block to protect our status.
            try
            {
                //
                // perform gesture reco before raising this event
                // if we're in the right mode
                //
                //IMPORTANT: only call gesture recognition if userInitiated.  See SecurityNote.
                if (userInitiated)
                {
                    if ((this.ActiveEditingMode == InkCanvasEditingMode.InkAndGesture ||
                          this.ActiveEditingMode == InkCanvasEditingMode.GestureOnly) &&
                          this.GestureRecognizer.IsRecognizerAvailable)
                    {
                        StrokeCollection strokes = new StrokeCollection();
                        strokes.Add(e.Stroke);
 
                        //
                        // GestureRecognizer.Recognize demands unmanaged code, we assert it here
                        // as this codepath is only called in response to user input
                        //
                        ReadOnlyCollection<GestureRecognitionResult> results =
                            this.GestureRecognizer.CriticalRecognize(strokes);
 
                        if (results.Count > 0)
                        {
                            InkCanvasGestureEventArgs args =
                                new InkCanvasGestureEventArgs(strokes, results);
 
                            if (results[0].ApplicationGesture == ApplicationGesture.NoGesture)
                            {
                                //
                                // we set Cancel=true if we didn't detect a gesture
                                //
                                args.Cancel = true;
                            }
                            else
                            {
                                args.Cancel = false;
                            }
 
                            this.OnGesture(args);
 
                            //
                            // now that we've raised the Gesture event and the developer
                            // has had a chance to change args.Cancel, see what their intent is.
                            //
                            if (args.Cancel == false)
                            {
                                //bail out and don't add
                                //the stroke to InkCanvas.Strokes
                                addStrokeToInkCanvas = false; // Reset the flag.
                                return;
                            }
                        }
                    }
                }
 
                // Reset the flag.
                addStrokeToInkCanvas = false;
 
                //
                // only raise StrokeCollected if we're in InkCanvasEditingMode.Ink or InkCanvasEditingMode.InkAndGesture
                //
                if ( this.ActiveEditingMode == InkCanvasEditingMode.Ink ||
                    this.ActiveEditingMode == InkCanvasEditingMode.InkAndGesture )
                {
                    //add the stroke to the StrokeCollection and raise this event
                    this.Strokes.Add(e.Stroke);
                    this.OnStrokeCollected(e);
                }
            }
            finally
            {
                // If the gesture events are failed, we should still add Stroke to the InkCanvas so that the data won't be lost.
                if ( addStrokeToInkCanvas )
                {
                    this.Strokes.Add(e.Stroke);
                }
            }
        }
 
        /// <summary>
        ///     The Gesture Routed Event
        /// </summary>
        public static readonly RoutedEvent GestureEvent =
            EventManager.RegisterRoutedEvent("Gesture", RoutingStrategy.Bubble, typeof(InkCanvasGestureEventHandler), typeof(InkCanvas));
 
        /// <summary>
        ///     Add / Remove Gesture handler
        /// </summary>
        [Category("Behavior")]
        public event InkCanvasGestureEventHandler Gesture
        {
            add
            {
                AddHandler(InkCanvas.GestureEvent, value);
            }
 
            remove
            {
                RemoveHandler(InkCanvas.GestureEvent, value);
            }
        }
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">InkCanvasGestureEventArgs to raise the event with</param>
        protected virtual void OnGesture(InkCanvasGestureEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
 
            RaiseEvent(e);
        }
 
        /// <summary>
        /// Raised when the InkCanvas.Strokes StrokeCollection has been replaced with another one
        /// </summary>
        public event InkCanvasStrokesReplacedEventHandler StrokesReplaced;
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">InkCanvasStrokesChangedEventArgs to raise the event with</param>
        protected virtual void OnStrokesReplaced(InkCanvasStrokesReplacedEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != this.StrokesReplaced)
            {
                StrokesReplaced(this, e);
            }
        }
 
        /// <summary>
        /// Raised when the InkCanvas.DefaultDrawingAttributes has been replaced with another one
        /// </summary>
        public event DrawingAttributesReplacedEventHandler DefaultDrawingAttributesReplaced;
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">DrawingAttributesReplacedEventArgs to raise the event with</param>
        protected virtual void OnDefaultDrawingAttributesReplaced(DrawingAttributesReplacedEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != this.DefaultDrawingAttributesReplaced)
            {
                DefaultDrawingAttributesReplaced(this, e);
            }
        }
 
        /// <summary>
        /// Private helper for raising DDAReplaced.  Invalidates the inking cursor
        /// </summary>
        /// <param name="e"></param>
        private void RaiseDefaultDrawingAttributeReplaced(DrawingAttributesReplacedEventArgs e)
        {
            this.OnDefaultDrawingAttributesReplaced(e);
 
            // Invalidate the inking cursor
            _editingCoordinator.InvalidateBehaviorCursor(_editingCoordinator.InkCollectionBehavior);
        }
 
        /// <summary>
        ///     Event corresponds to ActiveEditingModeChanged
        /// </summary>
        public static readonly RoutedEvent ActiveEditingModeChangedEvent =
            EventManager.RegisterRoutedEvent("ActiveEditingModeChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(InkCanvas));
 
        /// <summary>
        ///     Add / Remove ActiveEditingModeChanged handler
        /// </summary>
        [Category("Behavior")]
        public event RoutedEventHandler ActiveEditingModeChanged
        {
            add
            {
                AddHandler(InkCanvas.ActiveEditingModeChangedEvent, value);
            }
            remove
            {
                RemoveHandler(InkCanvas.ActiveEditingModeChangedEvent, value);
            }
        }
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        protected virtual void OnActiveEditingModeChanged(RoutedEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
 
            RaiseEvent(e);
        }
 
        /// <summary>
        /// Private helper that raises ActiveEditingModeChanged
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        internal void RaiseActiveEditingModeChanged(RoutedEventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
 
            InkCanvasEditingMode mode = this.ActiveEditingMode;
            if (mode != _editingCoordinator.ActiveEditingMode)
            {
                //change our DP, then raise the event via our protected override
                SetValue(ActiveEditingModePropertyKey, _editingCoordinator.ActiveEditingMode);
 
                this.OnActiveEditingModeChanged(e);
            }
        }
 
 
 
        /// <summary>
        ///     Event corresponds to EditingModeChanged
        /// </summary>
        public static readonly RoutedEvent EditingModeChangedEvent =
            EventManager.RegisterRoutedEvent("EditingModeChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(InkCanvas));
 
        /// <summary>
        ///     Add / Remove EditingModeChanged handler
        /// </summary>
        [Category("Behavior")]
        public event RoutedEventHandler EditingModeChanged
        {
            add
            {
                AddHandler(InkCanvas.EditingModeChangedEvent, value);
            }
 
            remove
            {
                RemoveHandler(InkCanvas.EditingModeChangedEvent, value);
            }
        }
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        protected virtual void OnEditingModeChanged(RoutedEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
 
            RaiseEvent(e);
        }
        /// <summary>
        /// Private helper that raises EditingModeChanged but first
        /// talks to the InkEditor about it
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        private void RaiseEditingModeChanged(RoutedEventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
 
            _editingCoordinator.UpdateEditingState(false /* EditingMode */);
 
            this.OnEditingModeChanged(e);
        }
 
        //note: there is no need for an internal RaiseEditingModeInvertedChanging
        //since this isn't a dynamic property and therefore can not be set
        //outside of this class
 
        /// <summary>
        ///     Event corresponds to EditingModeInvertedChanged
        /// </summary>
        public static readonly RoutedEvent EditingModeInvertedChangedEvent =
            EventManager.RegisterRoutedEvent("EditingModeInvertedChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(InkCanvas));
 
        /// <summary>
        ///     Add / Remove EditingModeChanged handler
        /// </summary>
        [Category("Behavior")]
        public event RoutedEventHandler EditingModeInvertedChanged
        {
            add
            {
                AddHandler(InkCanvas.EditingModeInvertedChangedEvent, value);
            }
 
            remove
            {
                RemoveHandler(InkCanvas.EditingModeInvertedChangedEvent, value);
            }
        }
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        protected virtual void OnEditingModeInvertedChanged(RoutedEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
 
            RaiseEvent(e);
        }
        /// <summary>
        /// Private helper that raises EditingModeInvertedChanged but first
        /// talks to the InkEditor about it
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        private void RaiseEditingModeInvertedChanged(RoutedEventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
 
            _editingCoordinator.UpdateEditingState(true /* EditingModeInverted */);
 
            this.OnEditingModeInvertedChanged(e);
        }
 
        /// <summary>
        /// Occurs when the user has moved the selection, after they lift their stylus to commit the change.
        /// This event allows the developer to cancel the move.
        /// </summary>
        public event  InkCanvasSelectionEditingEventHandler SelectionMoving;
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e"> InkCanvasSelectionEditingEventArgs to raise the event with</param>
        protected virtual void OnSelectionMoving( InkCanvasSelectionEditingEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != SelectionMoving)
            {
                SelectionMoving(this, e);
            }
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the SelectionMoving event via the protected virtual
        /// </summary>
        /// <param name="e"> InkCanvasSelectionEditingEventArgs to raise the event with</param>
        internal void RaiseSelectionMoving( InkCanvasSelectionEditingEventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
            this.OnSelectionMoving(e);
        }
 
        /// <summary>
        /// Occurs when the user has moved the selection, after they lift their stylus to commit the change.
        /// This event allows the developer to cancel the move.
        /// </summary>
        public event EventHandler SelectionMoved;
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        protected virtual void OnSelectionMoved(EventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != SelectionMoved)
            {
                SelectionMoved(this, e);
            }
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the SelectionMoved event via the protected virtual
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        internal void RaiseSelectionMoved(EventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
 
            this.OnSelectionMoved(e);
            // Update the cursor of SelectionEditor behavior.
            EditingCoordinator.SelectionEditor.OnInkCanvasSelectionChanged();
        }
 
        /// <summary>
        /// Occurs when the user has erased Strokes using the erase behavior
        ///
        /// This event allows the developer to cancel the erase -- therefore, the Stroke should not disappear until
        /// this event has finished.
        /// </summary>
        public event InkCanvasStrokeErasingEventHandler StrokeErasing;
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">InkCanvasStrokeErasingEventArgs to raise the event with</param>
        protected virtual void OnStrokeErasing(InkCanvasStrokeErasingEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != StrokeErasing)
            {
                StrokeErasing(this, e);
            }
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the InkErasing event via the protected virtual
        /// </summary>
        /// <param name="e">InkCanvasStrokeErasingEventArgs to raise the event with</param>
        internal void RaiseStrokeErasing(InkCanvasStrokeErasingEventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
            this.OnStrokeErasing(e);
        }
 
        /// <summary>
        ///     The StrokeErased Routed Event
        /// </summary>
        public static readonly RoutedEvent StrokeErasedEvent =
            EventManager.RegisterRoutedEvent("StrokeErased", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(InkCanvas));
 
        /// <summary>
        ///     Add / Remove EditingModeChanged handler
        /// </summary>
        [Category("Behavior")]
        public event RoutedEventHandler StrokeErased
        {
            add
            {
                AddHandler(InkCanvas.StrokeErasedEvent, value);
            }
 
            remove
            {
                RemoveHandler(InkCanvas.StrokeErasedEvent, value);
            }
        }
 
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        protected virtual void OnStrokeErased(RoutedEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            RaiseEvent(e);
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the InkErasing event via the protected virtual
        /// </summary>
        internal void RaiseInkErased()
        {
            this.OnStrokeErased(
                new RoutedEventArgs(InkCanvas.StrokeErasedEvent, this));
        }
 
        /// <summary>
        /// Occurs when the user has resized the selection, after they lift their stylus to commit the change.
        /// This event allows the developer to cancel the resize.
        /// </summary>
        public event  InkCanvasSelectionEditingEventHandler SelectionResizing;
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e"> InkCanvasSelectionEditingEventArgs to raise the event with</param>
        protected virtual void OnSelectionResizing( InkCanvasSelectionEditingEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != SelectionResizing)
            {
                SelectionResizing(this, e);
            }
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the SelectionResizing event via the protected virtual
        /// </summary>
        /// <param name="e"> InkCanvasSelectionEditingEventArgs to raise the event with</param>
        internal void RaiseSelectionResizing( InkCanvasSelectionEditingEventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
            this.OnSelectionResizing(e);
        }
 
        /// <summary>
        /// Occurs when the selection has been resized via UI interaction.
        /// </summary>
        public event EventHandler SelectionResized;
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        protected virtual void OnSelectionResized(EventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != SelectionResized)
            {
                SelectionResized(this, e);
            }
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the SelectionResized event via the protected virtual
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        internal void RaiseSelectionResized(EventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
 
            this.OnSelectionResized(e);
            // Update the cursor of SelectionEditor behavior.
            EditingCoordinator.SelectionEditor.OnInkCanvasSelectionChanged();
        }
 
        /// <summary>
        /// Occurs when the selection has been changed, either using the lasso or programmatically.
        /// This event allows the developer to cancel the change.
        /// </summary>
        public event InkCanvasSelectionChangingEventHandler SelectionChanging;
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">InkCanvasSelectionChangingEventArgs to raise the event with</param>
        protected virtual void OnSelectionChanging(InkCanvasSelectionChangingEventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != SelectionChanging)
            {
                SelectionChanging(this, e);
            }
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the SelectionChanging event via the protected virtual
        /// </summary>
        /// <param name="e">InkCanvasSelectionChangingEventArgs to raise the event with</param>
        private void RaiseSelectionChanging(InkCanvasSelectionChangingEventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
            this.OnSelectionChanging(e);
        }
 
        /// <summary>
        /// Occurs when the selection has been changed
        /// </summary>
        public event EventHandler SelectionChanged;
        /// <summary>
        /// Protected virtual version for developers deriving from InkCanvas.
        /// This method is what actually throws the event.
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        protected virtual void OnSelectionChanged(EventArgs e)
        {
            // No need to invoke VerifyAccess since this method is thread free.
 
            ArgumentNullException.ThrowIfNull(e);
            if (null != SelectionChanged)
            {
                SelectionChanged(this, e);
            }
        }
 
        /// <summary>
        /// Allows the EditingBehaviors to raise the SelectionChanged event via the protected virtual
        /// </summary>
        /// <param name="e">EventArgs to raise the event with</param>
        internal void RaiseSelectionChanged(EventArgs e)
        {
            Debug.Assert(e != null, "EventArg can not be null");
 
            this.OnSelectionChanged(e);
            // Update the cursor of SelectionEditor behavior.
            EditingCoordinator.SelectionEditor.OnInkCanvasSelectionChanged();
        }
 
        /// <summary>
        /// The InkCanvas uses an inner Canvas to host children.  When the inner Canvas's children
        /// are changed, we need to call the protected virtual OnVisualChildrenChanged on the InkCanvas
        /// so that subclasses can be notified
        /// </summary>
        internal void RaiseOnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
        {
            this.OnVisualChildrenChanged(visualAdded, visualRemoved);
        }
 
        #endregion Public Events
 
        #region Public Methods
 
        /// <summary>
        /// Returns the enabled gestures.  This method throws an exception if GestureRecognizerAvailable
        /// is false
        /// </summary>
        /// <returns></returns>
        public ReadOnlyCollection<ApplicationGesture> GetEnabledGestures()
        {
            // No need to invoke VerifyAccess since it's checked in GestureRecognizer.GetEnabledGestures.
 
            //gestureRecognizer throws appropriately if there is no gesture recognizer available
            return new ReadOnlyCollection<ApplicationGesture>(this.GestureRecognizer.GetEnabledGestures());
        }
 
        /// <summary>
        /// Sets the enabled gestures.  This method throws an exception if GestureRecognizerAvailable
        /// is false
        /// </summary>
        /// <returns></returns>
        public void SetEnabledGestures(IEnumerable<ApplicationGesture> applicationGestures)
        {
            // No need to invoke VerifyAccess since it's checked in GestureRecognizer.GetEnabledGestures.
 
            //gestureRecognizer throws appropriately if there is no gesture recognizer available
            this.GestureRecognizer.SetEnabledGestures(applicationGestures);
        }
 
        /// <summary>
        /// Get the selection bounds.
        /// </summary>
        /// <returns></returns>
        public Rect GetSelectionBounds()
        {
            VerifyAccess();
 
            return InkCanvasSelection.SelectionBounds;
        }
 
        /// <summary>
        /// provides access to the currently selected elements which are children of this InkCanvas
        /// </summary>
        public ReadOnlyCollection<UIElement> GetSelectedElements()
        {
            VerifyAccess();
            return InkCanvasSelection.SelectedElements;
        }
 
        /// <summary>
        /// provides read access to the currently selected strokes
        /// </summary>
        public StrokeCollection GetSelectedStrokes()
        {
            VerifyAccess();
 
            StrokeCollection sc = new StrokeCollection();
            sc.Add(InkCanvasSelection.SelectedStrokes);
            return sc;
        }
 
        /// <summary>
        /// Overload which calls the more complex version, passing null for selectedElements
        /// </summary>
        /// <param name="selectedStrokes">The strokes to select</param>
        public void Select(StrokeCollection selectedStrokes)
        {
            // No need to invoke VerifyAccess since this call is forwarded.
            Select(selectedStrokes, null);
        }
 
        /// <summary>
        /// Overload which calls the more complex version, passing null for selectedStrokes
        /// </summary>
        /// <param name="selectedElements">The elements to select</param>
        public void Select(IEnumerable<UIElement> selectedElements)
        {
            // No need to invoke VerifyAccess since this call is forwarded.
            Select(null, selectedElements);
        }
 
        /// <summary>
        /// Overload which calls the more complex version, passing null for selectedStrokes
        /// </summary>
        /// <param name="selectedStrokes">The strokes to select</param>
        /// <param name="selectedElements">The elements to select</param>
        public void Select(StrokeCollection selectedStrokes, IEnumerable<UIElement> selectedElements)
        {
            VerifyAccess();
 
            //
            // Try to switch to Select mode first. If we fail to change the mode, then just simply no-op.
            if ( EnsureActiveEditingMode(InkCanvasEditingMode.Select) )
            {
                //
                // validate
                //
                UIElement[] validElements = ValidateSelectedElements(selectedElements);
                StrokeCollection validStrokes = ValidateSelectedStrokes(selectedStrokes);
 
                //
                // this will raise the 'SelectionChanging' event ONLY if the selection
                // is actually different
                //
                ChangeInkCanvasSelection(validStrokes, validElements);
            }
        }
 
        /// <summary>
        /// Hit test on the selection
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public InkCanvasSelectionHitResult HitTestSelection(Point point)
        {
            VerifyAccess();
 
            // Ensure the visual tree.
            if ( _localAdornerDecorator == null )
            {
                ApplyTemplate();
            }
 
            return InkCanvasSelection.HitTestSelection(point);
        }
 
        /// <summary>
        /// Copy the current selection in the InkCanvas to the clipboard
        /// </summary>
        public void CopySelection()
        {
            VerifyAccess();
            PrivateCopySelection();
        }
 
        /// <summary>
        /// Copy the current selection in the InkCanvas to the clipboard and then delete it
        /// </summary>
        public void CutSelection()
        {
            VerifyAccess();
 
            // Copy first
            InkCanvasClipboardDataFormats copiedDataFormats = PrivateCopySelection();
 
            // Don't even bother if we don't have a selection.
            if ( copiedDataFormats != InkCanvasClipboardDataFormats.None )
            {
                // Then delete the current selection. Note the XAML format won't be avaliable under Partial
                // Trust. So, the selected element shouldn't be copied or removed.
                DeleteCurrentSelection(
                    /* We want to delete the selected Strokes if there is ISF and/or XAML data being copied */
                    (copiedDataFormats &
                        (InkCanvasClipboardDataFormats.ISF | InkCanvasClipboardDataFormats.XAML)) != 0,
                    /* We only want to delete the selected elements if there is XAML data being copied */
                    (copiedDataFormats & InkCanvasClipboardDataFormats.XAML) != 0);
            }
        }
 
        /// <summary>
        /// Paste the contents of the clipboard into the InkCanvas
        /// </summary>
        public void Paste()
        {
            // No need to call VerifyAccess since this call is forwarded.
 
            // We always paste the data to the default location which is (0,0).
            Paste(new Point(c_pasteDefaultLocation, c_pasteDefaultLocation));
        }
 
        /// <summary>
        /// Paste the contents of the clipboard to the specified location in the InkCanvas
        /// </summary>
        public void Paste(Point point)
        {
            VerifyAccess();
 
            if (double.IsNaN(point.X) ||
                double.IsNaN(point.Y) ||
                Double.IsInfinity(point.X)||
                Double.IsInfinity(point.Y) )
            {
                    throw new ArgumentException(SR.InvalidPoint, "point");
            }
 
 
            //
            // only do this if the user is not editing (input active)
            // or we will violate a dispatcher lock
            //
            if (!_editingCoordinator.UserIsEditing)
            {
                IDataObject dataObj = null;
                try
                {
                    dataObj = Clipboard.GetDataObject();
                }
                catch (ExternalException)
                {
                    //harden against ExternalException
                    return;
                }
                if (dataObj != null)
                {
                    PasteFromDataObject(dataObj, point);
                }
            }
        }
 
        /// <summary>
        /// Return true if clipboard contents can be pasted into the InkCanvas.
        /// </summary>
        public bool CanPaste()
        {
            VerifyAccess();
 
            bool ret = false;
            //
            // can't paste if the user is editing (input active)
            // or we will violate a dispatcher lock
            //
            if (_editingCoordinator.UserIsEditing)
            {
                return false;
            }
 
            ret = PrivateCanPaste();
 
            return ret;
        }
 
        #endregion Public Methods
 
        //------------------------------------------------------
        //
        //  IAddChild Interface
        //
        //------------------------------------------------------
 
        #region IAddChild Interface
 
        ///<summary>
        /// Called to Add the object as a Child.
        ///</summary>
        ///<param name="value">
        /// Object to add as a child
        ///</param>
        void IAddChild.AddChild(Object value)
        {
            //             VerifyAccess();
 
            ArgumentNullException.ThrowIfNull(value);
 
            ( (IAddChild)InnerCanvas ).AddChild(value);
        }
 
        ///<summary>
        /// Called when text appears under the tag in markup.
        ///</summary>
        ///<param name="textData">
        /// Text to Add to the Canvas
        ///</param>
        void IAddChild.AddText(string textData)
        {
            //             VerifyAccess();
 
            ( (IAddChild)InnerCanvas ).AddText(textData);
        }
 
        #endregion IAddChild Interface
 
        //------------------------------------------------------
        //
        //  Protected Properties
        //
        //------------------------------------------------------
 
        #region Protected Properties
 
        /// <summary>
        /// Returns enumerator to logical children.
        /// </summary>
        protected internal override IEnumerator LogicalChildren
        {
            get
            {
                //        VerifyAccess( );
 
                // Return the private logical children of the InnerCanvas
                return ( (InkCanvasInnerCanvas)InnerCanvas).PrivateLogicalChildren;
            }
        }
 
        /// <summary>
        /// Protected DynamicRenderer property.
        /// </summary>
        protected DynamicRenderer DynamicRenderer
        {
            get
            {
                VerifyAccess();
                return InternalDynamicRenderer;
            }
            set
            {
                VerifyAccess();
                if (!object.ReferenceEquals(value, _dynamicRenderer))
                {
                    int previousIndex = -1;
                    //remove the existing plugin
                    if (_dynamicRenderer != null)
                    {
                        //remove the plugin from the collection
                        previousIndex = this.StylusPlugIns.IndexOf(_dynamicRenderer);
                        if (-1 != previousIndex)
                        {
                            this.StylusPlugIns.RemoveAt(previousIndex);
                        }
 
                        //remove the plugin's visual from the InkPresenter
                        if (this.InkPresenter.ContainsAttachedVisual(_dynamicRenderer.RootVisual))
                        {
                            this.InkPresenter.DetachVisuals(_dynamicRenderer.RootVisual);
                        }
                    }
 
                    _dynamicRenderer = value;
 
                    if (_dynamicRenderer != null) //null is acceptable
                    {
                        //remove the plugin from the collection
                        if (!this.StylusPlugIns.Contains(_dynamicRenderer))
                        {
                            if (-1 != previousIndex)
                            {
                                //insert the new DR in the same location as the old one
                                this.StylusPlugIns.Insert(previousIndex, _dynamicRenderer);
                            }
                            else
                            {
                                this.StylusPlugIns.Add(_dynamicRenderer);
                            }
                        }
 
                        //refer to the same DrawingAttributes as the InkCanvas
                        _dynamicRenderer.DrawingAttributes = this.DefaultDrawingAttributes;
 
                        //attach the DynamicRenderer if it is not already
                        if (!(this.InkPresenter.ContainsAttachedVisual(_dynamicRenderer.RootVisual)) &&
                            _dynamicRenderer.Enabled &&
                            _dynamicRenderer.RootVisual != null)
                        {
                            this.InkPresenter.AttachVisuals(_dynamicRenderer.RootVisual, this.DefaultDrawingAttributes);
                        }
                    }
                }
            }
        }
 
        /// <summary>
        /// Protected read only access to the InkPresenter this InkCanvas uses
        /// </summary>
        protected InkPresenter InkPresenter
        {
            get
            {
                VerifyAccess();
                if ( _inkPresenter == null )
                {
                    _inkPresenter = new InkPresenter();
 
                    // Bind the InkPresenter.Strokes to InkCanvas.Strokes
                    Binding strokes = new Binding();
                    strokes.Path = new PropertyPath(InkCanvas.StrokesProperty);
                    strokes.Mode = BindingMode.OneWay;
                    strokes.Source = this;
                    _inkPresenter.SetBinding(InkPresenter.StrokesProperty, strokes);
                }
                return _inkPresenter;
            }
        }
 
        #endregion Protected Properties
 
        #region Internal Properties / Methods
 
 
        /// <summary>
        /// Deselect the current selection
        /// </summary>
        internal static readonly RoutedCommand DeselectCommand = new RoutedCommand("Deselect", typeof(InkCanvas));
 
        /// <summary>
        /// UserInitiatedCanPaste
        /// </summary>
        /// <returns></returns>
        private bool UserInitiatedCanPaste()
        {
            return PrivateCanPaste();
        }
 
        /// <summary>
        /// PrivateCanPaste
        /// </summary>
        /// <returns></returns>
        private bool PrivateCanPaste()
        {
            bool canPaste = false;
            IDataObject dataObj = null;
            try
            {
                dataObj = Clipboard.GetDataObject();
            }
            catch (ExternalException)
            {
                //harden against ExternalException
                return false;
            }
            if ( dataObj != null )
            {
                canPaste = ClipboardProcessor.CheckDataFormats(dataObj);
            }
 
            return canPaste;
        }
 
        /// <summary>
        /// This method pastes data from an IDataObject object
        /// </summary>
        internal void PasteFromDataObject(IDataObject dataObj, Point point)
        {
            // Reset the current selection
            ClearSelection(false);
 
            // Assume that there is nothing to be selected.
            StrokeCollection newStrokes = new StrokeCollection();
            List<UIElement> newElements = new List<UIElement>();
 
            // Paste the data from the data object.
            if ( !ClipboardProcessor.PasteData(dataObj, ref newStrokes, ref newElements) )
            {
                // Paste was failed.
                return;
            }
            else if ( newStrokes.Count == 0 && newElements.Count == 0 )
            {
                // Nothing has been received from the clipboard.
                return;
            }
 
            // We add elements here. Then we have to wait for the layout update.
            UIElementCollection children = Children;
            foreach ( UIElement element in newElements )
            {
                children.Add(element);
            }
 
            if ( newStrokes != null )
            {
                Strokes.Add(newStrokes);
            }
 
            try
            {
                // We should fire SelectionChanged event if the current editing mode is Select.
                CoreChangeSelection(newStrokes, newElements.ToArray(), EditingMode == InkCanvasEditingMode.Select);
            }
            finally
            {
                // Now move the selection to the desired location.
                Rect bounds = GetSelectionBounds( );
                InkCanvasSelection.CommitChanges(Rect.Offset(bounds, -bounds.Left + point.X, -bounds.Top + point.Y), false);
 
                if (EditingMode != InkCanvasEditingMode.Select)
                {
                    // Clear the selection without the event if the editing mode is not Select.
                    ClearSelection(false);
                }
            }
        }
 
        /// <summary>
        /// Copies the InkCanvas contents to a DataObject and returns it to the caller.
        ///  Can return NULL for DataObject.
        /// </summary>
        private InkCanvasClipboardDataFormats CopyToDataObject()
        {
             DataObject dataObj;
            dataObj = new DataObject();
            InkCanvasClipboardDataFormats copiedDataFormats = InkCanvasClipboardDataFormats.None;
 
            // Try to copy the data from the InkCanvas to the clipboard.
            copiedDataFormats = ClipboardProcessor.CopySelectedData(dataObj);
 
            if ( copiedDataFormats != InkCanvasClipboardDataFormats.None )
            {
                // Put our data object into the clipboard.
                Clipboard.SetDataObject(dataObj, true);
            }
 
            return copiedDataFormats;
        }
 
        /// <summary>
        /// Read-only property of the associated EditingCoordinator.
        /// </summary>
        internal EditingCoordinator EditingCoordinator
        {
            get
            {
                return _editingCoordinator;
            }
        }
 
        /// <summary>
        /// Internal access to the protected DynamicRenderer.  Can be null.
        /// </summary>
        internal DynamicRenderer InternalDynamicRenderer
        {
            get
            {
                return _dynamicRenderer;
            }
        }
 
        /// <summary>
        /// Return the inner Canvas.
        /// </summary>
        internal InkCanvasInnerCanvas InnerCanvas
        {
            get
            {
                // We have to create our visual at this point.
                if (_innerCanvas == null)
                {
                    // Create our InnerCanvas to change the logical parent of Canvas' children.
                    _innerCanvas = new InkCanvasInnerCanvas(this);
 
                    // Bind the inner Canvas' Background to InkCanvas' Background
                    Binding background = new Binding();
                    background.Path = new PropertyPath(InkCanvas.BackgroundProperty);
                    background.Mode = BindingMode.OneWay;
                    background.Source = this;
                    _innerCanvas.SetBinding(Panel.BackgroundProperty, background);
                }
 
                return _innerCanvas;
            }
        }
 
        /// <summary>
        /// Internal access to the current selection
        /// </summary>
        internal InkCanvasSelection InkCanvasSelection
        {
            get
            {
                if ( _selection == null )
                {
                    _selection = new InkCanvasSelection(this);
                }
 
                return _selection;
            }
        }
 
 
        /// <summary>
        /// Internal helper called by the LassoSelectionBehavior
        /// </summary>
        internal void BeginDynamicSelection(Visual visual)
        {
            EditingCoordinator.DebugCheckActiveBehavior(EditingCoordinator.LassoSelectionBehavior);
 
            _dynamicallySelectedStrokes = new StrokeCollection();
 
            InkPresenter.AttachVisuals(visual, new DrawingAttributes());
        }
 
        /// <summary>
        /// Internal helper called by LassoSelectionBehavior to update the display
        /// of dynamically added strokes
        /// </summary>
        internal void UpdateDynamicSelection(   StrokeCollection strokesToDynamicallySelect,
                                                StrokeCollection strokesToDynamicallyUnselect)
        {
            EditingCoordinator.DebugCheckActiveBehavior(EditingCoordinator.LassoSelectionBehavior);
 
            //
            // update our internal stroke collections used by dynamic selection
            //
            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;
                }
            }
        }
 
        /// <summary>
        /// Internal helper used by LassoSelectionBehavior
        /// </summary>
        internal StrokeCollection EndDynamicSelection(Visual visual)
        {
            EditingCoordinator.DebugCheckActiveBehavior(EditingCoordinator.LassoSelectionBehavior);
 
            InkPresenter.DetachVisuals(visual);
 
            StrokeCollection selectedStrokes = _dynamicallySelectedStrokes;
            _dynamicallySelectedStrokes = null;
 
            return selectedStrokes;
        }
 
        /// <summary>
        /// Clears the selection in the ink canvas
        /// and returns a bool indicating if the selection was actually cleared
        /// (developers can cancel selectionchanging)
        ///
        /// If the InkCanvas has no selection, selectionchanging is not raised
        /// and this method returns true
        ///
        /// used by InkEditor during editing operations
        /// </summary>
        /// <returns>true if selection was cleared even after raising selectionchanging</returns>
        internal bool ClearSelectionRaiseSelectionChanging()
        {
            if ( !InkCanvasSelection.HasSelection )
            {
                return true;
            }
 
            //
            // attempt to clear selection
            //
            ChangeInkCanvasSelection(new StrokeCollection(), new UIElement[]{});
 
            return !InkCanvasSelection.HasSelection;
        }
 
        /// <summary>
        /// ClearSelection
        ///     Called by:
        ///         PasteFromDataObject
        ///         EditingCoordinator.UpdateEditingState
        /// </summary>
        internal void ClearSelection(bool raiseSelectionChangedEvent)
        {
            if ( InkCanvasSelection.HasSelection )
            {
                // Reset the current selection
                CoreChangeSelection(new StrokeCollection(), new UIElement[] { }, raiseSelectionChangedEvent);
            }
        }
 
 
        /// <summary>
        /// Helper that creates selection for an InkCanvas.  Used by the SelectedStrokes and
        /// SelectedElements properties
        /// </summary>
        internal void ChangeInkCanvasSelection(StrokeCollection strokes, UIElement[] elements)
        {
            //validate in debug only for this internal static
            Debug.Assert(strokes != null
                        && elements != null,
                        "Invalid arguments in ChangeInkCanvasSelection");
 
            bool strokesAreDifferent;
            bool elementsAreDifferent;
            InkCanvasSelection.SelectionIsDifferentThanCurrent(strokes, out strokesAreDifferent, elements, out elementsAreDifferent);
            if ( strokesAreDifferent || elementsAreDifferent )
            {
                InkCanvasSelectionChangingEventArgs args = new InkCanvasSelectionChangingEventArgs(strokes, elements);
 
                StrokeCollection validStrokes = strokes;
                UIElement[] validElements = elements;
 
                this.RaiseSelectionChanging(args);
 
                //now that the event has been raised and all of the delegates
                //have had their way with it, process the result
                if ( !args.Cancel )
                {
                    //
                    // rock and roll, time to validate our arguments
                    // note: these event args are visible outside the apis,
                    // so we need to validate them again
                    //
 
                    // PERF-2006/05/02-WAYNEZEN,
                    // Check our internal flag. If the SelectedStrokes has been changed, we shouldn't do any extra work here.
                    if ( args.StrokesChanged )
                    {
                        validStrokes = ValidateSelectedStrokes(args.GetSelectedStrokes());
                        int countOldSelectedStrokes = strokes.Count;
                        for ( int i = 0; i < countOldSelectedStrokes; i++ )
                        {
                            // PERF-2006/05/02-WAYNEZEN,
                            // We only have to reset IsSelected for the elements no longer exists in the new collection.
                            if ( !validStrokes.Contains(strokes[i]) )
                            {
                                //
                                // Make sure we reset the IsSelected property which could have been
                                // set to true in the dynamic selection.
                                strokes[i].IsSelected = false;
                            }
                        }
                    }
 
 
                    // PERF-2006/05/02-WAYNEZEN,
                    // Check our internal flag. If the SelectedElements has been changed, we shouldn't do any extra work here.
                    if ( args.ElementsChanged )
                    {
                        validElements = ValidateSelectedElements(args.GetSelectedElements());
                    }
 
                    CoreChangeSelection(validStrokes, validElements, true);
                }
                else
                {
                    StrokeCollection currentSelectedStrokes = InkCanvasSelection.SelectedStrokes;
                    int countOldSelectedStrokes = strokes.Count;
                    for ( int i = 0; i < countOldSelectedStrokes; i++ )
                    {
                        // Make sure we reset the IsSelected property which could have been
                        // set to true in the dynamic selection but not being selected previously.
                        if ( !currentSelectedStrokes.Contains(strokes[i]) )
                        {
                            strokes[i].IsSelected = false;
                        }
                    }
                }
            }
        }
 
 
        /// <summary>
        /// Helper method used by ChangeInkCanvasSelection and directly by ClearSelectionWithoutSelectionChanging
        /// </summary>
        /// <param name="validStrokes">validStrokes</param>
        /// <param name="validElements">validElements</param>
        /// <param name="raiseSelectionChanged">raiseSelectionChanged</param>
        private void CoreChangeSelection(StrokeCollection validStrokes, IList<UIElement> validElements, bool raiseSelectionChanged)
        {
            InkCanvasSelection.Select(validStrokes, validElements, raiseSelectionChanged);
        }
 
#if DEBUG_LASSO_FEEDBACK
        internal ContainerVisual RendererRootContainer
        {
            get {return _inkContainerVisual;}
        }
#endif
 
 
        /// <summary>
        /// Private helper method used to retrieve a StrokeCollection which does AND operation on two input collections.
        /// </summary>
        /// <param name="subset">The possible subset</param>
        /// <param name="superset">The container set</param>
        /// <returns>True if subset is a subset of superset, false otherwise</returns>
        internal static StrokeCollection GetValidStrokes(StrokeCollection subset, StrokeCollection superset)
        {
            StrokeCollection validStrokes = new StrokeCollection();
 
            int subsetCount = subset.Count;
 
            // special case an empty subset as a guaranteed subset
            if ( subsetCount == 0 )
            {
                return validStrokes;
            }
 
            for ( int i = 0; i < subsetCount; i++ )
            {
                Stroke stroke = subset[i];
                if ( superset.Contains(stroke) )
                {
                    validStrokes.Add(stroke);
                }
            }
 
            return validStrokes;
        }
 
        /// <summary>
        /// Register the commanding handlers for the clipboard operations
        /// </summary>
        private static void _RegisterClipboardHandlers()
        {
            Type ownerType = typeof(InkCanvas);
 
            CommandHelpers.RegisterCommandHandler(ownerType, ApplicationCommands.Cut,
                new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled),
                KeyGesture.CreateFromResourceStrings(KeyShiftDelete, nameof(SR.KeyShiftDeleteDisplayString)));
            CommandHelpers.RegisterCommandHandler(ownerType, ApplicationCommands.Copy,
                new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled),
                KeyGesture.CreateFromResourceStrings(KeyCtrlInsert, nameof(SR.KeyCtrlInsertDisplayString)));
 
            // Use temp variables to reduce code under elevation
            ExecutedRoutedEventHandler pasteExecuteEventHandler = new ExecutedRoutedEventHandler(_OnCommandExecuted);
            CanExecuteRoutedEventHandler pasteQueryEnabledEventHandler = new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled);
            InputGesture pasteInputGesture = KeyGesture.CreateFromResourceStrings(KeyShiftInsert, SR.KeyShiftInsertDisplayString);
 
            CommandHelpers.RegisterCommandHandler(ownerType, ApplicationCommands.Paste,
                pasteExecuteEventHandler, pasteQueryEnabledEventHandler, pasteInputGesture);
        }
 
        /// <summary>
        /// Private helper used to ensure that any stroke collection
        /// passed to the InkCanvas is valid.  Throws exceptions if the argument is invalid
        /// </summary>
        private StrokeCollection ValidateSelectedStrokes(StrokeCollection strokes)
        {
            //
            //  null is a valid input
            //
            if (strokes == null)
            {
                return new StrokeCollection();
            }
            else
            {
                return GetValidStrokes(strokes, this.Strokes);
            }
        }
 
        /// <summary>
        /// Private helper used to ensure that a UIElement argument passed in
        /// is valid.
        /// </summary>
        private UIElement[] ValidateSelectedElements(IEnumerable<UIElement> selectedElements)
        {
            if (selectedElements == null)
            {
                return new UIElement[]{};
            }
 
            List<UIElement> elements = new List<UIElement>();
            foreach (UIElement element in selectedElements)
            {
                //
                // Don't add the duplicated element.
                if ( !elements.Contains(element) )
                {
                    //
                    // check the common case first, e
                    //
                    if ( InkCanvasIsAncestorOf(element) )
                    {
                        elements.Add(element);
                    }
                }
            }
 
            return elements.ToArray();
        }
 
        /// <summary>
        /// Helper method used by DesignActivation to see if an element
        /// has this InkCanvas as an Ancestor
        /// </summary>
        private bool InkCanvasIsAncestorOf(UIElement element)
        {
            if (this != element && this.IsAncestorOf(element))
            {
                return true;
            }
            return false;
        }
 
        /// <summary>
        /// Handler called whenever changes are made to properties of the DefaultDrawingAttributes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <remarks>For example, when a developer changes the Color property on the DefaultDrawingAttributes,
        /// this event will fire - allowing the InkCanvas a chance to notify the BasicRTI Service.
        /// Also - we do not currently call through the DefaultDrawingAttributes setter since
        /// parameter validation in the setter may detect if the reference isn't changing, and ignore
        /// the call. Also - there is no need for extra parameter validation.</remarks>
        private void DefaultDrawingAttributes_Changed(object sender, PropertyDataChangedEventArgs args)
        {
            // note that sender should be the same as _defaultDrawingAttributes
            // If a developer writes code to change the DefaultDrawingAttributes inside of the event
            //      handler before the InkCanvas receives the notification (multi-cast delegate scenario) -
            //      The DefaultDrawingAttributes should still be updated, and in that case we would
            //      update the RTI DAC twice. Typically, however, this will just refresh the
            //      attributes in the RTI thread.
            System.Diagnostics.Debug.Assert(object.ReferenceEquals(sender, DefaultDrawingAttributes));
 
            InvalidateSubProperty(DefaultDrawingAttributesProperty);
 
            // Be sure to update the RealTimeInking PlugIn with the drawing attribute changes.
            UpdateDynamicRenderer();
 
            // Invalidate the inking cursor
            _editingCoordinator.InvalidateBehaviorCursor(_editingCoordinator.InkCollectionBehavior);
        }
 
        /// <summary>
        /// Helper method used to set up the DynamicRenderer.
        /// </summary>
        internal void UpdateDynamicRenderer()
        {
            UpdateDynamicRenderer(DefaultDrawingAttributes);
        }
        /// <summary>
        /// Helper method used to set up the DynamicRenderer.
        /// </summary>
        private void UpdateDynamicRenderer(DrawingAttributes newDrawingAttributes)
        {
            ApplyTemplate();
 
            if (this.DynamicRenderer != null)
            {
                this.DynamicRenderer.DrawingAttributes = newDrawingAttributes;
 
                if (!this.InkPresenter.AttachedVisualIsPositionedCorrectly(this.DynamicRenderer.RootVisual, newDrawingAttributes))
                {
                    if (this.InkPresenter.ContainsAttachedVisual(this.DynamicRenderer.RootVisual))
                    {
                        this.InkPresenter.DetachVisuals(this.DynamicRenderer.RootVisual);
                    }
 
                    // Only hook up if we are enabled.  As we change editing modes this routine will be called
                    // to clean up things.
                    if (this.DynamicRenderer.Enabled && this.DynamicRenderer.RootVisual != null)
                    {
                        this.InkPresenter.AttachVisuals(this.DynamicRenderer.RootVisual, newDrawingAttributes);
                    }
                }
            }
        }
 
        private bool EnsureActiveEditingMode(InkCanvasEditingMode newEditingMode)
        {
            bool ret = true;
 
            if ( ActiveEditingMode != newEditingMode )
            {
                if ( EditingCoordinator.IsStylusInverted )
                {
                    EditingModeInverted = newEditingMode;
                }
                else
                {
                    EditingMode = newEditingMode;
                }
 
                // Verify whether user has cancelled the change in EditingModeChanging event.
                ret = ( ActiveEditingMode == newEditingMode );
            }
 
            return ret;
        }
 
        // The ClipboardProcessor instance which deals with the operations relevant to the clipboard.
        private ClipboardProcessor ClipboardProcessor
        {
            get
            {
                if ( _clipboardProcessor == null )
                {
                    _clipboardProcessor = new ClipboardProcessor(this);
                }
 
                return _clipboardProcessor;
            }
        }
 
        //lazy instance the gesture recognizer
        private GestureRecognizer GestureRecognizer
        {
            get
            {
                if (_gestureRecognizer == null)
                {
                    _gestureRecognizer = new GestureRecognizer();
                }
                return _gestureRecognizer;
            }
        }
 
        /// <summary>
        /// Delete the current selection
        /// </summary>
        private void DeleteCurrentSelection(bool removeSelectedStrokes, bool removeSelectedElements)
        {
            Debug.Assert(removeSelectedStrokes || removeSelectedElements, "At least either Strokes or Elements should be removed!");
 
            // Now delete the current selection.
            StrokeCollection strokes = GetSelectedStrokes();
            IList<UIElement> elements = GetSelectedElements();
 
            // Clear the selection first.
            CoreChangeSelection(
                removeSelectedStrokes ? new StrokeCollection() : strokes,
                removeSelectedElements ? new List<UIElement>() : elements,
                true);
 
            // Remove the ink.
            if ( removeSelectedStrokes && strokes != null && strokes.Count != 0 )
            {
                Strokes.Remove(strokes);
            }
 
            // Remove the elements.
            if ( removeSelectedElements )
            {
                UIElementCollection children = Children;
                foreach ( UIElement element in elements )
                {
                    children.Remove(element);
                }
            }
        }
 
        /// <summary>
        /// A class handler of the Commands
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private static void _OnCommandExecuted(object sender, ExecutedRoutedEventArgs args)
        {
            ICommand command = args.Command;
            InkCanvas inkCanvas = sender as InkCanvas;
 
            Debug.Assert(inkCanvas != null);
 
            if ( inkCanvas.IsEnabled && !inkCanvas.EditingCoordinator.UserIsEditing )
            {
                if ( command == ApplicationCommands.Delete )
                {
                    inkCanvas.DeleteCurrentSelection(true, true);
                }
                else if ( command == ApplicationCommands.Cut )
                {
                    inkCanvas.CutSelection();
                }
                else if ( command == ApplicationCommands.Copy )
                {
                    inkCanvas.CopySelection();
                }
                else if ( command == ApplicationCommands.SelectAll )
                {
                    if ( inkCanvas.ActiveEditingMode == InkCanvasEditingMode.Select )
                    {
                        IEnumerable<UIElement> children = null;
                        UIElementCollection uiElementCollection = inkCanvas.Children;
                        if ( uiElementCollection.Count > 0 )
                        {
                            //UIElementCollection doesn't implement IEnumerable<UIElement>
                            //for some reason
                            UIElement[] uiElementArray = new UIElement[uiElementCollection.Count];
                            for ( int i = 0; i < uiElementCollection.Count; i++ )
                            {
                                uiElementArray[i] = uiElementCollection[i];
                            }
                            children = uiElementArray;
                        }
                        inkCanvas.Select(inkCanvas.Strokes, children);
                    }
                }
                else if ( command == ApplicationCommands.Paste )
                {
                    try
                    {
                        inkCanvas.Paste();
                    }
                    // Eat it and do nothing if one of the following exceptions is caught.
                    catch ( System.Runtime.InteropServices.COMException )
                    {
                        // The window may be destroyed which could cause the opening failed..
                    }
                    catch ( XamlParseException )
                    {
                        // The Xaml parser fails
                    }
                    catch ( ArgumentException )
                    {
                        // The ISF decoder fails
                    }
                }
                else if ( command == InkCanvas.DeselectCommand )
                {
                    inkCanvas.ClearSelectionRaiseSelectionChanging();
                }
            }
        }
 
        /// <summary>
        /// A class handler for querying the enabled status of the commands.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private static void _OnQueryCommandEnabled(object sender, CanExecuteRoutedEventArgs args)
        {
            RoutedCommand command = (RoutedCommand)(args.Command);
            InkCanvas inkCanvas = sender as InkCanvas;
 
            Debug.Assert(inkCanvas != null);
 
            if ( inkCanvas.IsEnabled
                //
                // If user is editing, we should disable all commands.
                && !inkCanvas.EditingCoordinator.UserIsEditing )
            {
                if ( command == ApplicationCommands.Delete
                    || command == ApplicationCommands.Cut
                    || command == ApplicationCommands.Copy
                    || command == InkCanvas.DeselectCommand )
                {
                    args.CanExecute = inkCanvas.InkCanvasSelection.HasSelection;
                }
                else if ( command == ApplicationCommands.Paste )
                {
                    try
                    {
                        args.CanExecute = args.UserInitiated
                                            ? inkCanvas.UserInitiatedCanPaste() /* Call UserInitiatedCanPaste when the query is initiated by user */
                                            : inkCanvas.CanPaste() /* Call the public CanPaste if not */;
                    }
                    catch ( System.Runtime.InteropServices.COMException )
                    {
                        // The window may be destroyed which could cause the opening failed..
                        // Eat the exception and do nothing.
                        args.CanExecute = false;
                    }
                }
                else if ( command == ApplicationCommands.SelectAll )
                {
                    //anything to select?
                    args.CanExecute = ( inkCanvas.ActiveEditingMode == InkCanvasEditingMode.Select
                                            && (inkCanvas.Strokes.Count > 0 || inkCanvas.Children.Count > 0));
                }
            }
            else
            {
                //
                // Return false for CanExecute if InkCanvas is disabled.
                args.CanExecute = false;
            }
 
            //
            // Mark Handled as true so that the clipboard commands stops routing to InkCanvas' ancestors.
            if ( command == ApplicationCommands.Cut || command == ApplicationCommands.Copy
                || command == ApplicationCommands.Paste )
            {
                args.Handled = true;
            }
        }
 
        private InkCanvasClipboardDataFormats PrivateCopySelection()
        {
            InkCanvasClipboardDataFormats copiedDataFormats = InkCanvasClipboardDataFormats.None;
 
            // Don't even bother if we don't have a selection or UserIsEditing has been set.
            if ( InkCanvasSelection.HasSelection && !_editingCoordinator.UserIsEditing)
            {
                copiedDataFormats = CopyToDataObject();
            }
 
            return copiedDataFormats;
        }
 
 
        /// <summary>
        /// _OnDeviceDown
        /// </summary>
        /// <typeparam name="TEventArgs"></typeparam>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void _OnDeviceDown<TEventArgs>(object sender, TEventArgs e)
            where TEventArgs : InputEventArgs
        {
            ( (InkCanvas)sender ).EditingCoordinator.OnInkCanvasDeviceDown(sender, e);
        }
 
        /// <summary>
        /// _OnDeviceUp
        /// </summary>
        /// <typeparam name="TEventArgs"></typeparam>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void _OnDeviceUp<TEventArgs>(object sender, TEventArgs e)
            where TEventArgs : InputEventArgs
        {
            ((InkCanvas)sender).EditingCoordinator.OnInkCanvasDeviceUp(sender, e);
        }
 
        /// <summary>
        /// _OnQueryCursor
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void _OnQueryCursor(object sender, QueryCursorEventArgs e)
        {
            InkCanvas inkCanvas = (InkCanvas)sender;
 
            if ( inkCanvas.UseCustomCursor )
            {
                // If UseCustomCursor is set, we bail out. Let the base class (FrameworkElement) to do the rest.
                return;
            }
 
            // We should behave like our base - honor ForceCursor property.
            if ( !e.Handled || inkCanvas.ForceCursor )
            {
                Cursor cursor = inkCanvas.EditingCoordinator.GetActiveBehaviorCursor();
 
                // If cursor is null, we don't handle the event and leave it as whatever the default is.
                if ( cursor != null )
                {
                    e.Cursor = cursor;
                    e.Handled = true;
                }
            }
        }
 
        /// <summary>
        /// Update the current cursor if mouse is over InkCanvas. Called by
        ///     EditingCoordinator.InvalidateBehaviorCursor
        ///     EditingCoordinator.UpdateEditingState
        ///     InkCanvas.set_UseCustomCursor
        /// </summary>
        internal void UpdateCursor()
        {
            if ( IsMouseOver )
            {
                Mouse.UpdateCursor();
            }
        }
 
        #endregion Private Properties / Methods
 
        //------------------------------------------------------
        //
        //  Private Classes
        //
        //------------------------------------------------------
 
        #region Private Classes
 
 
        /// <summary>
        /// A helper class for RTI high contrast support
        /// </summary>
        private class RTIHighContrastCallback : HighContrastCallback
        {
            //------------------------------------------------------
            //
            //  Cnostructors
            //
            //------------------------------------------------------
 
            #region Constructors
 
            internal RTIHighContrastCallback(InkCanvas inkCanvas)
            {
                _thisInkCanvas = inkCanvas;
            }
 
            private RTIHighContrastCallback() { }
 
            #endregion Constructors
 
            //------------------------------------------------------
            //
            //  Internal Methods
            //
            //------------------------------------------------------
 
            #region Internal Methods
 
            /// <summary>
            /// TurnHighContrastOn
            /// </summary>
            /// <param name="highContrastColor"></param>
            internal override void TurnHighContrastOn(Color highContrastColor)
            {
                // The static strokes already have been taken care of by InkPresenter.
                // We only update the RTI renderer here.
                DrawingAttributes highContrastDa = _thisInkCanvas.DefaultDrawingAttributes.Clone();
                highContrastDa.Color = highContrastColor;
                _thisInkCanvas.UpdateDynamicRenderer(highContrastDa);
            }
 
            /// <summary>
            /// TurnHighContrastOff
            /// </summary>
            internal override void TurnHighContrastOff()
            {
                // The static strokes already have been taken care of by InkPresenter.
                // We only update the RTI renderer here.
                _thisInkCanvas.UpdateDynamicRenderer(_thisInkCanvas.DefaultDrawingAttributes);
            }
 
            #endregion Internal Methods
 
            //------------------------------------------------------
            //
            //  Internal Properties
            //
            //------------------------------------------------------
 
            #region Internal Properties
 
            /// <summary>
            /// Returns the dispatcher if the object is associated to a UIContext.
            /// </summary>
            internal override Dispatcher Dispatcher
            {
                get
                {
                    return _thisInkCanvas.Dispatcher;
                }
            }
 
            #endregion Internal Properties
 
            //------------------------------------------------------
            //
            //  Private Fields
            //
            //------------------------------------------------------
 
            #region Private Fields
 
            private InkCanvas _thisInkCanvas;
 
            #endregion Private Fields
        }
 
        /// <summary>
        /// This is a binding converter which translates the InkCanvas.ActiveEditingMode to UIElement.Visibility.
        /// </summary>
        private class ActiveEditingMode2VisibilityConverter : IValueConverter
        {
            public object Convert(object o, Type type, object parameter, System.Globalization.CultureInfo culture)
            {
                InkCanvasEditingMode activeMode = (InkCanvasEditingMode)o;
 
                // If the current EditingMode is the mode which menuitem is expecting, return true for IsChecked.
                if ( activeMode != InkCanvasEditingMode.None )
                {
                    return Visibility.Visible;
                }
                else
                {
                    return Visibility.Collapsed;
                }
            }
 
            public object ConvertBack(object o, Type type, object parameter, System.Globalization.CultureInfo culture)
            {
                // Non-reversed convertion
                return null;
            }
        }
 
        #endregion Private Classes
 
        #region Private Members
 
        /// <summary>
        /// The element that represents the selected ink strokes, if any exist.  Will frequently be null.
        /// </summary>
        private InkCanvasSelection          _selection = null;
        private InkCanvasSelectionAdorner   _selectionAdorner = null;
        private InkCanvasFeedbackAdorner    _feedbackAdorner = null;
 
        /// <summary>
        /// The internal Canvas used to hold elements
        /// </summary>
        private InkCanvasInnerCanvas        _innerCanvas = null;
 
        /// <summary>
        /// The internal private AdornerDecorator
        /// </summary>
        private AdornerDecorator            _localAdornerDecorator = null;
 
        /// <summary>
        /// Runtime Selection StrokeCollection
        /// </summary>
        private StrokeCollection            _dynamicallySelectedStrokes;
 
        /// <summary>
        /// Our editing logic
        /// </summary>
        private EditingCoordinator          _editingCoordinator;
 
        /// <summary>
        /// Defines the default StylusPointDescription
        /// </summary>
        private StylusPointDescription     _defaultStylusPointDescription;
 
 
        /// <summary>
        /// Defines the shape of the eraser tip
        /// </summary>
        private StylusShape                 _eraserShape;
 
        /// <summary>
        /// Determines if EditingBehaviors should use their own cursor or a custom one specified.
        /// </summary>
        private bool                        _useCustomCursor = false;
 
 
        //
        // Rendering support.
        //
        private InkPresenter                _inkPresenter;
 
        //
        // The RealTimeInking PlugIn that handles our off UIContext rendering.
        //
        private DynamicRenderer             _dynamicRenderer;
 
        //
        // Clipboard Helper
        //
        private ClipboardProcessor          _clipboardProcessor;
 
        //
        // Gesture support
        //
        private GestureRecognizer           _gestureRecognizer;
 
        //
        // HighContrast support
        //
        private RTIHighContrastCallback     _rtiHighContrastCallback;
 
        private const double                    c_pasteDefaultLocation = 0.0;
 
        private const string InkCanvasDeselectKey   = "Esc";
        private const string KeyCtrlInsert = "Ctrl+Insert";
        private const string KeyShiftInsert = "Shift+Insert";
        private const string KeyShiftDelete = "Shift+Delete";
 
        #endregion Private Members
    }
}