TextBoxView.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / documents / TextBoxView.cs / 1544692 / TextBoxView.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: TextBoxView.cs 
//
// Description: Content presenter for the TextBox. 
// 
//---------------------------------------------------------------------------
 
namespace System.Windows.Controls
{
    using System.Windows.Documents;
    using System.Windows.Controls.Primitives; 
    using System.Windows.Media;
    using System.Windows.Threading; 
    using System.Collections.Generic; 
    using System.Collections.ObjectModel;
    using MS.Internal; 
    using MS.Internal.Text;
    using MS.Internal.Documents;
    using MS.Internal.PtsHost;
    using System.Windows.Media.TextFormatting; 

    // Content presenter for the TextBox. 
    internal class TextBoxView : FrameworkElement, ITextView, IScrollInfo, IServiceProvider 
    {
        //----------------------------------------------------- 
        //
        //  Constructors
        //
        //----------------------------------------------------- 

        #region Constructors 
 
        // Static constructor.
        static TextBoxView() 
        {
            // Set a margin so that the bidi caret has room to render at the edges of content.
            MarginProperty.OverrideMetadata(typeof(TextBoxView), new FrameworkPropertyMetadata(new Thickness(CaretElement.BidiCaretIndicatorWidth, 0, CaretElement.BidiCaretIndicatorWidth, 0)));
        } 

        // Constructor. 
        internal TextBoxView(ITextBoxViewHost host) 
        {
            Invariant.Assert(host is Control); 
            _host = host;
        }

        #endregion Constructors 

        //------------------------------------------------------ 
        // 
        //  Public Methods
        // 
        //-----------------------------------------------------

        #region Public Methods
 
        // IServiceProvider for TextEditor/renderscope contract.
        // Provides access to our ITextView implementation. 
        object IServiceProvider.GetService(Type serviceType) 
        {
            object service = null; 

            if (serviceType == typeof(ITextView))
            {
                service = this; 
            }
 
            return service; 
        }
 
        /// 
        /// 
        /// 
        void IScrollInfo.LineUp() 
        {
            if (_scrollData != null) 
            { 
                _scrollData.LineUp(this);
            } 
        }

        /// 
        ///  
        /// 
        void IScrollInfo.LineDown() 
        { 
            if (_scrollData != null)
            { 
                _scrollData.LineDown(this);
            }
        }
 
        /// 
        ///  
        ///  
        void IScrollInfo.LineLeft()
        { 
            if (_scrollData != null)
            {
                _scrollData.LineLeft(this);
            } 
        }
 
        ///  
        /// 
        ///  
        void IScrollInfo.LineRight()
        {
            if (_scrollData != null)
            { 
                _scrollData.LineRight(this);
            } 
        } 

        ///  
        /// 
        /// 
        void IScrollInfo.PageUp()
        { 
            if (_scrollData != null)
            { 
                _scrollData.PageUp(this); 
            }
        } 

        /// 
        /// 
        ///  
        void IScrollInfo.PageDown()
        { 
            if (_scrollData != null) 
            {
                _scrollData.PageDown(this); 
            }
        }

        ///  
        /// 
        ///  
        void IScrollInfo.PageLeft() 
        {
            if (_scrollData != null) 
            {
                _scrollData.PageLeft(this);
            }
        } 

        ///  
        ///  
        /// 
        void IScrollInfo.PageRight() 
        {
            if (_scrollData != null)
            {
                _scrollData.PageRight(this); 
            }
        } 
 
        /// 
        ///  
        /// 
        void IScrollInfo.MouseWheelUp()
        {
            if (_scrollData != null) 
            {
                _scrollData.MouseWheelUp(this); 
            } 
        }
 
        /// 
        /// 
        /// 
        void IScrollInfo.MouseWheelDown() 
        {
            if (_scrollData != null) 
            { 
                _scrollData.MouseWheelDown(this);
            } 
        }

        /// 
        ///  
        /// 
        void IScrollInfo.MouseWheelLeft() 
        { 
            if (_scrollData != null)
            { 
                _scrollData.MouseWheelLeft(this);
            }
        }
 
        /// 
        ///  
        ///  
        void IScrollInfo.MouseWheelRight()
        { 
            if (_scrollData != null)
            {
                _scrollData.MouseWheelRight(this);
            } 
        }
 
        ///  
        /// 
        ///  
        void IScrollInfo.SetHorizontalOffset(double offset)
        {
            if (_scrollData != null)
            { 
                _scrollData.SetHorizontalOffset(this, offset);
            } 
        } 

        ///  
        /// 
        /// 
        void IScrollInfo.SetVerticalOffset(double offset)
        { 
            if (_scrollData != null)
            { 
                _scrollData.SetVerticalOffset(this, offset); 
            }
        } 

        /// 
        /// 
        ///  
        Rect IScrollInfo.MakeVisible(Visual visual, Rect rectangle)
        { 
            if (_scrollData == null) 
            {
                rectangle = Rect.Empty; 
            }
            else
            {
                rectangle = _scrollData.MakeVisible(this, visual, rectangle); 
            }
 
            return rectangle; 
        }
 
        /// 
        /// 
        /// 
        bool IScrollInfo.CanVerticallyScroll 
        {
            get 
            { 
                return (_scrollData != null) ? _scrollData.CanVerticallyScroll : false;
            } 
            set
            {
                if (_scrollData != null)
                { 
                    _scrollData.CanVerticallyScroll = value;
                } 
            } 
        }
 
        /// 
        /// 
        /// 
        bool IScrollInfo.CanHorizontallyScroll 
        {
            get 
            { 
                return (_scrollData != null) ? _scrollData.CanHorizontallyScroll : false;
            } 
            set
            {
                if (_scrollData != null)
                { 
                    _scrollData.CanHorizontallyScroll = value;
                } 
            } 
        }
 
        /// 
        /// 
        /// 
        double IScrollInfo.ExtentWidth 
        {
            get 
            { 
                return (_scrollData != null) ? _scrollData.ExtentWidth : 0;
            } 
        }

        /// 
        ///  
        /// 
        double IScrollInfo.ExtentHeight 
        { 
            get
            { 
                return (_scrollData != null) ? _scrollData.ExtentHeight : 0;
            }
        }
 
        /// 
        ///  
        ///  
        double IScrollInfo.ViewportWidth
        { 
            get
            {
                return (_scrollData != null) ? _scrollData.ViewportWidth : 0;
            } 
        }
 
        ///  
        /// 
        ///  
        double IScrollInfo.ViewportHeight
        {
            get
            { 
                return (_scrollData != null) ? _scrollData.ViewportHeight : 0;
            } 
        } 

        ///  
        /// 
        /// 
        double IScrollInfo.HorizontalOffset
        { 
            get
            { 
                return (_scrollData != null) ? _scrollData.HorizontalOffset : 0; 
            }
        } 

        /// 
        /// 
        ///  
        double IScrollInfo.VerticalOffset
        { 
            get 
            {
                return (_scrollData != null) ? _scrollData.VerticalOffset : 0; 
            }
        }

        ///  
        /// 
        ///  
        ScrollViewer IScrollInfo.ScrollOwner 
        {
            get 
            {
                return (_scrollData != null) ? _scrollData.ScrollOwner : null;
            }
 
            set
            { 
                if (_scrollData == null) 
                {
                    // Create cached scroll info. 
                    _scrollData = new ScrollData();
                }
                _scrollData.SetScrollOwner(this, value);
            } 
        }
 
        #endregion Public Methods 

        //------------------------------------------------------ 
        //
        //  Protected Methods
        //
        //------------------------------------------------------ 

        #region Protected Methods 
 
        // Calculates ideal content size.
        protected override Size MeasureOverride(Size constraint) 
        {
            // Lazy init TextContainer listeners on the first measure.
            EnsureTextContainerListeners();
 
            // Lazy allocate _lineMetrics on the first measure.
            if (_lineMetrics == null) 
            { 
                _lineMetrics = new List(1);
            } 

            Size desiredSize;

            // Init a cache we'll use here and in the following ArrangeOverride call. 
            _cache = null;
            EnsureCache(); 
 
            LineProperties lineProperties = _cache.LineProperties;
 
            // Skip the measure if constraints have not changed.
            bool widthChanged = !DoubleUtil.AreClose(constraint.Width, _previousConstraint.Width);

            // If width changed and TextAlignment is Center or Right the visual offsets of the visible 
            // lines need to be recalculated.
            if (widthChanged && lineProperties.TextAlignment != TextAlignment.Left) 
            { 
                _viewportLineVisuals = null;
            } 

            bool constraintschanged = widthChanged &&
                                      lineProperties.TextWrapping != TextWrapping.NoWrap;
 
            if (_lineMetrics.Count == 0 || constraintschanged)
            { 
                // Null out the dirty list when constraints change -- everything's dirty. 
                _dirtyList = null;
            } 
            else if (_dirtyList == null && !this.IsBackgroundLayoutPending)
            {
                // No dirty region, no constraint change, no pending background layout.
                desiredSize = _contentSize; 
                goto Exit;
            } 
 
            // Treat an insert into an empty document just like a full invalidation,
            // to allow background layout to run. 
            if (_dirtyList != null &&
                _lineMetrics.Count == 1 && _lineMetrics[0].EndOffset == 0)
            {
                _lineMetrics.Clear(); 
                _viewportLineVisuals = null;
                _dirtyList = null; 
            } 

            Size safeConstraint = constraint; 
            // Make sure that TextFormatter limitations are not exceeded.
            //
            TextDpi.EnsureValidLineWidth(ref safeConstraint);
 
            // Do the measure.
            if (_dirtyList == null) 
            { 
                if (constraintschanged)
                { 
                    _lineMetrics.Clear();
                    _viewportLineVisuals = null;
                }
                desiredSize = FullMeasureTick(safeConstraint.Width, lineProperties); 
            }
            else 
            { 
                desiredSize = IncrementalMeasure(safeConstraint.Width, lineProperties);
            } 
            Invariant.Assert(_lineMetrics.Count >= 1);

            _dirtyList = null;
 
            double oldWidth = _contentSize.Width;
            _contentSize = desiredSize; 
 
            // If the width has changed we need to reformat if we're centered or right aligned so the
            // spacing gets properly updated. 
            if (oldWidth != desiredSize.Width && lineProperties.TextAlignment != TextAlignment.Left)
            {
                Rerender();
            } 

Exit: 
            // DesiredSize is set to the calculated size of the content. 
            // If hosted by ScrollViewer, desired size is limited to constraint.
            if (_scrollData != null) 
            {
                desiredSize.Width = Math.Min(constraint.Width, desiredSize.Width);
                desiredSize.Height = Math.Min(constraint.Height, desiredSize.Height);
            } 

            _previousConstraint = constraint; 
 
            return desiredSize;
        } 

        // Arranges content within a specified constraint.
        protected override Size ArrangeOverride(Size arrangeSize)
        { 
            if (_lineMetrics == null || _lineMetrics.Count == 0)
            { 
                // No matching MeasureOverride call. 
                goto Exit;
            } 

            EnsureCache();

            ArrangeScrollData(arrangeSize); 
            ArrangeVisuals(arrangeSize);
 
            _cache = null; 

            FireTextViewUpdatedEvent(); 

Exit:
            return arrangeSize;
        } 

        // Render callback for this TextBoxView. 
        protected override void OnRender(DrawingContext context) 
        {
            // Render a transparent Rect to enable hit-testing even when content does not fill 
            // the entire viewport.
            //
            context.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, new Rect(0, 0, this.RenderSize.Width, this.RenderSize.Height));
        } 

        ///  
        ///   Derived class must implement to support Visual children. The method must return 
        ///    the child at the specified index. Index must be between 0 and GetVisualChildrenCount-1.
        /// 
        ///    By default a Visual does not have any children.
        ///
        ///  Remark:
        ///       During this virtual call it is not valid to modify the Visual tree. 
        /// 
        protected override Visual GetVisualChild(int index) 
        { 
            if (index >= this.VisualChildrenCount)
            { 
                throw new ArgumentOutOfRangeException("index");
            }

            return _visualChildren[index]; 
        }
 
        #endregion Protected Methods 

        //----------------------------------------------------- 
        //
        //  Protected Properties
        //
        //------------------------------------------------------ 

        #region Protected Properties 
 
        /// 
        ///  Derived classes override this property to enable the Visual code to enumerate 
        ///  the Visual children. Derived classes need to return the number of children
        ///  from this method.
        ///
        ///    By default a Visual does not have any children. 
        ///
        ///  Remark: 
        ///      During this virtual method the Visual tree must not be modified. 
        /// 
        protected override int VisualChildrenCount 
        {
            get
            {
                return (_visualChildren == null) ? 0 : _visualChildren.Count; 
            }
        } 
 
        #endregion Protected Properties
 
        //-----------------------------------------------------
        //
        //  Internal Methods
        // 
        //-----------------------------------------------------
 
        #region Internal Methods 

        ///  
        /// 
        /// 
        ITextPointer ITextView.GetTextPositionFromPoint(Point point, bool snapToText)
        { 
            Invariant.Assert(this.IsLayoutValid);
 
            point = TransformToDocumentSpace(point); 

            int lineIndex = GetLineIndexFromPoint(point, snapToText); 
            ITextPointer position;

            if (lineIndex == -1)
            { 
                position = null;
            } 
            else 
            {
                position = GetTextPositionFromDistance(lineIndex, point.X); 
                position.Freeze();
            }

            return position; 
        }
 
        ///  
        /// 
        ///  
        Rect ITextView.GetRectangleFromTextPosition(ITextPointer position)
        {
            Rect rect;
 
            Invariant.Assert(this.IsLayoutValid);
            Invariant.Assert(Contains(position)); 
 
            int offset = position.Offset;
            if (offset > 0 && position.LogicalDirection == LogicalDirection.Backward) 
            {
                // TextBoxLine always gets the forward Rect, so back up to preceding char.
                offset--;
            } 

            int lineIndex = GetLineIndexFromOffset(offset); 
            FlowDirection flowDirection; 
            LineProperties lineProperties;
 
            using (TextBoxLine line = GetFormattedLine(lineIndex, out lineProperties))
            {
                rect = line.GetBoundsFromTextPosition(offset, out flowDirection);
            } 

            if (!rect.IsEmpty) // Empty rects can't be modified. 
            { 
                rect.Y += lineIndex * _lineHeight;
 
                // Return only TopLeft and Height.
                // Adjust rect.Left by taking into account flow direction of the
                // content and orientation of input position.
                if (lineProperties.FlowDirection != flowDirection) 
                {
                    if (position.LogicalDirection == LogicalDirection.Forward || position.Offset == 0) 
                    { 
                        rect.X = rect.Right;
                    } 
                }
                else
                {
                    if (position.LogicalDirection == LogicalDirection.Backward && position.Offset > 0) 
                    {
                        rect.X = rect.Right; 
                    } 
                }
 
                rect.Width = 0;
            }

            return TransformToVisualSpace(rect); 
        }
 
        ///  
        /// 
        ///  
        Rect ITextView.GetRawRectangleFromTextPosition(ITextPointer position, out Transform transform)
        {
            transform = Transform.Identity;
 
            return ((ITextView)this).GetRectangleFromTextPosition(position);
        } 
 
        /// 
        ///  
        /// 
        Geometry ITextView.GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition)
        {
            Invariant.Assert(this.IsLayoutValid); 

            Geometry geometry = null; 
            double endOfParaGlyphWidth = ((Control)_host).FontSize * CaretElement.c_endOfParaMagicMultiplier; 

            // Since background layout may be running, clip to the computed region. 
            int startOffset = Math.Min(_lineMetrics[_lineMetrics.Count-1].EndOffset, startPosition.Offset);
            int endOffset = Math.Min(_lineMetrics[_lineMetrics.Count - 1].EndOffset, endPosition.Offset);

            // Find the intersection of the viewport with the requested range. 
            int firstLineIndex;
            int lastLineIndex; 
            GetVisibleLines(out firstLineIndex, out lastLineIndex); 

            firstLineIndex = Math.Max(firstLineIndex, GetLineIndexFromOffset(startOffset, LogicalDirection.Forward)); 
            lastLineIndex = Math.Min(lastLineIndex, GetLineIndexFromOffset(endOffset, LogicalDirection.Backward));

            if (firstLineIndex > lastLineIndex)
            { 
                // Visible region does not intersect with geometry.
                return null; 
            } 

            // Partially covered lines require a line format, so we'll handle them specially. 
            // Only the first and last line are potentially partially covered.
            bool firstLinePartiallyCovered = _lineMetrics[firstLineIndex].Offset < startOffset ||
                                             _lineMetrics[firstLineIndex].EndOffset > endOffset;
            bool lastLinePartiallyCovered =  _lineMetrics[lastLineIndex].Offset < startOffset || 
                                             _lineMetrics[lastLineIndex].EndOffset > endOffset;
 
            TextAlignment alignment = this.CalculatedTextAlignment; 
            int lineIndex = firstLineIndex;
 
            // If we don't cover the entire first line, special case it.
            if (firstLinePartiallyCovered)
            {
                GetTightBoundingGeometryFromLineIndex(lineIndex, startOffset, endOffset, alignment, endOfParaGlyphWidth, ref geometry); 
                lineIndex++;
            } 
 
            // If it is completely covered, adjust lastLineIndex such that we handle
            // the last line in the loop below. 
            if (firstLineIndex <= lastLineIndex && !lastLinePartiallyCovered)
            {
                lastLineIndex++;
            } 

            // Handle all the lines that are entirely covered -- they don't require any heavy lifting. 
            for (; lineIndex < lastLineIndex; lineIndex++) 
            {
                double contentOffset = GetContentOffset(_lineMetrics[lineIndex].Width, alignment); 
                Rect rect = new Rect(contentOffset, lineIndex * _lineHeight, _lineMetrics[lineIndex].Width, _lineHeight);

                // Add extra padding at the end of lines with linebreaks.
                ITextPointer endOfLinePosition = _host.TextContainer.CreatePointerAtOffset(_lineMetrics[lineIndex].EndOffset, LogicalDirection.Backward); 
                if (TextPointerBase.IsNextToPlainLineBreak(endOfLinePosition, LogicalDirection.Backward))
                { 
                    rect.Width += endOfParaGlyphWidth; 
                }
 
                rect = TransformToVisualSpace(rect);
                CaretElement.AddGeometry(ref geometry, new RectangleGeometry(rect));
            }
 
            // If we don't cover the entire last line, special case it.
            // Otherwise, we already handled it in the loop above. 
            if (lineIndex == lastLineIndex && lastLinePartiallyCovered) 
            {
                GetTightBoundingGeometryFromLineIndex(lineIndex, startOffset, endOffset, alignment, endOfParaGlyphWidth, ref geometry); 
            }

            return geometry;
        } 

        ///  
        ///  
        /// 
        ITextPointer ITextView.GetPositionAtNextLine(ITextPointer position, double suggestedX, int count, out double newSuggestedX, out int linesMoved) 
        {
            Invariant.Assert(this.IsLayoutValid);
            Invariant.Assert(Contains(position));
 
            newSuggestedX = suggestedX;
            int lineIndex = GetLineIndexFromPosition(position); 
            int nextLineIndex = Math.Max(0, Math.Min(_lineMetrics.Count - 1, lineIndex + count)); 
            linesMoved = nextLineIndex - lineIndex;
 
            ITextPointer nextLinePosition;
            if (linesMoved == 0)
            {
                nextLinePosition = position.GetFrozenPointer(position.LogicalDirection); 
            }
            else if (DoubleUtil.IsNaN(suggestedX)) 
            { 
                nextLinePosition = _host.TextContainer.CreatePointerAtOffset(_lineMetrics[lineIndex + linesMoved].Offset, LogicalDirection.Forward);
            } 
            else
            {
                suggestedX -= GetTextAlignmentCorrection(this.CalculatedTextAlignment, GetWrappingWidth(this.RenderSize.Width));
                nextLinePosition = GetTextPositionFromDistance(nextLineIndex, suggestedX); 
            }
 
            nextLinePosition.Freeze(); 
            return nextLinePosition;
        } 

        /// 
        /// 
        ///  
        ITextPointer ITextView.GetPositionAtNextPage(ITextPointer position, Point suggestedOffset, int count, out Point newSuggestedOffset, out int pagesMoved)
        { 
            // This method is not expected to be called. 
            // Caller should only call this method when !ITextView.Contains(position).
            // Since TextBox is not paginated, this view always contains all TextContainer positions. 
            Invariant.Assert(false);

            newSuggestedOffset = new Point();
            pagesMoved = 0; 
            return null;
        } 
 
        /// 
        ///  
        /// 
        bool ITextView.IsAtCaretUnitBoundary(ITextPointer position)
        {
            Invariant.Assert(this.IsLayoutValid); 
            Invariant.Assert(Contains(position));
            bool boundary = false; 
 
            int lineIndex = GetLineIndexFromPosition(position);
 
            CharacterHit sourceCharacterHit = new CharacterHit();
            if (position.LogicalDirection == LogicalDirection.Forward)
            {
                // Forward context, go to leading edge of position offset 
                sourceCharacterHit = new CharacterHit(position.Offset, 0);
            } 
            else if (position.LogicalDirection == LogicalDirection.Backward) 
            {
                if (position.Offset > _lineMetrics[lineIndex].Offset) 
                {
                    // For backward context, go to trailing edge of previous character
                    sourceCharacterHit = new CharacterHit(position.Offset - 1, 1);
                } 
                else
                { 
                    // There is no previous trailing edge on this line. We don't consider this a unit boundary. 
                    return false;
                } 
            }

            using (TextBoxLine line = GetFormattedLine(lineIndex))
            { 
                boundary = line.IsAtCaretCharacterHit(sourceCharacterHit);
            } 
 
            return boundary;
        } 

        /// 
        /// 
        ///  
        ITextPointer ITextView.GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction)
        { 
            Invariant.Assert(this.IsLayoutValid); 
            Invariant.Assert(Contains(position));
 
            // Special case document start/end.
            if (position.Offset == 0 && direction == LogicalDirection.Backward)
            {
                return position.GetFrozenPointer(LogicalDirection.Forward); 
            }
            else if (position.Offset == _host.TextContainer.SymbolCount && direction == LogicalDirection.Forward) 
            { 
                return position.GetFrozenPointer(LogicalDirection.Backward);
            } 

            int lineIndex = GetLineIndexFromPosition(position);

            CharacterHit sourceCharacterHit = new CharacterHit(position.Offset, 0); 
            CharacterHit nextCharacterHit;
 
            using (TextBoxLine line = GetFormattedLine(lineIndex)) 
            {
                if (direction == LogicalDirection.Forward) 
                {
                    // Get the next caret position from the line
                    nextCharacterHit = line.GetNextCaretCharacterHit(sourceCharacterHit);
                } 
                else
                { 
                    // Get previous caret position from the line 
                    nextCharacterHit = line.GetPreviousCaretCharacterHit(sourceCharacterHit);
                } 
            }

            // Determine logical direction for next caret index and create TextPointer from it.
            LogicalDirection logicalDirection; 
            if (nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == _lineMetrics[lineIndex].EndOffset &&
                direction == LogicalDirection.Forward) 
            { 
                // Going forward brought us to the end of a line, context must be forward for next line.
                if (lineIndex == _lineMetrics.Count - 1) 
                {
                    // Last line so context must stay backward.
                    logicalDirection = LogicalDirection.Backward;
                } 
                else
                { 
                    logicalDirection = LogicalDirection.Forward; 
                }
            } 
            else if (nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == _lineMetrics[lineIndex].Offset &&
                     direction == LogicalDirection.Backward)
            {
                // Going backward brought us to the start of a line, context must be backward for previous line. 
                if (lineIndex == 0)
                { 
                    // First line, so we will stay forward. 
                    logicalDirection = LogicalDirection.Forward;
                } 
                else
                {
                    logicalDirection = LogicalDirection.Backward;
                } 
            }
            else 
            { 
                logicalDirection = (nextCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
            } 

            ITextPointer nextCaretUnitPosition = _host.TextContainer.CreatePointerAtOffset(nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength, logicalDirection);
            nextCaretUnitPosition.Freeze();
            return nextCaretUnitPosition; 
        }
 
        ///  
        /// 
        ///  
        ITextPointer ITextView.GetBackspaceCaretUnitPosition(ITextPointer position)
        {
            Invariant.Assert(this.IsLayoutValid);
            Invariant.Assert(Contains(position)); 

            // Special case document start. 
            if (position.Offset == 0) 
            {
                return position.GetFrozenPointer(LogicalDirection.Forward); 
            }

            int lineIndex = GetLineIndexFromPosition(position, LogicalDirection.Backward);
 
            CharacterHit sourceCharacterHit = new CharacterHit(position.Offset, 0);
            CharacterHit backspaceCharacterHit; 
 
            using (TextBoxLine line = GetFormattedLine(lineIndex))
            { 
                backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(sourceCharacterHit);
            }

            LogicalDirection logicalDirection; 
            if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == _lineMetrics[lineIndex].Offset)
            { 
                // Going backward brought us to the start of a line, context must be backward for previous line 
                if (lineIndex == 0)
                { 
                    // First line, so we will stay forward.
                    logicalDirection = LogicalDirection.Forward;
                }
                else 
                {
                    logicalDirection = LogicalDirection.Backward; 
                } 
            }
            else 
            {
                logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
            }
 
            ITextPointer backspaceUnitPosition = _host.TextContainer.CreatePointerAtOffset(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection);
            backspaceUnitPosition.Freeze(); 
            return backspaceUnitPosition; 
        }
 
        /// 
        /// 
        /// 
        TextSegment ITextView.GetLineRange(ITextPointer position) 
        {
            Invariant.Assert(this.IsLayoutValid); 
            Invariant.Assert(Contains(position)); 

            int lineIndex = GetLineIndexFromPosition(position); 

            ITextPointer start = _host.TextContainer.CreatePointerAtOffset(_lineMetrics[lineIndex].Offset, LogicalDirection.Forward);
            ITextPointer end = _host.TextContainer.CreatePointerAtOffset(_lineMetrics[lineIndex].Offset + _lineMetrics[lineIndex].ContentLength, LogicalDirection.Forward);
 
            return new TextSegment(start, end, true);
        } 
 
        /// 
        ///  
        /// 
        ReadOnlyCollection ITextView.GetGlyphRuns(ITextPointer start, ITextPointer end)
        {
            // This method is not expected to be called. 
            Invariant.Assert(false);
            return null; 
        } 

        ///  
        /// 
        /// 
        bool ITextView.Contains(ITextPointer position)
        { 
            return Contains(position);
        } 
 
        /// 
        ///  
        /// 
        void ITextView.BringPositionIntoViewAsync(ITextPointer position, object userState)
        {
            // This method is not expected to be called. 
            // Caller should only call this method when !ITextView.Contains(position).
            // Since TextBox is not paginated, this view always contains all TextContainer positions. 
            Invariant.Assert(false); 
        }
 
        /// 
        /// 
        /// 
        void ITextView.BringPointIntoViewAsync(Point point, object userState) 
        {
            // This method is not expected to be called. 
            // Caller should only call this method when !ITextView.Contains(position). 
            // Since TextBox is not paginated, this view always contains all TextContainer positions.
            Invariant.Assert(false); 
        }

        /// 
        ///  
        /// 
        void ITextView.BringLineIntoViewAsync(ITextPointer position, double suggestedX, int count, object userState) 
        { 
            // This method is not expected to be called.
            // Caller should only call this method when !ITextView.Contains(position). 
            // Since TextBox is not paginated, this view always contains all TextContainer positions.
            Invariant.Assert(false);
        }
 
        /// 
        ///  
        ///  
        void ITextView.BringPageIntoViewAsync(ITextPointer position, Point suggestedOffset, int count, object userState)
        { 
            // This method is not expected to be called.
            // Caller should only call this method when !ITextView.Contains(position).
            // Since TextBox is not paginated, this view always contains all TextContainer positions.
            Invariant.Assert(false); 
        }
 
        ///  
        /// 
        ///  
        void ITextView.CancelAsync(object userState)
        {
            // This method is not expected to be called.
            // Caller should only call this method when !ITextView.Contains(position). 
            // Since TextBox is not paginated, this view always contains all TextContainer positions.
            Invariant.Assert(false); 
        } 

        ///  
        /// 
        /// 
        bool ITextView.Validate()
        { 
            UpdateLayout();
            return this.IsLayoutValid; 
        } 

        ///  
        /// 
        /// 
        bool ITextView.Validate(Point point)
        { 
            return ((ITextView)this).Validate();
        } 
 
        /// 
        ///  
        /// 
        bool ITextView.Validate(ITextPointer position)
        {
            if (position.TextContainer != _host.TextContainer) 
                return false;
 
            if (!this.IsLayoutValid) 
            {
                // UpdateLayout has side-effects even when measure and arrange are clean, 
                // so avoid calling it unless we must.
                UpdateLayout();

                if (!this.IsLayoutValid) 
                {
                    // If we can't get the layout system to give us a valid 
                    // measure/arrange, there's no hope. 
                    return false;
                } 
            }

            // Force background layout iterations until we catch up
            // with the position. 

            int lastValidOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset; 
 
            while (!Contains(position))
            { 
                InvalidateMeasure();
                UpdateLayout();

                // UpdateLayout may invalidate the view. 
                if (!this.IsLayoutValid)
                    break; 
 
                // Break if background layout is not progressing.
                int newLastValidOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset; 
                if (lastValidOffset >= newLastValidOffset)
                    break;
                lastValidOffset = newLastValidOffset;
            } 

            return this.IsLayoutValid && Contains(position); 
        } 

        ///  
        /// 
        /// 
        void ITextView.ThrottleBackgroundTasksForUserInput()
        { 
            if (_throttleBackgroundTimer == null)
            { 
                // Start up a timer.  Until the timer fires, we'll disable 
                // all background layout.  This leaves the TextBox responsive
                // to user input. 
                _throttleBackgroundTimer = new DispatcherTimer(DispatcherPriority.Background);
                _throttleBackgroundTimer.Interval = new TimeSpan(0, 0, _throttleBackgroundSeconds);
                _throttleBackgroundTimer.Tick += new EventHandler(OnThrottleBackgroundTimeout);
            } 
            else
            { 
                // Reset the timer. 
                _throttleBackgroundTimer.Stop();
            } 

            _throttleBackgroundTimer.Start();
        }
 
        // Forces a full document invalidation.
        // Called when properties that do affect layout (eg, FontSize) 
        // change value. 
        internal void Remeasure()
        { 
            if (_lineMetrics != null)
            {
                _lineMetrics.Clear();
                _viewportLineVisuals = null; 
            }
            InvalidateMeasure(); 
        } 

        // Forces a visual invalidation. 
        // Called when properties that do not affect layout (eg, ForegroundColor)
        // change value.
        internal void Rerender()
        { 
            _viewportLineVisuals = null;
            InvalidateArrange(); 
        } 

        // Returns the index of the line containing the specified offset. 
        // Offset has forward direction -- we always return the following
        // line in ambiguous cases.
        internal int GetLineIndexFromOffset(int offset)
        { 
            int index = -1;
            int min = 0; 
            int max = _lineMetrics.Count; 

            Invariant.Assert(_lineMetrics.Count >= 1); 

            while (true)
            {
                Invariant.Assert(min < max, "Couldn't find offset!"); 

                index = min + (max - min) / 2; 
                LineRecord record = _lineMetrics[index]; 

                if (offset < record.Offset) 
                {
                    max = index;
                }
                else if (offset > record.EndOffset) 
                {
                    min = index + 1; 
                } 
                else
                { 
                    if (offset == record.EndOffset && index < _lineMetrics.Count - 1)
                    {
                        // Go to the next line if we're between two lines.
                        index++; 
                    }
                    break; 
                } 
            }
 
            return index;
        }

        #endregion Internal Methods 

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

        #region Internal Properties
 
        // Control that owns this TextBoxView.
        internal ITextBoxViewHost Host 
        { 
            get
            { 
                return _host;
            }
        }
 
        /// 
        ///  
        ///  
        UIElement ITextView.RenderScope
        { 
            get
            {
                return this;
            } 
        }
 
        ///  
        /// 
        ///  
        ITextContainer ITextView.TextContainer
        {
            get
            { 
                return _host.TextContainer;
            } 
        } 

        ///  
        /// 
        /// 
        bool ITextView.IsValid
        { 
            get
            { 
                return this.IsLayoutValid; 
            }
        } 

        /// 
        /// 
        ///  
        bool ITextView.RendersOwnSelection
        { 
            get 
            {
                return false; 
            }
        }

 
        /// 
        ///  
        ///  
        ReadOnlyCollection ITextView.TextSegments
        { 
            get
            {
                List segments = new List(1);
                if (_lineMetrics != null) 
                {
                    ITextPointer start = _host.TextContainer.CreatePointerAtOffset(_lineMetrics[0].Offset, LogicalDirection.Backward); 
                    ITextPointer end = _host.TextContainer.CreatePointerAtOffset(_lineMetrics[_lineMetrics.Count-1].EndOffset, LogicalDirection.Forward); 

                    segments.Add(new TextSegment(start, end, true)); 
                }
                return new ReadOnlyCollection(segments);
            }
        } 

        #endregion Internal Properties 
 
        //-----------------------------------------------------
        // 
        //  Internal Events
        //
        //------------------------------------------------------
 
        #region Internal Events
 
        ///  
        /// 
        ///  
        // Caller should only call this method when !ITextView.Contains(position).
        // Since TextBox is not paginated, this view always contains all TextContainer positions.
        event BringPositionIntoViewCompletedEventHandler ITextView.BringPositionIntoViewCompleted
        { 
            add { Invariant.Assert(false); }
            remove { Invariant.Assert(false); } 
        } 

        ///  
        /// 
        /// 
        // Caller should only call this method when !ITextView.Contains(position).
        // Since TextBox is not paginated, this view always contains all TextContainer positions. 
        event BringPointIntoViewCompletedEventHandler ITextView.BringPointIntoViewCompleted
        { 
            add { Invariant.Assert(false); } 
            remove { Invariant.Assert(false); }
        } 

        /// 
        /// 
        ///  
        // Caller should only call this method when !ITextView.Contains(position).
        // Since TextBox is not paginated, this view always contains all TextContainer positions. 
        event BringLineIntoViewCompletedEventHandler ITextView.BringLineIntoViewCompleted 
        {
            add { Invariant.Assert(false); } 
            remove { Invariant.Assert(false); }
        }

        ///  
        /// 
        ///  
        // Caller should only call this method when !ITextView.Contains(position). 
        // Since TextBox is not paginated, this view always contains all TextContainer positions.
        event BringPageIntoViewCompletedEventHandler ITextView.BringPageIntoViewCompleted 
        {
            add { Invariant.Assert(false); }
            remove { Invariant.Assert(false); }
        } 

        ///  
        ///  
        /// 
        event EventHandler ITextView.Updated 
        {
            add { UpdatedEvent += value; }
            remove { UpdatedEvent -= value; }
        } 

        #endregion Internal Events 
 
        //------------------------------------------------------
        // 
        //  Private Methods
        //
        //-----------------------------------------------------
 
        #region Private Methods
 
        // Initializes TextContainer event listeners. 
        // Called on the first Measure.
        // We delay the init to avoid responding to events before we're attached 
        // to the visual tree, when it doesn't matter.
        private void EnsureTextContainerListeners()
        {
            if (CheckFlags(Flags.TextContainerListenersInitialized)) 
                return;
 
            _host.TextContainer.Changing += new EventHandler(OnTextContainerChanging); 
            _host.TextContainer.Change += new TextContainerChangeEventHandler(OnTextContainerChange);
            _host.TextContainer.Highlights.Changed += new HighlightChangedEventHandler(OnHighlightChanged); 

            SetFlags(true, Flags.TextContainerListenersInitialized);
        }
 
        // Initializes state used across a measure/arrange calculation.
        private void EnsureCache() 
        { 
            if (_cache == null)
            { 
                _cache = new TextCache(this);
            }
        }
 
        // Reads the current (interesting) property values on the owning TextBox.
        private LineProperties GetLineProperties() 
        { 
            TextProperties defaultTextProperties = new TextProperties((Control)_host, _host.IsTypographyDefaultValue);
 
            // Pass page width and height as double.MaxValue when creating LineProperties, since TextBox does not restrict
            // TextIndent or LineHeight.
            return new LineProperties((Control)_host, (Control)_host, defaultTextProperties, null, this.CalculatedTextAlignment);
        } 

        // Callback from the TextContainer when a change block starts. 
        private void OnTextContainerChanging(object sender, EventArgs args) 
        {
            // 
        }

        // Callback from the TextContainer on a document edit.
        private void OnTextContainerChange(object sender, TextContainerChangeEventArgs args) 
        {
            if (args.Count == 0) 
            { 
                // A no-op for this control.  Happens when IMECharCount updates happen
                // without corresponding SymbolCount changes. 
                return;
            }

            // 
            // Add the change to our dirty list.
            // 
 
            if (_dirtyList == null)
            { 
                _dirtyList = new DtrList();
            }

            DirtyTextRange dirtyTextRange = new DirtyTextRange(args); 
            _dirtyList.Merge(dirtyTextRange);
 
            // 
            // Force a re-measure.
            // 
            InvalidateMeasure();
        }

        // Callback from the TextContainer when a highlight changes. 
        private void OnHighlightChanged(object sender, HighlightChangedEventArgs args)
        { 
            // The only supported highlight type for TextBoxView is SpellerHighlight. 
            if (args.OwnerType != typeof(SpellerHighlightLayer))
            { 
                return;
            }

            if (_dirtyList == null) 
            {
                _dirtyList = new DtrList(); 
            } 

            // 
            // Add the change to our dirty list.
            //

            foreach (TextSegment segment in args.Ranges) 
            {
                int positionsCovered = segment.End.Offset - segment.Start.Offset; 
                DirtyTextRange dirtyTextRange = new DirtyTextRange(segment.Start.Offset, positionsCovered, positionsCovered); 
                _dirtyList.Merge(dirtyTextRange);
            } 

            //
            // Force a re-measure.
            // 
            // NB: it's not currently possible to InvalidateArrange here.
            // "Render only" changes from the highlight layer change the way we 
            // ultimately feed text to the formatter.  Introducing breaks for 
            // highlights may actually change the layout of the text as
            // characters are interpreted in different contexts.  Dev10 Bugs 
            // 511849 has an example.
            //
            InvalidateMeasure();
        } 

        // Sets boolean state. 
        private void SetFlags(bool value, Flags flags) 
        {
            _flags = value ? (_flags | flags) : (_flags & (~flags)); 
        }

        // Reads boolean state.
        private bool CheckFlags(Flags flags) 
        {
            return ((_flags & flags) == flags); 
        } 

        // Announces a layout change to any listeners. 
        private void FireTextViewUpdatedEvent()
        {
            if (UpdatedEvent != null)
            { 
                UpdatedEvent(this, EventArgs.Empty);
            } 
        } 

        // Returns the index of a line containing point, or -1 if no such 
        // line exists.  If snapToText is true, the closest match is returned.
        //
        // Point must be in document space.
        private int GetLineIndexFromPoint(Point point, bool snapToText) 
        {
            Invariant.Assert(_lineMetrics.Count >= 1); 
 
            // Special case points above or below the content.
 
            if (point.Y < 0)
            {
                return snapToText ? 0 : -1;
            } 
            if (point.Y >= _lineHeight * _lineMetrics.Count)
            { 
                return snapToText ? _lineMetrics.Count-1 : -1; 
            }
 
            // Do a binary search to find the matching line.

            int index = -1;
            int min = 0; 
            int max = _lineMetrics.Count;
 
            while (min < max) 
            {
                index = min + (max - min) / 2; 
                LineRecord record = _lineMetrics[index];
                double lineY = _lineHeight * index;

                if (point.Y < lineY) 
                {
                    max = index; 
                } 
                else if (point.Y >= lineY + _lineHeight)
                { 
                    min = index + 1;
                }
                else
                { 
                    if (!snapToText &&
                        (point.X < 0 || point.X >= record.Width)) 
                    { 
                        index = -1;
                    } 
                    break;
                }
            }
 
            return (min < max) ? index : -1;
        } 
 
        // Returns the index of the line containing position.
        private int GetLineIndexFromPosition(ITextPointer position) 
        {
            return GetLineIndexFromOffset(position.Offset, position.LogicalDirection);
        }
 
        // Returns the index of the line containing position.
        private int GetLineIndexFromPosition(ITextPointer position, LogicalDirection direction) 
        { 
            return GetLineIndexFromOffset(position.Offset, direction);
        } 

        // Returns the index of the line containing the specified offset.
        private int GetLineIndexFromOffset(int offset, LogicalDirection direction)
        { 
            if (offset > 0 && direction == LogicalDirection.Backward)
            { 
                // GetLineIndexFromOffset has forward bias, so backup for backward search. 
                offset--;
            } 

            return GetLineIndexFromOffset(offset);
        }
 
        // Returns a formatted TextBoxLine at the specified index.
        // Caller must Dispose the TextBoxLine. 
        // This method is expensive. 
        private TextBoxLine GetFormattedLine(int lineIndex)
        { 
            LineProperties lineProperties;
            return GetFormattedLine(lineIndex, out lineProperties);
        }
 
        // Returns a formatted TextBoxLine at the specified index.
        // Caller must Dispose the TextBoxLine. 
        // This method is expensive. 
        private TextBoxLine GetFormattedLine(int lineIndex, out LineProperties lineProperties)
        { 
            TextBoxLine line = new TextBoxLine(this);
            LineRecord metrics = _lineMetrics[lineIndex];
            lineProperties = GetLineProperties();
 
            Control hostControl = (Control)_host;
            TextFormattingMode textFormattingMode = TextOptions.GetTextFormattingMode(hostControl); 
            TextFormatter formatter = TextFormatter.FromCurrentDispatcher(textFormattingMode); 

            double width = GetWrappingWidth(this.RenderSize.Width); 
            double formatWidth = GetWrappingWidth(_previousConstraint.Width);

            line.Format(metrics.Offset, formatWidth, width, lineProperties, new TextRunCache(), formatter);
            Invariant.Assert(metrics.Length == line.Length, "Line is out of [....] with metrics!"); 

            return line; 
        } 

        // Returns a TextPointer at the position closest to pixel offset x 
        // on a specified line.
        private ITextPointer GetTextPositionFromDistance(int lineIndex, double x)
        {
            LineProperties lineProperties; 
            CharacterHit charIndex;
            LogicalDirection logicalDirection; 
 
            using (TextBoxLine line = GetFormattedLine(lineIndex, out lineProperties))
            { 
                charIndex = line.GetTextPositionFromDistance(x);

                logicalDirection = (charIndex.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward;
            } 

            return _host.TextContainer.CreatePointerAtOffset(charIndex.FirstCharacterIndex + charIndex.TrailingLength, logicalDirection); 
        } 

        // Updates IScrollInfo related state on an ArrangeOverride call. 
        private void ArrangeScrollData(Size arrangeSize)
        {
            if (_scrollData == null)
            { 
                return;
            } 
 
            bool invalidateScrollInfo = false;
 
            if (!DoubleUtil.AreClose(_scrollData.Viewport, arrangeSize))
            {
                _scrollData.Viewport = arrangeSize;
                invalidateScrollInfo = true; 
            }
 
            if (!DoubleUtil.AreClose(_scrollData.Extent, _contentSize)) 
            {
                _scrollData.Extent = _contentSize; 
                invalidateScrollInfo = true;
            }

            Vector offset = new Vector( 
                Math.Max(0, Math.Min(_scrollData.ExtentWidth - _scrollData.ViewportWidth, _scrollData.HorizontalOffset)),
                Math.Max(0, Math.Min(_scrollData.ExtentHeight - _scrollData.ViewportHeight, _scrollData.VerticalOffset))); 
 
            if (!DoubleUtil.AreClose(offset, _scrollData.Offset))
            { 
                _scrollData.Offset = offset;
                invalidateScrollInfo = true;
            }
 
            if (invalidateScrollInfo && _scrollData.ScrollOwner != null)
            { 
                _scrollData.ScrollOwner.InvalidateScrollInfo(); 
            }
        } 

        // Updates line visuals on an ArrangeOverride call.
        private void ArrangeVisuals(Size arrangeSize)
        { 
            Invariant.Assert(_dirtyList == null); // We should never see pending incremental updates during arrange.
 
            // 
            // Initialize state.
            // 

            if (_visualChildren == null)
            {
                _visualChildren = new List(1) ; 
            }
 
            EnsureCache(); 

            LineProperties lineProperties = _cache.LineProperties; 
            TextBoxLine line = new TextBoxLine(this);

            //
            // Calculate the current viewport extent, in lines. 
            // We won't do any work for lines that aren't visible.
            // 
 
            int firstLineIndex;
            int lastLineIndex; 

            GetVisibleLines(out firstLineIndex, out lastLineIndex);

            SetViewportLines(firstLineIndex, lastLineIndex); 

            double width = GetWrappingWidth(arrangeSize.Width); 
 
            double horizontalOffset = GetTextAlignmentCorrection(lineProperties.TextAlignment, width);
            double verticalOffset = this.VerticalAlignmentOffset; 

            if (_scrollData != null)
            {
                horizontalOffset -= _scrollData.HorizontalOffset; 
                verticalOffset -= _scrollData.VerticalOffset;
            } 
 
            // Remove invalidated lines from the visual tree.
            DetachDiscardedVisualChildren(); 

            //
            // Iterate across the visible lines.
            // If we have a cached visual, simply update its current offset. 
            // Otherwise, allocate and render a new visual.
            // 
 
            double formatWidth = GetWrappingWidth(_previousConstraint.Width);
 
            for (int lineIndex = firstLineIndex; lineIndex <= lastLineIndex; lineIndex++)
            {
                TextBoxLineDrawingVisual lineVisual = GetLineVisual(lineIndex);
 
                if (lineVisual == null)
                { 
                    LineRecord metrics = _lineMetrics[lineIndex]; 

                    using (line) 
                    {
                        line.Format(metrics.Offset, formatWidth, width, lineProperties, _cache.TextRunCache, _cache.TextFormatter);

                        // We should be in [....] with current metrics, unless background layout is pending. 
                        if (!this.IsBackgroundLayoutPending)
                        { 
                            Invariant.Assert(metrics.Length == line.Length, "Line is out of [....] with metrics!"); 
                        }
 
                        lineVisual = line.CreateVisual();
                    }

                    SetLineVisual(lineIndex, lineVisual); 
                    AttachVisualChild(lineVisual);
                } 
 
                lineVisual.Offset = new Vector(horizontalOffset, verticalOffset + lineIndex * _lineHeight);
            } 
        }

        // Removes lines that were discarded during Measure from the visual tree. We don't want to
        // clear all of the visual children and then add lines that were already in the visual tree 
        // back because native resources will get freed and reallocated unnecessarily (ref count goes
        // to 0 -- see Dev10 bug 607756). 
        // 
        // It is safe to modify the visual tree in Arrange, but there are no guarantees during Measure.
        // It might be possible to get rid of TextBoxLineDrawingVisual and remove items from the 
        // visual tree during Measure as well.
        private void DetachDiscardedVisualChildren()
        {
            int j = _visualChildren.Count - 1; // last non-discarded element index 

            for (int i = _visualChildren.Count - 1; i >= 0; i--) 
            { 
                if (_visualChildren[i] == null || _visualChildren[i].DiscardOnArrange)
                { 
                    RemoveVisualChild(_visualChildren[i]);

                    if (i < j)
                    { 
                        _visualChildren[i] = _visualChildren[j];
                    } 
 
                    j--;
                } 
            }

            if (j < _visualChildren.Count - 1)
            { 
                _visualChildren.RemoveRange(j + 1, _visualChildren.Count - j - 1);
            } 
        } 

        // Adds a line visual to the visual tree. 
        private void AttachVisualChild(TextBoxLineDrawingVisual lineVisual)
        {
            AddVisualChild(lineVisual);
            _visualChildren.Add(lineVisual); 
        }
 
        // Removes all line visuals from the visual tree. 
        private void ClearVisualChildren()
        { 
            for (int i = 0; i < _visualChildren.Count; i++)
            {
                RemoveVisualChild(_visualChildren[i]);
            } 

            _visualChildren.Clear(); 
        } 

        // Transforms a Point in visual space (where (0, 0) is the upper-left 
        // corner of this FrameworkElement) to document space (where (0, 0) is
        // the upper-left corner of the document, which may be scrolled to a
        // negative offset relative to visual space).
        private Point TransformToDocumentSpace(Point point) 
        {
            if (_scrollData != null) 
            { 
                point = new Point(point.X + _scrollData.HorizontalOffset, point.Y + _scrollData.VerticalOffset);
            } 

            point.X -= GetTextAlignmentCorrection(this.CalculatedTextAlignment, GetWrappingWidth(this.RenderSize.Width));
            point.Y -= this.VerticalAlignmentOffset;
 
            return point;
        } 
 
        // Transforms a Rect in document space (where (0, 0) is
        // the upper-left corner of the document, which may be scrolled to a 
        // negative offset relative to visual space) to visual space
        // (where (0, 0) is the upper-left corner of this FrameworkElement).
        private Rect TransformToVisualSpace(Rect rect)
        { 
            if (_scrollData != null)
            { 
                rect.X -= _scrollData.HorizontalOffset; 
                rect.Y -= _scrollData.VerticalOffset;
            } 

            rect.X += GetTextAlignmentCorrection(this.CalculatedTextAlignment, GetWrappingWidth(this.RenderSize.Width));
            rect.Y += this.VerticalAlignmentOffset;
 
            return rect;
        } 
 
        // Helper for GetTightBoundingGeometryFromTextPositions.
        // Calculates the geometry of a single line intersected with a pair of document offsets. 
        private void GetTightBoundingGeometryFromLineIndex(int lineIndex, int unclippedStartOffset, int unclippedEndOffset, TextAlignment alignment, double endOfParaGlyphWidth, ref Geometry geometry)
        {
            IList bounds;
 
            int startOffset = Math.Max(_lineMetrics[lineIndex].Offset, unclippedStartOffset);
            int endOffset = Math.Min(_lineMetrics[lineIndex].EndOffset, unclippedEndOffset); 
 
            if (startOffset == endOffset) // GetRangeBounds does not accept empty runs.
            { 
                // If we have any empty intersection, the only case to handle is when
                // the empty range is exactly at the end of a line with a hard break.
                // In that case we need to add the newline whitespace geometry.
                if (unclippedStartOffset == _lineMetrics[lineIndex].EndOffset) 
                {
                    ITextPointer position = _host.TextContainer.CreatePointerAtOffset(unclippedStartOffset, LogicalDirection.Backward); 
                    if (TextPointerBase.IsNextToPlainLineBreak(position, LogicalDirection.Backward)) 
                    {
                        Rect rect = new Rect(0, lineIndex * _lineHeight, endOfParaGlyphWidth, _lineHeight); 
                        CaretElement.AddGeometry(ref geometry, new RectangleGeometry(rect));
                    }
                }
                else 
                {
                    Invariant.Assert(endOffset == _lineMetrics[lineIndex].Offset); 
                } 
            }
            else 
            {
                using (TextBoxLine line = GetFormattedLine(lineIndex))
                {
                    bounds = line.GetRangeBounds(startOffset, endOffset - startOffset, 0, lineIndex * _lineHeight); 
                }
 
                for (int i = 0; i < bounds.Count; i++) 
                {
                    Rect rect = TransformToVisualSpace(bounds[i]); 
                    CaretElement.AddGeometry(ref geometry, new RectangleGeometry(rect));
                }

                // Add the Rect representing end-of-line, if the range covers the line end 
                // and the line has a hard line break.
                if (unclippedEndOffset >= _lineMetrics[lineIndex].EndOffset) 
                { 
                    ITextPointer endOfLinePosition = _host.TextContainer.CreatePointerAtOffset(endOffset, LogicalDirection.Backward);
 
                    if (TextPointerBase.IsNextToPlainLineBreak(endOfLinePosition, LogicalDirection.Backward))
                    {
                        double contentOffset = GetContentOffset(_lineMetrics[lineIndex].Width, alignment);
                        Rect rect = new Rect(contentOffset + _lineMetrics[lineIndex].Width, lineIndex * _lineHeight, endOfParaGlyphWidth, _lineHeight); 
                        rect = TransformToVisualSpace(rect);
                        CaretElement.AddGeometry(ref geometry, new RectangleGeometry(rect)); 
                    } 
                }
            } 
        }

        // Returns the indices of the first and last lines that intersect
        // with the current viewport. 
        private void GetVisibleLines(out int firstLineIndex, out int lastLineIndex)
        { 
            Rect viewport = this.Viewport; 

            if (!viewport.IsEmpty) 
            {
                firstLineIndex = (int)(viewport.Y / _lineHeight);
                lastLineIndex = (int)Math.Ceiling((viewport.Y + viewport.Height) / _lineHeight) - 1;
 
                // There may not be enough lines to fill the viewport, clip appropriately.
                firstLineIndex = Math.Max(0, Math.Min(firstLineIndex, _lineMetrics.Count - 1)); 
                lastLineIndex = Math.Max(0, Math.Min(lastLineIndex, _lineMetrics.Count - 1)); 
            }
            else 
            {
                // If we're not hosted by a ScrollViewer, the viewport is the whole doc.
                firstLineIndex = 0;
                lastLineIndex = _lineMetrics.Count - 1; 
            }
        } 
 
        // Performs one iteration of background measure.
        // Background measure always works at the end of the current 
        // line metrics array -- invalidations to prevoiusly examined
        // content is handled by incremental layout, synchronously.
        //
        // Returns the full content size, omitting any unanalyzed content 
        // at the document end.
        private Size FullMeasureTick(double constraintWidth, LineProperties lineProperties) 
        { 
            Size desiredSize;
            TextBoxLine line = new TextBoxLine(this); 
            int lineOffset;
            bool endOfParagraph;

            // Find the next position for this iteration. 

            if (_lineMetrics.Count == 0) 
            { 
                desiredSize = new Size();
                lineOffset = 0; 
            }
            else
            {
                desiredSize = _contentSize; 
                lineOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset;
            } 
 
            // Calculate a stop time.
            // We limit work to just a few milliseconds per iteration 
            // to avoid blocking the thread.
            DateTime stopTime;

            if ((ScrollBarVisibility)((Control)_host).GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty) == ScrollBarVisibility.Auto) 
            {
                // Workaround for bug 1766924. 
                // When VerticalScrollBarVisiblity == Auto, there's a problem with 
                // our interaction with ScrollViewer.  Disable background layout to
                // mitigate the problem until we can take a real fix in v.next. 
                //
                stopTime = DateTime.MaxValue;
            }
            else 
            {
                stopTime = DateTime.Now.AddMilliseconds(_maxMeasureTimeMs); 
            } 

            // Format lines until we hit the end of document or run out of time. 

            do
            {
                using (line) 
                {
                    line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); 
 
                    // This is a loop invariant, but has negligable cost.
                    // 
                    _lineHeight = lineProperties.CalcLineAdvance(line.Height);

                    _lineMetrics.Add(new LineRecord(lineOffset, line));
 
                    // Desired width is always max of calculated line widths.
                    // Desired height is sum of all line heights. 
                    desiredSize.Width = Math.Max(desiredSize.Width, line.Width); 
                    desiredSize.Height += _lineHeight;
 
                    lineOffset += line.Length;
                    endOfParagraph = line.EndOfParagraph;
                }
            } 
            while (!endOfParagraph && DateTime.Now < stopTime);
 
            if (!endOfParagraph) 
            {
                // Ran out of time.  Defer to background layout. 
                SetFlags(true, Flags.BackgroundLayoutPending);
                this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnBackgroundMeasure), null);
            }
            else 
            {
                // Finished the entire document.  Stop background layout. 
                SetFlags(false, Flags.BackgroundLayoutPending); 
            }
 
            return desiredSize;
        }

        // Callback for the next background layout tick. 
        private object OnBackgroundMeasure(object o)
        { 
            if (_throttleBackgroundTimer == null) 
            {
                InvalidateMeasure(); 
            }

            return null;
        } 

        // Measures content invalidated due to a TextContainer change (rather than 
        // a constraint change). 
        //
        // Returns the full content size, omitting any unanalyzed content 
        // at the document end (due to pending background layout).
        private Size IncrementalMeasure(double constraintWidth, LineProperties lineProperties)
        {
            Invariant.Assert(_dirtyList != null); 
            Invariant.Assert(_dirtyList.Length > 0); // We only allocate _dirtyList when it has content.
 
            Size desiredSize = _contentSize; 
            DirtyTextRange range = _dirtyList[0];
 
            // Background layout may be running, in which case we need to
            // "clip" the scope of this incremental edit.  We want to ignore
            // changes that extend past the area of the document we're already
            // tracking. 
            if (range.StartIndex > _lineMetrics[_lineMetrics.Count - 1].EndOffset)
            { 
                Invariant.Assert(this.IsBackgroundLayoutPending); 
                return desiredSize;
            } 

            // Merge the dirty list into a single superset DirtyTextRange.
            //
 

 
 
            int previousOffset = range.StartIndex;
            int positionsAdded = range.PositionsAdded; 
            int positionsRemoved = range.PositionsRemoved;

            for (int i = 1; i < _dirtyList.Length; i++)
            { 
                range = _dirtyList[i];
 
                if (range.StartIndex > _lineMetrics[_lineMetrics.Count - 1].EndOffset) 
                {
                    Invariant.Assert(this.IsBackgroundLayoutPending); 
                    break;
                }

                int rangeDistance = range.StartIndex - previousOffset; 
                positionsAdded += rangeDistance + range.PositionsAdded;
                positionsRemoved += rangeDistance + range.PositionsRemoved; 
 
                previousOffset = range.StartIndex;
            } 

            range = new DirtyTextRange(_dirtyList[0].StartIndex, positionsAdded, positionsRemoved);

            if (range.PositionsAdded >= range.PositionsRemoved) 
            {
                IncrementalMeasureLinesAfterInsert(constraintWidth, lineProperties, range, ref desiredSize); 
            } 
            else if (range.PositionsAdded < range.PositionsRemoved)
            { 
                IncrementalMeasureLinesAfterDelete(constraintWidth, lineProperties, range, ref desiredSize);
            }

            return desiredSize; 
        }
 
        // Measures content invalidated due to a TextContainer change. 
        private void IncrementalMeasureLinesAfterInsert(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize)
        { 
            int delta = range.PositionsAdded - range.PositionsRemoved;
            Invariant.Assert(delta >= 0);

            int lineIndex = GetLineIndexFromOffset(range.StartIndex, LogicalDirection.Forward); 

            if (delta > 0) 
            { 
                // Increment of the offsets of all following lines.
                // 
                for (int i = lineIndex + 1; i < _lineMetrics.Count; i++)
                {
                    _lineMetrics[i].Offset += delta;
                } 
            }
 
            TextBoxLine line = new TextBoxLine(this); 
            int lineOffset;
            bool endOfParagraph = false; 

            // We need to re-format the previous line, because if someone inserted
            // a hard break, the first directly affected line might now be shorter
            // and mergeable with its predecessor. 
            if (lineIndex > 0) //
            { 
                FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); 
            }
            else 
            {
                lineOffset = _lineMetrics[lineIndex].Offset;
            }
 
            // Format the line directly affected by the change.
            // If endOfParagraph == true, then the line was absorbed into its 
            // predessor (because its new content is thinner, or because the 
            // TextWrapping property changed).
            if (!endOfParagraph) 
            {
                using (line)
                {
                    line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); 

                    _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); 
 
                    lineOffset += line.Length;
                    endOfParagraph = line.EndOfParagraph; 
                }
                ClearLineVisual(lineIndex);
                lineIndex++;
            } 

            // Recalc the following lines not directly affected as needed. 
            SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset); 

            desiredSize = BruteForceCalculateDesiredSize(); 
        }

        // Measures content invalidated due to a TextContainer change.
        private void IncrementalMeasureLinesAfterDelete(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize) 
        {
            int delta = range.PositionsAdded - range.PositionsRemoved; 
            Invariant.Assert(delta < 0); 

            int firstLineIndex = GetLineIndexFromOffset(range.StartIndex); 

            // Clip the scope of the affected lines to the region of the document
            // we've already inspected.  Clipping happens when background layout
            // has not yet completed but an incremental update happens. 
            int endOffset = range.StartIndex + -delta - 1;
            if (endOffset > _lineMetrics[_lineMetrics.Count - 1].EndOffset) 
            { 
                Invariant.Assert(this.IsBackgroundLayoutPending);
                endOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset; 
                if (range.StartIndex == endOffset)
                {
                    // Nothing left to do until background layout runs.
                    return; 
                }
            } 
 
            int lastLineIndex = GetLineIndexFromOffset(endOffset);
 
            // Increment the offsets of all following lines.
            //
            for (int i = lastLineIndex + 1; i < _lineMetrics.Count; i++)
            { 
                _lineMetrics[i].Offset += delta;
            } 
 
            TextBoxLine line = new TextBoxLine(this);
            int lineIndex = firstLineIndex; 
            int lineOffset;
            bool endOfParagraph;

            // We need to re-format the previous line, because if someone inserted 
            // a hard break, the first directly affected line might now be shorter
            // and mergeable with its predecessor. 
            if (lineIndex > 0) // 
            {
                FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); 
            }
            else
            {
                lineOffset = _lineMetrics[lineIndex].Offset; 
                endOfParagraph = false;
            } 
 
            //
 


            // Update the first affected line.  If it's completely covered, remove it entirely below.
            if (!endOfParagraph && 
                (range.StartIndex > lineOffset || range.StartIndex + -delta < _lineMetrics[lineIndex].EndOffset))
            { 
                // Only part of the line is covered, reformat it. 
                using (line)
                { 
                    line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter);

                    _lineMetrics[lineIndex] = new LineRecord(lineOffset, line);
 
                    lineOffset += line.Length;
                    endOfParagraph = line.EndOfParagraph; 
                } 
                ClearLineVisual(lineIndex);
                lineIndex++; 
            }

            // Remove all the following lines that are completely covered.
            // 
            _lineMetrics.RemoveRange(lineIndex, lastLineIndex - lineIndex + 1);
            RemoveLineVisualRange(lineIndex, lastLineIndex - lineIndex + 1); 
 
            // Recalc the following lines not directly affected as needed.
            SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset); 

            desiredSize = BruteForceCalculateDesiredSize();
        }
 
        // Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete.
        // Formats the line preceding the first directly affected line after a TextContainer change. 
        // In general this line might grow as content in the following line is absorbed. 
        private void FormatFirstIncrementalLine(int lineIndex, double constraintWidth, LineProperties lineProperties, TextBoxLine line,
            out int lineOffset, out bool endOfParagraph) 
        {
            int originalEndOffset = _lineMetrics[lineIndex].EndOffset;
            lineOffset = _lineMetrics[lineIndex].Offset;
 
            using (line)
            { 
                line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); 

                _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); 

                lineOffset += line.Length;
                endOfParagraph = line.EndOfParagraph;
            } 

            // Don't clear the cached Visual unless something changed. 
            if (originalEndOffset != _lineMetrics[lineIndex].EndOffset) 
            {
                ClearLineVisual(lineIndex); 
            }
        }

        // Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete. 
        // Formats line until we hit a synchronization point, a position where we know
        // following lines could not be affected by the change. 
        private void SyncLineMetrics(DirtyTextRange range, double constraintWidth, LineProperties lineProperties, TextBoxLine line, 
            bool endOfParagraph, int lineIndex, int lineOffset)
        { 
            bool offsetSyncOk = (range.PositionsAdded == 0 || range.PositionsRemoved == 0);
            int lastCoveredCharOffset = range.StartIndex + Math.Max(range.PositionsAdded, range.PositionsRemoved);

            // Keep updating lines until we find a synchronized position. 
            while (!endOfParagraph &&
                   (lineIndex == _lineMetrics.Count || 
                    !offsetSyncOk || 
                    lineOffset != _lineMetrics[lineIndex].Offset))
            { 
                if (lineIndex < _lineMetrics.Count &&
                    lineOffset >= _lineMetrics[lineIndex].EndOffset)
                {
                    // If the current line offset starts past the current line metric offset, 
                    // remove the metric.  This happens when the previous line
                    // frees up enough space to completely consume the following line. 
                    // We can't simply replace the record without potentially missing our 
                    // [....] position.
                    _lineMetrics.RemoveAt(lineIndex); // 
                    RemoveLineVisualRange(lineIndex, 1);
                }
                else
                { 
                    using (line)
                    { 
                        line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); 

                        LineRecord record = new LineRecord(lineOffset, line); 

                        if (lineIndex == _lineMetrics.Count ||
                            lineOffset + line.Length <= _lineMetrics[lineIndex].Offset)
                        { 
                            // The new line preceeds the old line, insert a new record.
 
                            // 
                            _lineMetrics.Insert(lineIndex, record);
                            AddLineVisualPlaceholder(lineIndex); 
                        }
                        else
                        {
                            // We expect to be colliding with the old line directly. 
                            // If we extend past it, we're in danger of needlessly
                            // re-formatting the entire doc (ie, we miss the real 
                            // [....] position and don't stop until EndOfParagraph). 
                            Invariant.Assert(lineOffset < _lineMetrics[lineIndex].EndOffset);
 
                            _lineMetrics[lineIndex] = record;
                            ClearLineVisual(lineIndex);

                            // If this line ends past the invalidated region, and it 
                            // has a hard line break, it's safe to synchronize on the next
                            // line metric with a matching start offset. 
                            offsetSyncOk |= lastCoveredCharOffset <= record.EndOffset && line.HasLineBreak; 
                        }
 
                        lineIndex++;
                        lineOffset += line.Length;
                        endOfParagraph = line.EndOfParagraph;
                    } 
                }
            } 
 
            // Remove any trailing lines that got absorbed into the new last line.
            if (endOfParagraph && lineIndex < _lineMetrics.Count) 
            {
                int count = _lineMetrics.Count - lineIndex;
                _lineMetrics.RemoveRange(lineIndex, count);
                RemoveLineVisualRange(lineIndex, count); 
            }
        } 
 
        // Calculates the bounding box of the content.
        private Size BruteForceCalculateDesiredSize() 
        {
            Size desiredSize = new Size();

            // 
            for (int i = 0; i < _lineMetrics.Count; i++)
            { 
                desiredSize.Width = Math.Max(desiredSize.Width, _lineMetrics[i].Width); 
            }
            desiredSize.Height = _lineMetrics.Count * _lineHeight; 

            return desiredSize;
        }
 
        // Updates the array of cached Visuals matching lines in the viewport.
        // Called on arrange as the viewport changes. 
        private void SetViewportLines(int firstLineIndex, int lastLineIndex) 
        {
            List oldLineVisuals = _viewportLineVisuals; 
            int oldLineVisualsIndex = _viewportLineVisualsIndex;

            // Assume we'll clear the cache.
 
            _viewportLineVisuals = null;
            _viewportLineVisualsIndex = -1; 
 
            int count = lastLineIndex - firstLineIndex + 1;
 
            // Don't bother caching Visuals for single-line TextBoxes.
            // In this common case memory is important and the single line will
            // always be the one invalidated on an edit.
            if (count <= 1) 
            {
                ClearVisualChildren(); 
                return; 
            }
 
            // Re-init the cache to match the new viewport size.
            // Even if we don't have any Visuals to copy over from
            // the previous cache, it's useful to pre-allocate space
            // in the cache that will be filled incrementally during 
            // Arrange.
 
            _viewportLineVisuals = new List(count); 
            _viewportLineVisuals.AddRange(new TextBoxLineDrawingVisual[count]); //
            _viewportLineVisualsIndex = firstLineIndex; 

            if (oldLineVisuals == null)
            {
                ClearVisualChildren(); 
                return;
            } 
 
            // Copy over the intersection of the old viewport Visuals cache
            // with the new one. 

            // It would be convenient if the code below assumed that if
            // viewport size has changed, we never make it this far (the
            // old viewport visuals should have been thrown away, since 
            // there's no way now to map to the new constraint).
            // 
            // However, because of rounding error, we can end up in the situation 
            // where the indices/lengths between the two arrays vary, after
            // an arrange invalidation. 

            int oldLastLineIndex = oldLineVisualsIndex + oldLineVisuals.Count - 1;

            if (oldLineVisualsIndex <= lastLineIndex && 
                oldLastLineIndex >= firstLineIndex)
            { 
                int lineIndex = Math.Max(oldLineVisualsIndex, firstLineIndex); 
                int lineCount = Math.Min(oldLastLineIndex, firstLineIndex + count - 1) - lineIndex + 1;
 
                for (int i = 0; i < lineCount; i++)
                {
                    _viewportLineVisuals[lineIndex - _viewportLineVisualsIndex + i] = oldLineVisuals[lineIndex - oldLineVisualsIndex + i];
                } 

                // Mark discarded lines visuals so they can be removed from the visual tree in ArrangeVisuals. 
 
                for (int i = 0; i < lineIndex - oldLineVisualsIndex; i++)
                { 
                    if (oldLineVisuals[i] != null)
                    {
                        oldLineVisuals[i].DiscardOnArrange = true;
                    } 
                }
 
                for (int i = lineIndex - oldLineVisualsIndex + lineCount; i < oldLineVisuals.Count; i++) 
                {
                    if (oldLineVisuals[i] != null) 
                    {
                        oldLineVisuals[i].DiscardOnArrange = true;
                    }
                } 
            }
            else 
            { 
                ClearVisualChildren();
            } 
        }

        // Retrives the cached line Visual matching a line index in the
        // current viewport.  Will return null if no value is cached. 
        private TextBoxLineDrawingVisual GetLineVisual(int lineIndex)
        { 
            TextBoxLineDrawingVisual lineVisual = null; 

            if (_viewportLineVisuals != null) 
            {
                lineVisual = _viewportLineVisuals[lineIndex - _viewportLineVisualsIndex];
            }
 
            return lineVisual;
        } 
 
        // Adds a Visual to the line Visuals cache.
        private void SetLineVisual(int lineIndex, TextBoxLineDrawingVisual lineVisual) 
        {
            if (_viewportLineVisuals != null)
            {
                _viewportLineVisuals[lineIndex - _viewportLineVisualsIndex] = lineVisual; 
            }
        } 
 
        // Adds an empty entry to the line Visuals cache.
        private void AddLineVisualPlaceholder(int lineIndex) 
        {
            if (_viewportLineVisuals != null)
            {
                // Clip to visible region. 
                if (lineIndex >= _viewportLineVisualsIndex &&
                    lineIndex < _viewportLineVisualsIndex + _viewportLineVisuals.Count) 
                { 
                    _viewportLineVisuals.Insert(lineIndex - _viewportLineVisualsIndex, null);
                } 
            }
        }

        // Invalidates a cached line Visual. 
        private void ClearLineVisual(int lineIndex)
        { 
            if (_viewportLineVisuals != null) 
            {
                // Clip to visible region. 
                if (lineIndex >= _viewportLineVisualsIndex &&
                    lineIndex < _viewportLineVisualsIndex + _viewportLineVisuals.Count &&
                    _viewportLineVisuals[lineIndex - _viewportLineVisualsIndex] != null)
                { 
                    // Mark discarded line visual so it can be removed from the visual tree in ArrangeVisuals.
                    _viewportLineVisuals[lineIndex - _viewportLineVisualsIndex].DiscardOnArrange = true; 
                    _viewportLineVisuals[lineIndex - _viewportLineVisualsIndex] = null; 
                }
            } 
        }

        // Removes a range of Visuals from the line Visual cache.
        private void RemoveLineVisualRange(int lineIndex, int count) 
        {
            if (_viewportLineVisuals != null) 
            { 
                // Clip to visible region.
                if (lineIndex < _viewportLineVisualsIndex) 
                {
                    count -= _viewportLineVisualsIndex - lineIndex;
                    count = Math.Max(0, count);
                    lineIndex = _viewportLineVisualsIndex; 
                }
                if (lineIndex < _viewportLineVisualsIndex + _viewportLineVisuals.Count) 
                { 
                    int start = lineIndex - _viewportLineVisualsIndex;
                    count = Math.Min(count, _viewportLineVisuals.Count - start); 

                    // Mark discarded lines visuals so they can be removed from the visual tree in ArrangeVisuals.

                    for (int i = 0; i < count; i++) 
                    {
                        if (_viewportLineVisuals[start + i] != null) 
                        { 
                            _viewportLineVisuals[start + i].DiscardOnArrange = true;
                        } 
                    }

                    _viewportLineVisuals.RemoveRange(start, count);
                } 
            }
        } 
 
        // Callback for the background layout throttle timer.
        // Resumes backgound layout. 
        private void OnThrottleBackgroundTimeout(object sender, EventArgs e)
        {
            _throttleBackgroundTimer.Stop();
            _throttleBackgroundTimer = null; 

            if (this.IsBackgroundLayoutPending) 
            { 
                OnBackgroundMeasure(null);
            } 
        }

        // Returns the x-axis offset of content on a line, based on current
        // text alignment. 
        private double GetContentOffset(double lineWidth, TextAlignment aligment)
        { 
            double contentOffset; 
            double width = GetWrappingWidth(this.RenderSize.Width);
 
            switch (aligment)
            {
                case TextAlignment.Right:
                    contentOffset = width - lineWidth; 
                    break;
 
                case TextAlignment.Center: 
                    contentOffset = (width - lineWidth) / 2;
                    break; 

                default:
                    // Default is Left alignment, in this case offset is 0.
                    contentOffset = 0.0; 
                    break;
            } 
 
            return contentOffset;
        } 

        // Converts a HorizontalAlignment enum to a TextAlignment enum.
        private TextAlignment HorizontalAlignmentToTextAlignment(HorizontalAlignment horizontalAlignment)
        { 
            TextAlignment textAlignment;
 
            switch (horizontalAlignment) 
            {
                case HorizontalAlignment.Left: 
                default:
                    textAlignment = TextAlignment.Left;
                    break;
 
                case HorizontalAlignment.Right:
                    textAlignment = TextAlignment.Right; 
                    break; 

                case HorizontalAlignment.Center: 
                    textAlignment = TextAlignment.Center;
                    break;

                case HorizontalAlignment.Stretch: 
                    textAlignment = TextAlignment.Justify;
                    break; 
            } 

            return textAlignment; 
        }

        /// 
        ///  
        /// 
        private bool Contains(ITextPointer position) 
        { 
            Invariant.Assert(this.IsLayoutValid);
 
            return position.TextContainer == _host.TextContainer &&
                   _lineMetrics != null &&
                   _lineMetrics[_lineMetrics.Count - 1].EndOffset >= position.Offset;
        } 

        // Converts a render size width into a wrapping width for lines. 
        private double GetWrappingWidth(double width) 
        {
            if (width < _contentSize.Width) 
            {
                width = _contentSize.Width;
            }
 
            if (width > _previousConstraint.Width)
            { 
                width = _previousConstraint.Width; 
            }
 
            // Make sure that TextFormatter limitations are not exceeded.
            //
            TextDpi.EnsureValidLineWidth(ref width);
 
            return width;
        } 
 
        // When the content size exceeds the viewport size, TextLine will align
        // its content such that the "extra" is clipped in inappropriate ways. 
        //
        // TextAlignment.Center: line offset = -(contentWidth - viewportWidth) / 2
        // TextAlignment.Right:  line offset = -(contentWidth - viewportWidth)
        // 
        // This method returns a value that exactly cancels out the undesired
        // offset, which is used to adjust the content origin to local zero. 
        private double GetTextAlignmentCorrection(TextAlignment textAlignment, double width) 
        {
            double correction = 0; 

            if (textAlignment != TextAlignment.Left &&
                _contentSize.Width > width)
            { 
                correction = -GetContentOffset(_contentSize.Width, textAlignment);
            } 
 
            return correction;
        } 

        #endregion Private Methods

        //------------------------------------------------------ 
        //
        //  Private Properties 
        // 
        //-----------------------------------------------------
 
        #region Private Properties

        // True when measure and arrange are valid.
        private bool IsLayoutValid 
        {
            get 
            { 
                return this.IsMeasureValid && this.IsArrangeValid;
            } 
        }

        // Current visible region in document space.
        private Rect Viewport 
        {
            get 
            { 
                return _scrollData == null ? Rect.Empty :
                                             new Rect(_scrollData.HorizontalOffset, _scrollData.VerticalOffset, _scrollData.ViewportWidth, _scrollData.ViewportHeight); 
            }
        }

        // True when background layout has not completed. 
        private bool IsBackgroundLayoutPending
        { 
            get 
            {
                return CheckFlags(Flags.BackgroundLayoutPending); 
            }
        }

        // Offset in pixels of the first line due to VerticalContentAlignment. 
        private double VerticalAlignmentOffset
        { 
            get 
            {
                double offset; 

                switch (((Control)_host).VerticalContentAlignment)
                {
                    case VerticalAlignment.Top: 
                    case VerticalAlignment.Stretch:
                    default: 
                        offset = 0; 
                        break;
 
                    case VerticalAlignment.Center:
                        offset = this.VerticalPadding / 2;
                        break;
 
                    case VerticalAlignment.Bottom:
                        offset = this.VerticalPadding; 
                        break; 
                }
 
                return offset;
            }
        }
 
        // Calculated TextAlignment property value.
        // Takes into account collisions between TextAlignment and HorizontalContentAlignment properties. 
        // 
        // TextAlignment always wins unless it has no local value and HorizontalContentAlignment does.
        // 
        // In order of precedence:
        // 1. Local value on TextAlignment.
        // 2. Local value on HorizontalContentAlignment.
        // 3. Inherited/styled TextAlignment 
        // 4. Inherited/styled HorizontalContentAlignment
        // 5. Inherited/styled/default TextAlignment. 
        private TextAlignment CalculatedTextAlignment 
        {
            get 
            {
                Control host = (Control)_host;
                object o = null;
 
                BaseValueSource textAlignmentSource = DependencyPropertyHelper.GetValueSource(host, TextBox.TextAlignmentProperty).BaseValueSource;
                BaseValueSource horizontalAlignmentSource = DependencyPropertyHelper.GetValueSource(host, TextBox.HorizontalContentAlignmentProperty).BaseValueSource; 
 

                if (textAlignmentSource == BaseValueSource.Local) 
                {
                    return (TextAlignment)host.GetValue(TextBox.TextAlignmentProperty);
                }
 
                if (horizontalAlignmentSource == BaseValueSource.Local)
                { 
                    o = host.GetValue(TextBox.HorizontalContentAlignmentProperty); 
                    return HorizontalAlignmentToTextAlignment((HorizontalAlignment)o);
                } 

                // if textAlignment has no inherited/styled value then
                // we'll check if there is inherited/styled value for HorizontalContentAlignmentProperty and take that.
                if ((textAlignmentSource == BaseValueSource.Default) && 
                    (horizontalAlignmentSource != BaseValueSource.Default))
                { 
                    o = host.GetValue(TextBox.HorizontalContentAlignmentProperty); 
                    return HorizontalAlignmentToTextAlignment((HorizontalAlignment)o);
                } 

                // return iether inherited/styled/default TextAlignment
                return (TextAlignment)host.GetValue(TextBox.TextAlignmentProperty);
            } 
        }
 
        // The delta between the current viewport height and the content height. 
        // Returns zero when content height is greater than viewport height.
        private double VerticalPadding 
        {
            get
            {
                double padding; 
                Rect viewport = this.Viewport;
 
                if (viewport.IsEmpty) 
                {
                    padding = 0; 
                }
                else
                {
                    padding = Math.Max(0, viewport.Height - _contentSize.Height); 
                }
 
                return padding; 
            }
        } 

        #endregion Private Properties

        //----------------------------------------------------- 
        //
        //  Private Types 
        // 
        //-----------------------------------------------------
 
        #region Private Types

        // Booleans for the _flags field.
        [System.Flags] 
        private enum Flags
        { 
            // When true, TextContainer listeners are hooked up. 
            TextContainerListenersInitialized = 0x1,
 
            // When true, background layout is still running.
            BackgroundLayoutPending = 0x2,
        }
 
        // Caches state used across a measure/arrange calculation.
        // In addition to performance benefits, this ensures a consistent 
        // view of property values across measure/arrange. 
        private class TextCache
        { 
            internal TextCache(TextBoxView owner)
            {
                _lineProperties = owner.GetLineProperties();
                _textRunCache = new TextRunCache(); 
                Control hostControl = (Control)owner.Host;
                TextFormattingMode textFormattingMode = TextOptions.GetTextFormattingMode(hostControl); 
                _textFormatter = System.Windows.Media.TextFormatting.TextFormatter.FromCurrentDispatcher(textFormattingMode); 
            }
 
            internal LineProperties LineProperties
            {
                get { return _lineProperties; }
            } 

            internal TextRunCache TextRunCache 
            { 
                get { return _textRunCache; }
            } 

            // Cached TextFormatter for this thread.
            internal TextFormatter TextFormatter
            { 
                get
                { 
                    return _textFormatter; 
                }
            } 

            private readonly LineProperties _lineProperties;
            private readonly TextRunCache _textRunCache;
            private TextFormatter      _textFormatter; 
        }
 
        // Line metrics array entry. 
        private class LineRecord
        { 
            internal LineRecord(int offset, TextBoxLine line)
            {
                _offset = offset;
                _length = line.Length; 
                _contentLength = line.ContentLength;
                _width = line.Width; 
            } 

            internal int Offset 
            {
                get { return _offset; }
                set { _offset = value; }
            } 

            internal int Length { get { return _length; } } 
 
            internal int ContentLength { get { return _contentLength; } }
 
            internal double Width { get { return _width; } }

            internal int EndOffset { get { return _offset + _length; } }
 
            private int _offset;
            private readonly int _length; // 
            private readonly int _contentLength; 
            private readonly double _width;
        } 

        #endregion Private Types

        //------------------------------------------------------ 
        //
        //  Private Fields 
        // 
        //-----------------------------------------------------
 
        #region Private Fields

        // TextBox that owns this TextBoxView.
        private readonly ITextBoxViewHost _host; 

        // Bounding box of the content, up to the point reached by background layout. 
        private Size _contentSize; 

        // The most recent constraint passed to MeasureOverride. 
        // this.PreviousConstraint cannot be used because it can be affected
        // by Margin, Width/Min/MaxWidth propreties and ClipToBounds.
        private Size _previousConstraint;
 
        // Caches state used across a measure/arrange calculation.
        // In addition to performance benefits, this ensures a consistent 
        // view of property values across measure/arrange. 
        private TextCache _cache;
 
        // Height of any line, in pixels.
        private double _lineHeight;

        // Visuals tracked by GetVisualChild/VisualChilrenCount overrides. 
        private List _visualChildren;
 
        // Array of cached line metrics. 
        private List _lineMetrics;
 
        // Array of cached line Visuals for the current viewport.
        private List _viewportLineVisuals;

        // Index of first line in the _viewportLineVisuals array. 
        private int _viewportLineVisualsIndex;
 
        // IScrollInfo state/code. 
        private ScrollData _scrollData;
 
        // List of invalidated regions created by TextContainer changes.
        private DtrList _dirtyList;

        // Timer used to disable background layout during user interaction. 
        private DispatcherTimer _throttleBackgroundTimer;
 
        // Boolean flags, set with Flags enum. 
        private Flags _flags;
 
        // Updated event listeners.
        private EventHandler UpdatedEvent;

        // Max time slice to run FullMeasureTick. 
        private const uint _maxMeasureTimeMs = 200;
 
        // Number of seconds to disable background layout after receiving 
        // user input.
        private const int _throttleBackgroundSeconds = 2; 

        #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