CaretElement.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Documents / CaretElement.cs / 2 / CaretElement.cs

                            //---------------------------------------------------------------------------- 
//
// File: CaretElement.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
// Description: Caret rendering visual. 
// 
//---------------------------------------------------------------------------
 
namespace System.Windows.Documents
{
    using System.Security; // SecurityCritical, SecurityTreatAsSafe
    using System.Security.Permissions; // UIPermission 
    using System.Windows.Media; // Brush, Transform
    using System.Windows.Media.Animation; // AnimationClock 
    using System.Windows.Controls; // ScrollViewer 
    using MS.Win32; // SafeNativeMethods
    using MS.Internal; // DoubleUtil.AreClose(), Invariant.Assert 
    using System.Runtime.InteropServices; // HandleRef
    using System.Collections.Generic; // List
    using System.Windows.Interop;
 

// Disable pragma warnings to enable PREsharp pragmas 
#pragma warning disable 1634, 1691 

    ///  
    /// This class is sealed because it calls OnVisualChildrenChanged virtual in the
    /// constructor and it does not override it, but derived classes could.
    /// 
    internal sealed class CaretElement : Adorner 
    {
        //----------------------------------------------------- 
        // 
        //  Constructors
        // 
        //-----------------------------------------------------

        #region Constructors
 
        /// 
        /// Creates new instance of CaretElement. 
        ///  
        /// 
        /// TextEditor that owns this Adorner. 
        /// 
        /// 
        /// Blinking for caret animation. Drag target caret does not need blinking,
        ///  
        internal CaretElement(TextEditor textEditor, bool isBlinkEnabled) : base(textEditor.TextView.RenderScope)
        { 
            Invariant.Assert(textEditor.TextView != null && textEditor.TextView.RenderScope != null, "Assert: textView != null && RenderScope != null"); 

            _textEditor = textEditor; 

            // Set the animation whether do it or not.
            _isBlinkEnabled = isBlinkEnabled;
 
            // caret position
            _left = 0.0; 
            _top = 0.0; 

            // caret dimensions 
            _systemCaretWidth = SystemParameters.CaretWidth;
            _height = 0.0;

            // Set AllowDropProperty as "False" not to inherit the value from the ancestor. 
            AllowDrop = false;
 
            _caretElement = new CaretSubElement(); 
            _caretElement.ClipToBounds = false;
            _caretElement.SnapsToDevicePixels = true; 

            AddVisualChild(_caretElement);
        }
 
        #endregion Constructors
 
        //------------------------------------------------------ 
        //
        //  Public Methods 
        //
        //-----------------------------------------------------

        #region Public Methods 

        #endregion Public Methods 
 
        //------------------------------------------------------
        // 
        //  Protected Methods
        //
        //------------------------------------------------------
 
        #region Protected Methods
 
        ///  
        /// Returns the Visual children count.
        ///  
        protected override int VisualChildrenCount
        {
            get { return (_caretElement == null) ? 0 : 1; }
        } 

        ///  
        /// Returns the child at the specified index. 
        /// 
        protected override Visual GetVisualChild(int index) 
        {
            if (index != 0)
            {
                throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange)); 
            }
 
            return _caretElement; 
        }
 
        // HitTestCore override not to hit testable Caret.
        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
        {
            // Return null not to hit testable for CaretElement. 
            return null;
        } 
 
        // Render override -- we render the caret here.
        protected override void OnRender(DrawingContext drawingContext) 
        {
            if (_selectionGeometry != null)
            {
                Brush selectionBrush = new SolidColorBrush(SystemColors.HighlightColor); 
                selectionBrush.Opacity = 0.4;
                selectionBrush.Freeze(); 
 
                Pen selectionOutlinePen = null;
 
                drawingContext.DrawGeometry(selectionBrush, selectionOutlinePen, _selectionGeometry);
            }
        }
 
        /// 
        /// Measurement override. 
        ///  
        /// 
        /// Available size for the component 
        /// 
        /// 
        /// Return the size of the caret
        ///  
        protected override Size MeasureOverride(Size availableSize)
        { 
            base.MeasureOverride(availableSize); 
            _caretElement.InvalidateVisual();
 
            // Return the available width and height. Please don't
            // return AdornedElement.RenderSize since it will be scrolled
            // in case of the reder size is greater than the available size.
            // Reference bug#1068444. 

            // We choose to return an arbitrary large value of double.MaxValue/2 in case of infinite height/width. 
            // This is safer, because adding (even zero) margin to double.MaxValue causes available size to become infinite again. 
            // UIElement.Measure enforces that MeasureCore can not return PositiveInfinity size even if given Infinite available size.
            return new Size( 
                double.IsInfinity(availableSize.Width) ? double.MaxValue/2 : availableSize.Width,
                double.IsInfinity(availableSize.Height) ? double.MaxValue/2 : availableSize.Height);
            // Note we do not use _systemCaretWidth for caret width,
            // because italic caret would be clipped in this case. 
            // We use maximum available visible width - as for height.
        } 
 
        /// 
        /// Arrange override. 
        /// 
        /// 
        /// Available size for the component
        ///  
        /// 
        /// Return the size of the caret 
        ///  
        protected override Size ArrangeOverride(Size availableSize)
        { 
            Point point;

            if (_pendingGeometryUpdate)
            { 
                ((TextSelection)_textEditor.Selection).UpdateCaretState(CaretScrollMethod.None);
                _pendingGeometryUpdate = false; 
            } 

            point = new Point(_left, _top); 

            _caretElement.Arrange(new Rect(point, availableSize));

            return availableSize; 
        }
 
#endregion Protected Events 

        //----------------------------------------------------- 
        //
        //  Internal Methods
        //
        //------------------------------------------------------ 

        #region Internal Methods 
 
        /// 
        /// Invalidates the caret render. 
        /// Used in TextSelection (for caret) and in TextEditor (for drag target)
        /// 
        /// 
        ///  
        /// 
        /// Rectangle relative to AdornedElement (textview) where caret should be drawn. 
        /// Width must be zero to apply the system caret width except the interim caret that has 
        /// the interim caret width.
        ///  
        /// 
        /// Brush for drawing the caret
        /// 
        ///  
        /// Request to italic caret.
        ///  
        ///  
        /// Determines how the caret is scrolled into view.
        ///  
        /// 
        /// Request to scroll caret position with the scroll origin position.
        /// 
        internal void Update(bool visible, Rect caretRectangle, Brush caretBrush, bool italic, CaretScrollMethod scrollMethod, double scrollToOriginPosition) 
        {
            double newLeft; 
            double newTop; 
            double newHeight;
            double newWidth; 
            bool positionChanged;

            Invariant.Assert(caretBrush != null, "Assert: caretBrush != null");
 
            // Make sure we're attached to the view.  We have to delay this work
            // until now because if we swap out a render scope on the fly, the 
            // new one won't be hooked up to the visual tree until after its 
            // style is rebuilt, which happens on an async layout update.
            EnsureAttachedToView(); 

            // Enforce caret refresh for the case when it appears after invisible state
            bool justAppearing = visible && !_showCaret;
 
            if (_showCaret != visible)
            { 
                InvalidateVisual(); 
                _showCaret = visible;
            } 

            // Update the caret brush.
            _caretBrush = caretBrush;
 
            // Define new coordinates and dimensions of the caret.
            // We don't consider caret visibility here because even if the 
            // caret is hidden, we need to calc the geometry info to scroll 
            // the active edge of the selection into view.
 
            if (caretRectangle.IsEmpty || caretRectangle.Height <= 0)
            {
                newLeft = 0;
                newTop = 0; 
                newHeight = 0;
                newWidth = 0; 
            } 
            else
            { 
                newLeft = caretRectangle.X;
                newTop = caretRectangle.Y;
                newHeight = caretRectangle.Height;
                newWidth = SystemParameters.CaretWidth; 
            }
 
            // Initialize flag requiring to refresh the caret 
            positionChanged = justAppearing || italic != _italic;
 
            if (!DoubleUtil.AreClose(_left, newLeft))
            {
                _left = newLeft;
                positionChanged = true; 
            }
            if (!DoubleUtil.AreClose(_top, newTop)) 
            { 
                _top = newTop;
                positionChanged = true; 
            }
            if (!caretRectangle.IsEmpty && _interimWidth != caretRectangle.Width)
            {
                _interimWidth = caretRectangle.Width; 
                positionChanged = true;
            } 
            if (!DoubleUtil.AreClose(_systemCaretWidth, newWidth)) 
            {
                _systemCaretWidth = newWidth; 
                positionChanged = true;
            }
            if (!DoubleUtil.AreClose(_height, newHeight))
            { 
                _height = newHeight;
 
                InvalidateMeasure(); 
            }
 
            // Refresh caret and ensure the caret to the view if the caret position is changed or
            // caret is currently out of view area which scrollToOriginPosition is set.
            // scrollToOriginPosition will be set properly to view the caret correctly if caret is
            // currently out of view boundary. For example, typing bidi characters on LTR flow direction 
            // or typing western characters on RTL flow direction from the out of view.
            if (positionChanged || !double.IsNaN(scrollToOriginPosition)) 
            { 
                _scrolledToCurrentPositionYet = false;
                RefreshCaret(italic); 
            }

            if (scrollMethod != CaretScrollMethod.None && !_scrolledToCurrentPositionYet)
            { 
                Rect scrollRectangle;
 
                //Set the interim width to show the interim caret in the interim mode. 
                // We're providing enough space to also take care of an italic or bidi caret.
                scrollRectangle = new Rect(_left - CaretPaddingWidth, _top, CaretPaddingWidth * 2 + (IsInInterimState ? _interimWidth : _systemCaretWidth), _height); 

                // If we're scrolling to one edge of the document or another,
                //  - Going backward, we want to scroll into view the left edge
                //    of the rect.  This is the default. 
                //  - Going forward, we want to scroll into view the right edge
                //    of the rect, which takes some adjustment. 
                if (!double.IsNaN(scrollToOriginPosition) && scrollToOriginPosition > 0) 
                {
                    scrollRectangle.X += scrollRectangle.Width; 
                    scrollRectangle.Width = 0;
                }

                switch (scrollMethod) 
                {
                    case CaretScrollMethod.Simple: 
                        DoSimpleScrollToView(scrollToOriginPosition, scrollRectangle); 
                        break;
 
                    case CaretScrollMethod.Navigation:
                        DoNavigationalScrollToView(scrollToOriginPosition, scrollRectangle);
                        break;
                } 
                _scrolledToCurrentPositionYet = true;
            } 
 
            // Skip the animation if the animation isn't set. E.g. DragDrop caret.
            SetBlinkAnimation(visible, positionChanged); 
        }

        // Scroll the caret rect into view with no allowance for surrounding text.
        private void DoSimpleScrollToView(double scrollToOriginPosition, Rect scrollRectangle) 
        {
            // Scroll to the original position first to ensure of displaying the wrapped word with 
            // the caret position. The scrollToOriginPosition is already base on both LTR and RTL 
            // flow direction.
            if (!double.IsNaN(scrollToOriginPosition)) 
            {
                MS.Internal.Documents.TextViewBase.BringRectIntoViewMinimally(_textEditor.TextView, new Rect(scrollToOriginPosition, scrollRectangle.Y, scrollRectangle.Width, scrollRectangle.Height));

                // Since we've moved the viewport, and scrollRectangle is relative to the viewport. scrollRectangle 
                // is no longer correct.  Adjust it by the distance we scrolled to make it correct.
                scrollRectangle.X -= scrollToOriginPosition; 
            } 

            // Now scroll to the caret position 
            MS.Internal.Documents.TextViewBase.BringRectIntoViewMinimally(_textEditor.TextView, scrollRectangle);
        }

        // Scroll the caret rect into view, adding a "buffer" of surrounding text 
        // porportional to the viewport size.
        private void DoNavigationalScrollToView(double scrollToOriginPosition, Rect targetRect) 
        { 
            // Find the scroller from the render scope
            ScrollViewer scroller = _textEditor._Scroller as ScrollViewer; 

            if (scroller != null)
            {
                Point targetPoint = new Point(targetRect.Left, targetRect.Top); 

                GeneralTransform transform = _textEditor.TextView.RenderScope.TransformToAncestor(scroller); 
 
                if (transform.TryTransform(targetPoint, out targetPoint))
                { 
                    double scrollerWidth = scroller.ViewportWidth;
                    double scrollerHeight = scroller.ViewportHeight;
                    double deltaToScroll;
 
                    // Scroll up or down if the moving position.Y is outside of viewport
                    if (targetPoint.Y < 0 || targetPoint.Y + targetRect.Height > scrollerHeight) 
                    { 
                        if (targetPoint.Y < 0)
                        { 
                            // Scroll up
                            deltaToScroll = Math.Abs(targetPoint.Y);
                            scroller.ScrollToVerticalOffset(Math.Max(0, scroller.VerticalOffset - deltaToScroll - scrollerHeight / 4));
                        } 
                        else
                        { 
                            // Scroll down 
                            deltaToScroll = targetPoint.Y + targetRect.Height - scrollerHeight;
                            scroller.ScrollToVerticalOffset(Math.Min(scroller.ExtentHeight, scroller.VerticalOffset + deltaToScroll + scrollerHeight / 4)); 
                        }
                    }

                    // Scroll line left or right if the moving position.X is outside of viewport 
                    if (targetPoint.X < 0 || targetPoint.X > scrollerWidth)
                    { 
                        if (targetPoint.X < 0) 
                        {
                            // Scroll the left line 
                            deltaToScroll = Math.Abs(targetPoint.X);
                            scroller.ScrollToHorizontalOffset(Math.Max(0, scroller.HorizontalOffset - deltaToScroll - scrollerWidth / 4));
                        }
                        else 
                        {
                            // Scroll the right line 
                            deltaToScroll = targetPoint.X - scrollerWidth; 
                            scroller.ScrollToHorizontalOffset(Math.Min(scroller.ExtentWidth, scroller.HorizontalOffset + deltaToScroll + scrollerWidth / 4));
                        } 
                    }
                }
            }
            else 
            {
                // 
 

 
                if (!_textEditor.Selection.MovingPosition.HasValidLayout && _textEditor.TextView != null && _textEditor.TextView.IsValid)
                {
                    DoSimpleScrollToView(scrollToOriginPosition, targetRect);
                } 
            }
        } 
 
        /// 
        /// Updates selection geometry. 
        /// 
        internal void UpdateSelection()
        {
            Geometry previousSelectionGeometry = _selectionGeometry; 

            _selectionGeometry = null; 
 
            if (!_textEditor.Selection.IsEmpty)
            { 
                EnsureAttachedToView();

                List textSegments = _textEditor.Selection.TextSegments;
 
                for (int i = 0; i < textSegments.Count; i++)
                { 
                    TextSegment segment = textSegments[i]; 
                    Geometry geometry = _textEditor.Selection.TextView.GetTightBoundingGeometryFromTextPositions(segment.Start, segment.End);
                    AddGeometry(ref _selectionGeometry, geometry); 
                }
            }

            if (_selectionGeometry != previousSelectionGeometry) 
            {
                // Request to re-render the selection. 
                RefreshCaret(_italic); 
            }
        } 

        internal static void AddGeometry(ref Geometry geometry, Geometry addedGeometry)
        {
            if (addedGeometry != null) 
            {
                if (geometry == null) 
                { 
                    geometry = addedGeometry;
                } 
                else
                {
                    geometry = Geometry.Combine(geometry, addedGeometry, GeometryCombineMode.Union, null, CaretElement.c_geometryCombineTolerance, ToleranceType.Absolute);
                } 
            }
        } 
 
        internal static void ClipGeometryByViewport(ref Geometry geometry, Rect viewport)
        { 
            if (geometry != null)
            {
                Geometry viewportGeometry = new RectangleGeometry(viewport);
                geometry = Geometry.Combine(geometry, viewportGeometry, GeometryCombineMode.Intersect, null, CaretElement.c_geometryCombineTolerance, ToleranceType.Absolute); 
            }
        } 
 
        internal static void AddTransformToGeometry(Geometry targetGeometry, Transform transformToAdd)
        { 
            if (targetGeometry != null && transformToAdd != null)
            {
                targetGeometry.Transform = (targetGeometry.Transform == null || targetGeometry.Transform.IsIdentity)
                    ? transformToAdd 
                    : new MatrixTransform(targetGeometry.Transform.Value * transformToAdd.Value);
            } 
        } 

        ///  
        /// Hide the caret render.
        /// 
        internal void Hide()
        { 
            if (_showCaret)
            { 
                // Hide the caret not to render the caret. 
                _showCaret = false;
 
                InvalidateVisual();

                // Skip the animation if the animation isn't set. E.g. DragDrop caret.
                SetBlinking(/*isBlinkEnabled:*/false); 

                // Destroy Win32 caret 
                Win32DestroyCaret(); 
            }
        } 

        /// 
        /// Requires to visually invalidate and update a caret without calculating
        /// its size and coordinates (assuming that they are the same as in preceding Update). 
        /// Used to force caret redraing when input language or italic condition is changed.
        /// Also called internally from Update method after new position and size is 
        /// calculated and set to the caret. 
        /// 
        ///  
        /// True specifies that the caret must be inclined to indicate italic state.
        /// 
        internal void RefreshCaret(bool italic)
        { 
            // Store italic status of the caret
            _italic = italic; 
 
            // Cache _adornerLayer to avoid reentrancy problems during Update.
            AdornerLayer adornerLayer = _adornerLayer; 

            if (adornerLayer != null)
            {
                Adorner[] adorners = adornerLayer.GetAdorners(this.AdornedElement); 

                if (adorners != null) 
                { 
                    // Verify we still adorn our element.
                    // We have a persistent but still unexplained stress bug where 
                    // the caret adorner is mysteriously detached -- Windows OS Bugs 1645800.
                    for (int i = 0; i < adorners.Length; i++)
                    {
                        if (adorners[i] == this) 
                        {
                            adornerLayer.Update(this.AdornedElement); 
                            adornerLayer.InvalidateVisual(); 
                            break;
                        } 
                    }
                }
            }
        } 

        // Removes this CaretElement from its AdornerLayer. 
        internal void DetachFromView() 
        {
            SetBlinking(/*isBlinkEnabled:*/false); 

            if (_adornerLayer != null)
            {
                _adornerLayer.Remove(this); 
                _adornerLayer = null;
            } 
        } 

        internal void SetBlinking(bool isBlinkEnabled) 
        {
            if (isBlinkEnabled != _isBlinkEnabled)
            {
                // Stopping blinking 
                if (_isBlinkEnabled)
                { 
                    if (_blinkAnimationClock != null) 
                    {
                        if (_blinkAnimationClock.CurrentState == ClockState.Active) 
                        {
                            _blinkAnimationClock.Controller.Stop();
                        }
                    } 
                }
 
                // Actual blinking will be started in Update method call - if needed 
                _isBlinkEnabled = isBlinkEnabled;
 
                if (isBlinkEnabled)
                {
                    // Create Win32 caret.
                    Win32CreateCaret(); 
                }
                else 
                { 
                    // Destory Win32 caret.
                    Win32DestroyCaret(); 
                }
            }
        }
 
        /// 
        /// Performs the actual rendering of the caret on the given context.  Called by 
        /// CaretSubElement. 
        /// 
        /// Drawing context 
        /// This method is on CaretElement instead of CaretSubElement because CaretElement
        /// knows all of the necessary data, and conceptually CaretSubElement only exists to provide
        /// a rendering surface.
        internal void OnRenderCaretSubElement(DrawingContext context) 
        {
            // Sync up Win32 caret position with Avalon caret position. 
            Win32SetCaretPos(); 

            if (_showCaret) 
            {
                TextEditorThreadLocalStore threadLocalStore = TextEditor._ThreadLocalStore;

                Invariant.Assert(!(_italic && this.IsInInterimState), "Assert !(_italic && IsInInterimState)"); 

                // Drawing context's pushed count to pop it up 
                int contextPushedCount = 0; 

                // Apply italic transformation 
                if (_italic && !(threadLocalStore.Bidi))
                {
                    // Rotate transform 20 degree for italic that is the based on 'H' italic degree.
                    // NOTE: The angle of italic caret is constant. This is Word behavior 
                    // established after usability studies with conditional angle dependent
                    // on font properties - they discovered that variations look annoying. 
                    // NOTE: We ignore _italic setting in _bidi case. This is Word behavior. 
                    // When flow direction is Right to Left, we need to reverse the caret transform.
                    // 
                    // Get the flow direction which is the flow direction of AdornedElement.
                    // CaretElement is rendering the caret that based on AdornedElement, so we can
                    // render the right italic caret whatever the text content set the flow direction.
                    FlowDirection flowDirection = (FlowDirection)AdornedElement.GetValue(FlowDirectionProperty); 
                    context.PushTransform(new RotateTransform(
                    flowDirection == FlowDirection.RightToLeft ? -20 : 20, 
 
                        0,  _height));
 
                    contextPushedCount++;
                }

                if (this.IsInInterimState || _systemCaretWidth > DefaultNarrowCaretWidth) 
                {
                    // Make the block caret as the half percent transparent to apply 
                    // the current text. 
                    context.PushOpacity(CaretTransparent);
                    contextPushedCount++; 
                }

                if (this.IsInInterimState)
                { 
                    // Render the interim block caret as the specified interim block caret width.
                    context.DrawRectangle(_caretBrush, null, new Rect(0, 0, _interimWidth, _height)); 
                } 
                else
                { 
                    // Render as a 2 pixel wide rect, one pixel in each bordering char bounding box.
                    context.DrawRectangle(_caretBrush, null, new Rect(-(_systemCaretWidth / 2), 0, _systemCaretWidth, _height));
                }
 
                if (threadLocalStore.Bidi)
                { 
                    // Set the Bidi caret indicator width. TextBox/RichTextBox control must have 
                    // the enough margin to display BiDi indicator.
                    double bidiCaretIndicatorWidth = BidiCaretIndicatorWidth; 

                    // Get the flow direction which is the flow direction of AdornedElement.
                    // Because CaretElement is rendering the caret that based on AdornedElement.
                    // With getting the flow direction, we can render the BiDi caret indicator correctly 
                    // whatever AdornedElement's flow direction is set.
                    FlowDirection flowDirection = (FlowDirection)AdornedElement.GetValue(FlowDirectionProperty); 
                    if (flowDirection == FlowDirection.RightToLeft) 
                    {
                        // BiDi caret indicator should always direct by the right to left 
                        bidiCaretIndicatorWidth = bidiCaretIndicatorWidth * (-1);
                    }

                    // Draw BIDI caret to indicate the coming input is BIDI characters. 
                    // Shape is a little flag oriented to the left - as in Word.
                    // Orientation does not depend on anything (which seems to be Word behavior). 
                    // 
                    PathGeometry pathGeometry;
                    PathFigure pathFigure; 

                    pathGeometry = new PathGeometry();
                    pathFigure = new PathFigure();
                    pathFigure.StartPoint = new Point(0, 0); 
                    pathFigure.Segments.Add(new LineSegment(new Point(-bidiCaretIndicatorWidth, 0), true));
                    pathFigure.Segments.Add(new LineSegment(new Point(0, _height / BidiIndicatorHeightRatio), true)); 
                    pathFigure.IsClosed = true; 

                    pathGeometry.Figures.Add(pathFigure); 
                    context.DrawGeometry(_caretBrush, null, pathGeometry);
                }

                // Pop the drawing context if pushed for italic or opacity setting 
                for (int i = 0; i < contextPushedCount; i++)
                { 
                    context.Pop(); 
                }
            } 
            else
            {
                // Destroy Win32 caret.
                Win32DestroyCaret(); 
            }
        } 
 
        // ITextView.Updated event listener.  Called by the TextSelection.
        internal void OnTextViewUpdated() 
        {
            // At this point, we potentially have to update our selection geometry,
            // but the TextView is still dirty.
            // 
            // We cannot simply delay the work with a Dispatcher.BeginInvoke,
            // because that will introduce latency (flicker) as the selection/caret 
            // update lags behind the text refresh. 
            //
            // So we invalidate this Adorner's arrange so that we can re-evaluate 
            // in the same layout pass as the view update, after this method returns.
            _pendingGeometryUpdate = true;
            InvalidateArrange();
        } 

        // ----------------------------------------------------------- 
        // 
        // Internal Properties for Test Automation
        // 
        // -----------------------------------------------------------

        // This method is used for text automation in DrtEditing
        private static CaretElement Debug_CaretElement 
        {
            get 
            { 
                TextEditorThreadLocalStore threadLocalStore = TextEditor._ThreadLocalStore;
                return ((ITextSelection)TextEditor._ThreadLocalStore.FocusedTextSelection).CaretElement; 
            }
        }

        // This method is used for text automation in DrtEditing 
        private static FrameworkElement Debug_RenderScope
        { 
            get 
            {
                return ((ITextSelection)TextEditor._ThreadLocalStore.FocusedTextSelection).TextView.RenderScope as FrameworkElement;  // TextBlock / TextFlow 
            }
        }

        #endregion Internal methods 

        //----------------------------------------------------- 
        // 
        //  Internal Properties
        // 
        //------------------------------------------------------

        #region Internal Properties
 
        internal Geometry SelectionGeometry
        { 
            get 
            {
                return _selectionGeometry; 
            }
        }

        #endregion Internal Properties 

        //----------------------------------------------------- 
        // 
        //  Private Methods
        // 
        //------------------------------------------------------

        #region Private Methods
 
        // Adds this CaretElement to the scoping AdornerLayer, if any.
        private void EnsureAttachedToView() 
        { 
            AdornerLayer layer = AdornerLayer.GetAdornerLayer(_textEditor.TextView.RenderScope);
            if (layer == null) 
            {
                // There is no AdornerLayer available.  Clear cached value and exit.
                if (_adornerLayer != null)
                { 
                    // We're currently in a layer that doesn't exist.
                    _adornerLayer.Remove(this); 
                } 

                _adornerLayer = null; 
                return;
            }

            if (_adornerLayer == layer) 
            {
                // We're already using the correct AdornerLayer. 
                return; 
            }
 
            if (_adornerLayer != null)
            {
                // We're currently in the wrong layer.
                _adornerLayer.Remove(this); 
            }
 
            // Add ourselves to the correct layer. 
            _adornerLayer = layer;
            _adornerLayer.Add(this, ZOrderValue); 
        }

        // Inits or resets an opacity animation used to make the caret blink.
        private void SetBlinkAnimation(bool visible, bool positionChanged) 
        {
            if (!_isBlinkEnabled) 
            { 
                return;
            } 

            // NB: "Blink" is the period of time between successive
            // state changes in the caret animation.  "Flash" is
            // 2 * blink, the period of time to transition from 
            // visible, to hidden, to visible again.
            int blinkInterval = Win32GetCaretBlinkTime(); 
            if (blinkInterval > 0) // -1 if the caret shouldn't blink. 
            {
                Duration blinkDuration = new Duration(TimeSpan.FromMilliseconds(blinkInterval * 2)); 

                if (_blinkAnimationClock == null || _blinkAnimationClock.Timeline.Duration != blinkDuration)
                {
                    DoubleAnimationUsingKeyFrames blinkAnimation = new DoubleAnimationUsingKeyFrames(); 
                    blinkAnimation.BeginTime = null;
                    blinkAnimation.RepeatBehavior = RepeatBehavior.Forever; 
                    blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(1, KeyTime.FromPercent(0.0))); 
                    blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(0, KeyTime.FromPercent(0.5)));
                    blinkAnimation.Duration = blinkDuration; 

                    _blinkAnimationClock = blinkAnimation.CreateClock();
                    _blinkAnimationClock.Controller.Begin();
 
                    _caretElement.ApplyAnimationClock(UIElement.OpacityProperty, _blinkAnimationClock);
                } 
            } 
            else if (_blinkAnimationClock != null)
            { 
                // No blink (control panel option).
                _caretElement.ApplyAnimationClock(UIElement.OpacityProperty, null);
                _blinkAnimationClock = null;
            } 

            if (_blinkAnimationClock != null) 
            { 
                // Disable the animation when the caret isn't rendered
                // to get better perf. 
                if (visible && (!(_blinkAnimationClock.CurrentState == ClockState.Active) || positionChanged))
                {
                    // Start the blinking animation for caret
                    _blinkAnimationClock.Controller.Begin(); 
                }
                else if (!visible) 
                { 
                    // Stop the blinking animation if the caret isn't visible even we start it yet.
                    // Because ApplyAnimationClock() calling can start the animation without 
                    // the calling of Begin() which case is the auto selection(with DatePicker).
                    _blinkAnimationClock.Controller.Stop();
                }
            } 
        }
 
        // Create Win32 caret to sync up Avalon caret with Win32 application and show Win32 caret 
        // which is the empty bitmap. This call will generate the accessiblity event for caret so that
        // Win32 application have the compatibility to handle the caret event which is Magnifier or Tablet Tip. 
        /// 
        /// Critical - as this calls PresentationSource.FromVisual() and PresentationSource.Handle
        ///            under elevation.
        /// Safe - as this doesn't expose the information. The call to CreateCaret here 
        ///        will destroy the caret only for the current window and create new empty bitmap
        ///        and show it for Win32 caret to handle the accesibility event well without 
        ///        exposing the information. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void Win32CreateCaret()
        {
            // Create Win32 caret if the height of caret is changed or
            // doesn't exist Win32 caret. 
            if (!_win32Caret || _win32Height != _height)
            { 
                // Create Win32 caret to support Win32 application(like Magnifier) that want to 
                // sync Avalon caret position.
                IntPtr hwnd = IntPtr.Zero; 
                PresentationSource source = PresentationSource.CriticalFromVisual(this);

                if (source != null)
                { 
                     new UIPermission(UIPermissionWindow.AllWindows).Assert();   //BlessedAssert
                     try 
                     { 
                          hwnd = (source as IWin32Window).Handle;
                     } 
                     finally
                     {

                         UIPermission.RevertAssert(); 
                     }
                } 
 
                if (hwnd != IntPtr.Zero)
                { 
                    // Convert _height (fixed at 96 dpi) to device units.
                    double deviceHeight = source.CompositionTarget.TransformToDevice.Transform(new Point(0, _height)).Y;

                    // Win32 CreateCaret automatically destroys the previous caret shape, 
                    // if any, regardless of the window that owns the caret.
 
                    // Create and show Win32 empty caret for win32 compatibility. 
                    // Creating Win32 empty caret and show it will generate the accesibility event
                    // so that Win32 application will have the compatibility who listen the caret event. 
                    // Specified height with the current caret height will sync the win32 caret which
                    // Win32 Magnifer rely on the caret height to scroll their window.
                    NativeMethods.BitmapHandle bitmap = UnsafeNativeMethods.CreateBitmap(/*width*/ 1, /*height*/ ConvertToInt32(deviceHeight), /*panels*/ 1, /*bitsPerPixel*/ 1, /*bits*/ null);
 
                    // Specified width and height as zero since they will be ignored by setting the bitmap.
                    bool returnValue = UnsafeNativeMethods.CreateCaret(new HandleRef(null, hwnd), bitmap, /*width*/ 0, /*height*/ 0); 
 
                    int win32Error = Marshal.GetLastWin32Error();
                    if (returnValue) 
                    {
                        _win32Caret = true;
                        _win32Height = _height;
                    } 
                    else
                    { 
                        _win32Caret = false; 

                        // Throw the Win32 exception with GetLastWin32Error. 
                        throw new System.ComponentModel.Win32Exception(win32Error);
                    }
                }
            } 
        }
 
        // Destroy Win32 caret if we create it with checking Win32 error. 
        /// 
        ///     Critical: This code calls into Marshal.GetLastError which has a link demand 
        ///     TreatAsSafe: Extracting the last error is safe
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        private void Win32DestroyCaret() 
        {
            // We only destroy the caret what we created a Win32 caret. 
            if (_win32Caret) 
            {
                // Destroy Win32 caret. 
                bool returnValue = SafeNativeMethods.DestroyCaret();
                int win32Error = Marshal.GetLastWin32Error();
                if (!returnValue)
                { 
                    // Suppress Win32 error of failing DestroyCaret(), because CreateCaret()can
                    // automatically destroys caret resource, if any, regardless of the owns caret. 
                    // Mshtml module can create Win32 caret when Avalon is loaded by IE, so our Win32 
                    // caret resousce is already destroyed.
                    //throw new System.ComponentModel.Win32Exception(win32Error); 
                }

                _win32Caret = false;
                _win32Height = 0; 
            }
        } 
 
        // Set Win32 caret position with checking Win32 error.
        ///  
        /// Critical - as this calls PresentationSource.CriticalFromVisual()
        /// Safe - as doesn't expose the data obtained and it uses the data to render the
        ///        caret.  This is safe as the caller can't render it on a random location.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void Win32SetCaretPos() 
        { 
            // Create Win32 caret if win32 caret isn't created yet or destroyed already.
            if (!_win32Caret) 
            {
                Win32CreateCaret();
            }
 
            // Get the presentation source to find a root visual.
            PresentationSource source = null; 
            source = PresentationSource.CriticalFromVisual(this); 
            if (source != null)
            { 
                // Calculate the current caret position then transform the point to the window's client position
                // so that Win32 applicatioin like as Magnifer(MSAA) or Tablet Ink icon can track the right
                // caret position by calling Win32 SetCaretPos that will generate MSAA event message
                // as EVENT_OBJECT_LOCATIONCHANGE. 
                Point win32CaretPoint = new Point(0, 0);
                GeneralTransform transform = _caretElement.TransformToAncestor(source.RootVisual); 
                if (!transform.TryTransform(win32CaretPoint, out win32CaretPoint)) 
                {
                    // Reset the win32 caret point as zero if TryTransform is failed 
                    win32CaretPoint = new Point(0, 0);
                }

                // Convert visual point (fixed at 96 dpi) to device units. 
                win32CaretPoint = source.CompositionTarget.TransformToDevice.Transform(win32CaretPoint);
 
                bool win32Return = SafeNativeMethods.SetCaretPos(ConvertToInt32(win32CaretPoint.X), ConvertToInt32(win32CaretPoint.Y)); 
                // GetLastWin32Error() have to be called immediately not to break FxCop rule
                // even though we ignore the first fail of SetCaretPos. 
                int win32Error = Marshal.GetLastWin32Error();
                if (!win32Return)
                {
                    // Suppress the first chance fail of Win32 SetCaretPos. Because Win32 caret can be 
                    // created by Win32 application(like as mshtml.dll) that destroying our avalon Win32
                    // caret resource to update Avalon caret position to the system. So we try to create 
                    // our own Win32 caret resource here and try to set the caret position again. 
                    // Throw the exception if we still have a fail of SetCaretPos on the second time.
                    _win32Caret = false; 
                    Win32CreateCaret();

                    win32Return = SafeNativeMethods.SetCaretPos(ConvertToInt32(win32CaretPoint.X), ConvertToInt32(win32CaretPoint.Y));
                    win32Error = Marshal.GetLastWin32Error(); 
                    if (!win32Return)
                    { 
                        // Throw the Win32 exception with GetLastWin32Error. 
                        throw new System.ComponentModel.Win32Exception(win32Error);
                    } 
                }
            }
        }
 
        // Converts a double into a 32 bit integer, truncating values that
        // exceed Int32.MinValue or Int32.MaxValue. 
        private int ConvertToInt32(double value) 
        {
            int i; 

            if (double.IsNaN(value))
            {
                i = 0; 
            }
            if (value < Int32.MinValue) 
            { 
                i = Int32.MinValue;
            } 
            else if (value > Int32.MaxValue)
            {
                i = Int32.MaxValue;
            } 
            else
            { 
                i = Convert.ToInt32(value); 
            }
 
            return i;
        }

        // Get the system caret blink time with checking Win32 error. 
        private int Win32GetCaretBlinkTime()
        { 
            // Disable PreSharp#6523 - Win32 GetCaretBlinkTime can return "0" 
            // without the error if SetCaretBlinkTime set as "0".
#pragma warning disable 6523 

            int caretBlinkTime = (int)SafeNativeMethods.GetCaretBlinkTime();
            if (caretBlinkTime == 0)
            { 
                // Return "-1" which is no blinking caret instead of throwing
                // exception. 
                return -1; 
            }
 
#pragma warning restore 6523

            return caretBlinkTime;
        } 

        #endregion Private methods 
 
        //------------------------------------------------------
        // 
        //  Private Properties
        //
        //-----------------------------------------------------
 
        #region Private Properties
 
        private bool IsInInterimState 
        {
            get 
            {
                // Returns true if the interim width is specified and shows
                // the interim block caret.
                return _interimWidth != 0; 
            }
        } 
 
        #endregion Private Properties
 
        //------------------------------------------------------
        //
        //  Internal Fields
        // 
        //-----------------------------------------------------
 
        #region Internal Fields 

        // BiDi caret indicator width. 
        internal const double BidiCaretIndicatorWidth = 2.0;

        // Caret padding width to ensure the visible caret for Bidi and Italic.
        // Control(TextBox/RichTextBox) must have the enough padding to display 
        // BiDi and Italic caret indicator.
        internal const double CaretPaddingWidth = 5.0; 
 
        #endregion Internal Fields
 
        //-----------------------------------------------------
        //
        //  Private Types
        // 
        //-----------------------------------------------------
 
        #region Private Types 

        private class CaretSubElement : UIElement 
        {
            internal CaretSubElement()
            {
            } 

            // HitTestCore override not to hit testable Caret. 
            protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) 
            {
                // Return null not to hit testable for CaretElement. 
                return null;
            }

            protected override void OnRender(DrawingContext drawingContext) 
            {
                ((CaretElement)_parent).OnRenderCaretSubElement(drawingContext); 
            } 
        }
 
        #endregion Private Types

        //------------------------------------------------------
        // 
        //  Private Fields
        // 
        //----------------------------------------------------- 

        #region Private Fields 

        // TextEditor that owns this adorner.
        private readonly TextEditor _textEditor;
 
        // If false, the caret is hidden and should not be rendered.
        private bool _showCaret; 
 
        // Our blink opacity animation.
        private AnimationClock _blinkAnimationClock; 

        // left offset of caret
        private double _left;
 
        // top offset of caret
        private double _top; 
 
        // width of caret
        private double _systemCaretWidth; 

        // width of interim caret
        private double _interimWidth;
 
        // height of caret
        private double _height; 
 
        // height of Win32 caret
        private double _win32Height; 

        // flag to control animation.
        private bool _isBlinkEnabled;
 
        // caret brush.
        private Brush _caretBrush; 
 
        // AdornerLayer holding this caret visual.
        private AdornerLayer _adornerLayer; 

        // italic caret
        private bool _italic;
 
        // win32 caret
        private bool _win32Caret; 
 
        // Caret trasparent percent
        private const double CaretTransparent = 0.5; 

        // BiDi caret indicator height ratio of caret
        private const double BidiIndicatorHeightRatio = 10.0;
 
        // default narrow caret width
        private const double  DefaultNarrowCaretWidth = 1.0; 
 
        //  selection related data
        private Geometry _selectionGeometry; 
        internal const double c_geometryCombineTolerance = 1e-4;
        internal const double c_endOfParaMagicMultiplier = 0.5;

        // ZOrder 
        internal const int ZOrderValue = System.Int32.MaxValue / 2;
 
        // child element on which we render the caret (selection is rendered on the adorner itself) 
        private readonly CaretSubElement _caretElement;
 
        // Flag set true after calls to OnTextViewUpdated.
        // When true, we need to re-evaluate the selection geometry on the
        // next Arrange.
        private bool _pendingGeometryUpdate; 

        // Flag set when we scroll and cleared when the position changes. 
        // Used in Update() to optimize when we scroll. 
        private bool _scrolledToCurrentPositionYet;
 
        #endregion Private Fields
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK