TextBlock.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Controls / TextBlock.cs / 4 / TextBlock.cs

                             
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
// 
// File: TextBlock.cs
// 
// Description: TextBlock element displays text. It is meant for UI scenarios. 
//              Most text scenarios should use the FlowDocumentScrollViewer.
// 
// History:
//  04/25/2003 : [....] - created.
//
//--------------------------------------------------------------------------- 

using System; 
using System.IO; 
using System.Collections;
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection; 
using System.Globalization;
using System.Windows.Automation; 
using System.Windows.Threading; 
using System.Windows.Documents;
using System.Windows.Media; 
using System.Windows.Media.TextFormatting;
using System.Windows.Markup;

using MS.Utility; 
using MS.Internal;                  // Invariant.Assert
using MS.Internal.Automation;       // TextAdaptor 
using System.Windows.Automation.Peers; 
using MS.Internal.Text;
using MS.Internal.Documents; 
using MS.Internal.Controls;
using MS.Internal.PresentationFramework;

#pragma warning disable 1634, 1691  // suppressing PreSharp warnings 

namespace System.Windows.Controls 
{ 
    /// 
    /// TextBlockCache caches the properties and Line which can be 
    /// reused during Measure, Arrange and Render phase.
    /// 
    ///
    class TextBlockCache 
    {
        public LineProperties _lineProperties; 
        public TextRunCache _textRunCache; 
    }
 
    /// 
    /// TextBlock element displays text. It is meant for UI scenarios.
    /// Most text scenarios should use the FlowDocumentScrollViewer.
    ///  
    [ContentProperty("Inlines")]
    [Localizability(LocalizationCategory.Text)] 
    public class TextBlock : FrameworkElement, IContentHost, IAddChildInternal, IServiceProvider 
    {
        //------------------------------------------------------------------- 
        //
        //  IContentHost Members
        //
        //------------------------------------------------------------------- 

        #region IContentHost Members 
 
        /// 
        /// Hit tests to the correct ContentElement within the ContentHost 
        /// that the mouse is over.
        /// 
        /// Mouse coordinates relative to the ContentHost.
        IInputElement IContentHost.InputHitTest(Point point) 
        {
            return this.InputHitTestCore(point); 
        } 

        ///  
        /// Returns an ICollection of bounding rectangles for the given ContentElement
        /// 
        /// 
        /// Content element for which rectangles are required 
        /// 
        ///  
        /// Looks at the ContentElement e line by line and gets rectangle bounds for each line 
        /// 
        ReadOnlyCollection IContentHost.GetRectangles(ContentElement child) 
        {
            return this.GetRectanglesCore(child);
        }
 
        /// 
        /// Returns elements hosted by the content host as an enumerator class 
        ///  
        IEnumerator IContentHost.HostedElements
        { 
            get
            {
                return this.HostedElementsCore;
            } 
        }
 
        ///  
        /// Called when a UIElement-derived class which is hosted by a IContentHost changes its DesiredSize
        /// NOTE: This method already exists for this class and is not specially implemented for IContentHost. 
        /// If this method is called through IContentHost for this class it will fire an assert
        /// 
        /// 
        /// Child element whose DesiredSize has changed 
        /// 
        void IContentHost.OnChildDesiredSizeChanged(UIElement child) 
        { 
            this.OnChildDesiredSizeChangedCore(child);
        } 


        #endregion IContentHost Members
 
        //--------------------------------------------------------------------
        // 
        //  IContentHost Members 
        //
        //------------------------------------------------------------------- 

        #region IAddChild members

        /// 
        /// Called to Add the object as a Child.
        /// 
        /// 
        /// Object to add as a child
        /// 
        void IAddChild.AddChild(Object value)
        {
            if (value == null)
            { 
                throw new ArgumentNullException("value");
            } 
 
            EnsureComplexContent();
 
            if (!(_complexContent.TextContainer is TextContainer))
            {
                throw new ArgumentException(SR.Get(SRID.TextPanelIllegalParaTypeForIAddChild, "value", value.GetType()));
            } 

            // Get parent of the text container. Note that it can be not a "this" TextBlock - in case 
            // when a TextBlock is used as a storage for plain TextBox - as an owner of a text container. 
            Type parentType = _complexContent.TextContainer.Parent.GetType();
 
            Type valueType = value.GetType();

            // Do implicit conversion to allowed inline type - if possible
            if (!TextSchema.IsValidChildOfContainer(parentType, /*childType*/valueType)) 
            {
                if (value is UIElement) 
                { 
                    value = new InlineUIContainer((UIElement)value);
                } 
                else
                {
                    throw new ArgumentException(SR.Get(SRID.TextSchema_ChildTypeIsInvalid, parentType.Name, valueType.Name));
                } 
            }
 
            // Insert inline element into the text container 
            Invariant.Assert(value is Inline, "Schema validation helper must guarantee that invalid element is not passed here");
 
            TextContainer textContainer = (TextContainer)_complexContent.TextContainer;

            textContainer.BeginChange();
            try 
            {
                TextPointer endPosition = textContainer.End; 
                textContainer.InsertElementInternal(endPosition, endPosition, (Inline)value); 
            }
            finally 
            {
                textContainer.EndChange();
            }
        } 

        /// 
        /// Called when text appears under the tag in markup. 
        ///
        /// 
        /// Text to Add to the Object
        ///
        void IAddChild.AddText(string text)
        { 
            if (text == null)
            { 
                throw new ArgumentNullException("text"); 
            }
 
            if (_complexContent == null)
            {
                Text = Text + text;
            } 
            else
            { 
                TextContainer textContainer = (TextContainer)_complexContent.TextContainer; 

                textContainer.BeginChange(); 
                try
                {
                    TextPointer endPosition = textContainer.End;
 
                    Run implicitRun = Inline.CreateImplicitRun(this);
 
                    textContainer.InsertElementInternal(endPosition, endPosition, implicitRun); 

                    implicitRun.Text = text; 
                }
                finally
                {
                    textContainer.EndChange(); 
                }
            } 
        } 

        #endregion IAddChild members 

        //--------------------------------------------------------------------
        //
        //  LogicalTree 
        //
        //-------------------------------------------------------------------- 
 
        #region LogicalTree
 
        /// 
        /// Returns enumerator to logical children.
        /// 
        protected internal override IEnumerator LogicalChildren 
        {
            get 
            { 
                if (IsContentPresenterContainer)
                { 
                    // We are hosting content that belongs to a ContentPresenter
                    return EmptyEnumerator.Instance;
                }
                else if (_complexContent == null) 
                {
                    return new SimpleContentEnumerator(Text); 
                } 
                else
                { 
                    if (!_complexContent.ForeignTextContainer)
                    {
                        return new RangeContentEnumerator(this.ContentStart, this.ContentEnd);
                    } 
                    else
                    { 
                        return EmptyEnumerator.Instance; // When we do not own the text tree we may not report its children as ours. 
                    }
                } 
            }
        }

        #endregion LogicalTree 

        //------------------------------------------------------------------- 
        // 
        //  IServiceProvider Members
        // 
        //--------------------------------------------------------------------

        #region IServiceProvider Members
 
        /// 
        /// Gets the service object of the specified type. 
        ///  
        /// 
        /// TextBlock control currently supports only TextView and TextContainer. 
        /// 
        /// 
        /// An object that specifies the type of service object to get.
        ///  
        /// 
        /// A service object of type serviceType. A null reference if there is no 
        /// service object of type serviceType. 
        /// 
        object IServiceProvider.GetService(Type serviceType) 
        {
            if (serviceType == null)
            {
                throw new ArgumentNullException("serviceType"); 
            }
            //             VerifyAccess(); 
 
            if (serviceType == typeof(ITextView))
            { 
                EnsureComplexContent();
                return _complexContent.TextView;
            }
            else if (serviceType == typeof(ITextContainer)) 
            {
                EnsureComplexContent(); 
                return _complexContent.TextContainer; 
            }
            else if (serviceType == typeof(TextContainer)) 
            {
                EnsureComplexContent();
                return _complexContent.TextContainer as TextContainer;
            } 
            return null;
        } 
 
        #endregion IServiceProvider Members
 
        //-------------------------------------------------------------------
        //
        //  Constructors
        // 
        //-------------------------------------------------------------------
 
        #region Constructors 

        ///  
        /// TextBlock static constructor. Registers metadata for its properties.
        /// 
        static TextBlock()
        { 
            // Required here, just for this type - Don't want anyone else to pick up this GetValueOverride
            BaselineOffsetProperty.OverrideMetadata( 
                    typeof(TextBlock), 
                    new FrameworkPropertyMetadata(
                            null, 
                            new CoerceValueCallback(CoerceBaselineOffset)));

            // Registering typography properties metadata
            PropertyChangedCallback onTypographyChanged = new PropertyChangedCallback(OnTypographyChanged); 
            DependencyProperty[] typographyProperties = Typography.TypographyPropertiesList;
            for (int i = 0; i < typographyProperties.Length; i++) 
            { 
                typographyProperties[i].OverrideMetadata(typeof(TextBlock), new FrameworkPropertyMetadata(onTypographyChanged));
            } 

            EventManager.RegisterClassHandler(typeof(TextBlock), RequestBringIntoViewEvent, new RequestBringIntoViewEventHandler(OnRequestBringIntoView));
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBlock), new FrameworkPropertyMetadata(typeof(TextBlock)));
        } 

        ///  
        /// Initializes a new instance of TextBlock class. 
        /// 
        public TextBlock() : base() 
        {
            Initialize();
        }
 
        /// 
        /// Initializes a new inslace of TextBlock class and adds a first Inline to its Inline collection. 
        ///  
        public TextBlock(Inline inline)
            : base() 
        {
            Initialize();
            if (inline == null)
            { 
                throw new ArgumentNullException("inline");
            } 
 
            this.Inlines.Add(inline);
        } 

        private void Initialize()
        {
        } 

        #endregion Constructors 
 
        //-------------------------------------------------------------------
        // 
        //  Public Methods
        //
        //--------------------------------------------------------------------
 
        #region Public Methods
 
        ///  
        /// Returns the TextPointer located closest to a supplied Point.
        ///  
        /// 
        /// Point to query, in the coordinate space of the Text.
        /// 
        ///  
        /// If true, this method will always return a TextPointer --
        /// the closest position as calculated by the control's heuristics. 
        /// If false, this method will return a null position if the test 
        /// point does not fall within any character bounding box.
        ///  
        /// 
        /// The closest TextPointer to the supplied Point, or null if
        /// snapToText is false and the supplied Point is not contained
        /// within any character bounding box. 
        /// 
        ///  
        /// Throws InvalidOperationException if layout is dirty. 
        /// 
        ///  
        /// The TextPointer returned always has its IsFrozen property set true
        /// and LogicalDirection property set towards a character hit by the point,
        /// or closest to this point.
        ///  
        public TextPointer GetPositionFromPoint(Point point, bool snapToText)
        { 
            TextPointer position; 

            if(CheckFlags(Flags.ContentChangeInProgress)) 
            {
                throw new InvalidOperationException(SR.Get(SRID.TextContainerChangingReentrancyInvalid));
            }
 
            // Validate layout information on TextView.
            if (((ITextView)_complexContent.TextView).Validate(point)) 
            { 
                position = (TextPointer)_complexContent.TextView.GetTextPositionFromPoint(point, snapToText);
            } 
            else
            {
                position = snapToText ? new TextPointer((TextPointer)_complexContent.TextContainer.Start) : null;
            } 

            // 
 
            return position;
        } 

        #endregion Public Methods

        //------------------------------------------------------------------- 
        //
        //  Public Properties 
        // 
        //--------------------------------------------------------------------
 
        #region Public Properties

        /// 
        /// Collection of Inline items contained in this TextBlock. 
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        public InlineCollection Inlines 
        {
            get 
            {
                return new InlineCollection(this, /*isOwnerParent*/true);
            }
        } 

        ///  
        /// TextPointer preceding all content. 
        /// 
        ///  
        /// The TextPointer returned always has its IsFrozen property set true
        /// and LogicalDirection property set to LogicalDirection.Backward.
        /// 
        public TextPointer ContentStart 
        {
            get 
            { 
                EnsureComplexContent();
 
                return (TextPointer)_complexContent.TextContainer.Start;
            }
        }
 
        /// 
        /// TextPointer following all content. 
        ///  
        /// 
        /// The TextPointer returned always has its IsFrozen property set true 
        /// and LogicalDirection property set to LogicalDirection.Forward.
        /// 
        public TextPointer ContentEnd
        { 
            get
            { 
                EnsureComplexContent(); 

                return (TextPointer)_complexContent.TextContainer.End; 
            }
        }

        ///  
        /// A TextRange spanning the content of this element.
        ///  
        internal TextRange TextRange 
        {
            get 
            {
                // NOTE: We are creating a new instance of a TextRange on each request.
                // We cannot cache the instance, because it may become incorrect
                // after collapsing TextBlock's content and insertion a new one: 
                // the cached range would remain empty, which is incorrect.
                return new TextRange(this.ContentStart, this.ContentEnd); 
            } 
        }
 
        /// 
        /// Breaking condition before the Element.
        /// 
        public LineBreakCondition BreakBefore { get { return LineBreakCondition.BreakDesired; } } 

        ///  
        /// Breaking condition after the Element. 
        /// 
        public LineBreakCondition BreakAfter { get { return LineBreakCondition.BreakDesired; } } 

        /// 
        /// Access to all text typography properties.
        ///  
        public Typography Typography
        { 
            get 
            {
                return new Typography(this); 
            }
        }

        #endregion Public Properties 

        //-------------------------------------------------------------------- 
        // 
        //  Public Dynamic Properties
        // 
        //-------------------------------------------------------------------

        #region Public Dynamic Properties
 
        /// 
        /// DependencyProperty for  property. 
        ///  
        public static readonly DependencyProperty BaselineOffsetProperty =
                DependencyProperty.RegisterAttached( 
                        "BaselineOffset",
                        typeof(double),
                        typeof(TextBlock),
                        new FrameworkPropertyMetadata( 
                                double.NaN,
                                new PropertyChangedCallback(OnBaselineOffsetChanged))); 
 
        /// 
        /// The BaselineOffset property provides an adjustment to baseline offset 
        /// 
        public double BaselineOffset
        {
            get { return (double)GetValue(BaselineOffsetProperty); } 
            set { SetValue(BaselineOffsetProperty, value); }
        } 
 
        /// 
        /// Writes the attached property BaselineOffset to the given element. 
        /// 
        /// The element to which to write the attached property.
        /// The property value to set
        public static void SetBaselineOffset(DependencyObject element, double value) 
        {
            if (element == null) 
            { 
                throw new ArgumentNullException("element");
            } 

            element.SetValue(BaselineOffsetProperty, value);
        }
 
        /// 
        /// Reads the attached property from the given element 
        ///  
        /// The element to which to read the attached property.
        public static double GetBaselineOffset(DependencyObject element) 
        {
            if (element == null)
            {
                throw new ArgumentNullException("element"); 
            }
 
            return (double)element.GetValue(BaselineOffsetProperty); 
        }
 
        /// 
        /// DependencyProperty for  property.
        /// 
        [CommonDependencyProperty] 
        public static readonly DependencyProperty TextProperty =
                DependencyProperty.Register( 
                        "Text", 
                        typeof(string),
                        typeof(TextBlock), 
                        new FrameworkPropertyMetadata(
                                string.Empty,
                                FrameworkPropertyMetadataOptions.AffectsMeasure |
                                FrameworkPropertyMetadataOptions.AffectsRender, 
                                new PropertyChangedCallback(OnTextChanged),
                                new CoerceValueCallback(CoerceText))); 
 
        /// 
        /// The Text property defines the content (text) to be displayed. 
        /// 
        [Localizability(LocalizationCategory.Text)]
        public string Text
        { 
            get { return (string) GetValue(TextProperty); }
            set { SetValue(TextProperty, value); } 
        } 

        ///  
        /// Coersion callback for the Text property.
        /// 
        /// 
        /// We cannot assume value is a string here -- it may be a DeferredTextReference. 
        /// 
        private static object CoerceText(DependencyObject d, object value) 
        { 
            TextBlock textblock = (TextBlock)d;
 
            if (value == null)
            {
                value = String.Empty;
            } 

            if (textblock._complexContent != null && 
                !textblock.CheckFlags(Flags.TextContentChanging) && 
                (string)value == (string)textblock.GetValue(TextProperty))
            { 
                // If the new value equals the old value, then the property
                // system will optimize out the call to OnTextChanged.  We can't
                // skip this call because there's ambiguity between the TextProperty
                // view of content and actual content -- we might have a new 
                // value even if strings match.
                // 
                // E.g.: content = , TextProperty == " " 
                // Now setting TextProperty = " " really changes content, replacing
                // the Image with a space char. 
                OnTextChanged(d, (string)value);
            }

            return value; 
        }
 
        ///  
        /// DependencyProperty for  property.
        ///  
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontFamilyProperty =
                TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));
 
        /// 
        /// The FontFamily property specifies the name of font family. 
        ///  
        [Localizability(LocalizationCategory.Font)]
        public FontFamily FontFamily 
        {
            get { return (FontFamily) GetValue(FontFamilyProperty); }
            set { SetValue(FontFamilyProperty, value); }
        } 

        ///  
        /// DependencyProperty setter for  property. 
        /// 
        /// The element to which to write the attached property. 
        /// The property value to set
        public static void SetFontFamily(DependencyObject element, FontFamily value)
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            } 

            element.SetValue(FontFamilyProperty, value); 
        }

        /// 
        /// DependencyProperty getter for  property. 
        /// 
        /// The element from which to read the attached property. 
        public static FontFamily GetFontFamily(DependencyObject element) 
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element");
            }
 
            return (FontFamily)element.GetValue(FontFamilyProperty);
        } 
 
        /// 
        /// DependencyProperty for  property. 
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontStyleProperty =
                TextElement.FontStyleProperty.AddOwner(typeof(TextBlock)); 

        ///  
        /// The FontStyle property requests normal, italic, and oblique faces within a font family. 
        /// 
        public FontStyle FontStyle 
        {
            get { return (FontStyle) GetValue(FontStyleProperty); }
            set { SetValue(FontStyleProperty, value); }
        } 

        ///  
        /// DependencyProperty setter for  property. 
        /// 
        /// The element to which to write the attached property. 
        /// The property value to set
        public static void SetFontStyle(DependencyObject element, FontStyle value)
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            } 

            element.SetValue(FontStyleProperty, value); 
        }

        /// 
        /// DependencyProperty getter for  property. 
        /// 
        /// The element from which to read the attached property. 
        public static FontStyle GetFontStyle(DependencyObject element) 
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element");
            }
 
            return (FontStyle)element.GetValue(FontStyleProperty);
        } 
 
        /// 
        /// DependencyProperty for  property. 
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontWeightProperty =
                TextElement.FontWeightProperty.AddOwner(typeof(TextBlock)); 

        ///  
        /// The FontWeight property specifies the weight of the font. 
        /// 
        public FontWeight FontWeight 
        {
            get { return (FontWeight) GetValue(FontWeightProperty); }
            set { SetValue(FontWeightProperty, value); }
        } 

        ///  
        /// DependencyProperty setter for  property. 
        /// 
        /// The element to which to write the attached property. 
        /// The property value to set
        public static void SetFontWeight(DependencyObject element, FontWeight value)
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            } 

            element.SetValue(FontWeightProperty, value); 
        }

        /// 
        /// DependencyProperty getter for  property. 
        /// 
        /// The element from which to read the attached property. 
        public static FontWeight GetFontWeight(DependencyObject element) 
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element");
            }
 
            return (FontWeight)element.GetValue(FontWeightProperty);
        } 
 
        /// 
        /// DependencyProperty for  property. 
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontStretchProperty =
                TextElement.FontStretchProperty.AddOwner(typeof(TextBlock)); 

        ///  
        /// The FontStretch property selects a normal, condensed, or extended face from a font family. 
        /// 
        public FontStretch FontStretch 
        {
            get { return (FontStretch) GetValue(FontStretchProperty); }
            set { SetValue(FontStretchProperty, value); }
        } 

        ///  
        /// DependencyProperty setter for  property. 
        /// 
        /// The element to which to write the attached property. 
        /// The property value to set
        public static void SetFontStretch(DependencyObject element, FontStretch value)
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            } 

            element.SetValue(FontStretchProperty, value); 
        }

        /// 
        /// DependencyProperty getter for  property. 
        /// 
        /// The element from which to read the attached property. 
        public static FontStretch GetFontStretch(DependencyObject element) 
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element");
            }
 
            return (FontStretch)element.GetValue(FontStretchProperty);
        } 
 
        /// 
        /// DependencyProperty for  property. 
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontSizeProperty =
                TextElement.FontSizeProperty.AddOwner( 
                        typeof(TextBlock));
 
        ///  
        /// The FontSize property specifies the size of the font.
        ///  
        [TypeConverter(typeof(FontSizeConverter))]
        [Localizability(LocalizationCategory.None)]
        public double FontSize
        { 
            get { return (double) GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); } 
        } 

        ///  
        /// DependencyProperty setter for  property.
        /// 
        /// The element to which to write the attached property.
        /// The property value to set 
        public static void SetFontSize(DependencyObject element, double value)
        { 
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            }

            element.SetValue(FontSizeProperty, value);
        } 

        ///  
        /// DependencyProperty getter for  property. 
        /// 
        /// The element from which to read the attached property. 
        [TypeConverter(typeof(FontSizeConverter))]
        public static double GetFontSize(DependencyObject element)
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element"); 
            } 

            return (double)element.GetValue(FontSizeProperty); 
        }

        /// 
        /// DependencyProperty for  property. 
        /// 
        [CommonDependencyProperty] 
        public static readonly DependencyProperty ForegroundProperty = 
                TextElement.ForegroundProperty.AddOwner(
                        typeof(TextBlock)); 

        /// 
        /// The Foreground property specifies the foreground brush of an element's text content.
        ///  
        public Brush Foreground
        { 
            get { return (Brush) GetValue(ForegroundProperty); } 
            set { SetValue(ForegroundProperty, value); }
        } 

        /// 
        /// DependencyProperty setter for  property.
        ///  
        /// The element to which to write the attached property.
        /// The property value to set 
        public static void SetForeground(DependencyObject element, Brush value) 
        {
            if (element == null) 
            {
                throw new ArgumentNullException("element");
            }
 
            element.SetValue(ForegroundProperty, value);
        } 
 
        /// 
        /// DependencyProperty getter for  property. 
        /// 
        /// The element from which to read the attached property.
        public static Brush GetForeground(DependencyObject element)
        { 
            if (element == null)
            { 
                throw new ArgumentNullException("element"); 
            }
 
            return (Brush)element.GetValue(ForegroundProperty);
        }

        ///  
        /// DependencyProperty for  property.
        ///  
        [CommonDependencyProperty] 
        public static readonly DependencyProperty BackgroundProperty =
                TextElement.BackgroundProperty.AddOwner( 
                        typeof(TextBlock),
                        new FrameworkPropertyMetadata(
                                null,
                                FrameworkPropertyMetadataOptions.AffectsRender)); 

        ///  
        /// The Background property defines the brush used to fill the content area. 
        /// 
        public Brush Background 
        {
            get { return (Brush) GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        } 

        ///  
        /// DependencyProperty for  property. 
        /// 
        [CommonDependencyProperty] 
        public static readonly DependencyProperty TextDecorationsProperty =
                Inline.TextDecorationsProperty.AddOwner(
                        typeof(TextBlock),
                        new FrameworkPropertyMetadata( 
                                new FreezableDefaultValueFactory(TextDecorationCollection.Empty),
                                FrameworkPropertyMetadataOptions.AffectsRender 
                                )); 

        ///  
        /// The TextDecorations property specifies decorations that are added to the text of an element.
        /// 
        public TextDecorationCollection TextDecorations
        { 
            get { return (TextDecorationCollection) GetValue(TextDecorationsProperty); }
            set { SetValue(TextDecorationsProperty, value); } 
        } 

        ///  
        /// DependencyProperty for  property.
        /// 
        public static readonly DependencyProperty TextEffectsProperty =
                TextElement.TextEffectsProperty.AddOwner( 
                        typeof(TextBlock),
                        new FrameworkPropertyMetadata( 
                                new FreezableDefaultValueFactory(TextEffectCollection.Empty), 
                                FrameworkPropertyMetadataOptions.AffectsRender));
 
        /// 
        /// The TextEffects property specifies effects that are added to the text of an element.
        /// 
        public TextEffectCollection TextEffects 
        {
            get { return (TextEffectCollection) GetValue(TextEffectsProperty); } 
            set { SetValue(TextEffectsProperty, value); } 
        }
 
        /// 
        /// DependencyProperty for  property.
        /// 
        public static readonly DependencyProperty LineHeightProperty = 
                Block.LineHeightProperty.AddOwner(typeof(TextBlock));
 
        ///  
        /// The LineHeight property specifies the height of each generated line box.
        ///  
        [TypeConverter(typeof(LengthConverter))]
        public double LineHeight
        {
            get { return (double)GetValue(LineHeightProperty); } 
            set { SetValue(LineHeightProperty, value); }
        } 
 
        /// 
        /// DependencyProperty setter for  property. 
        /// 
        /// The element to which to write the attached property.
        /// The property value to set
        public static void SetLineHeight(DependencyObject element, double value) 
        {
            if (element == null) 
            { 
                throw new ArgumentNullException("element");
            } 

            element.SetValue(LineHeightProperty, value);
        }
 
        /// 
        /// DependencyProperty getter for  property. 
        ///  
        /// The element from which to read the attached property.
        [TypeConverter(typeof(LengthConverter))] 
        public static double GetLineHeight(DependencyObject element)
        {
            if (element == null)
            { 
                throw new ArgumentNullException("element");
            } 
 
            return (double)element.GetValue(LineHeightProperty);
        } 

        /// 
        /// DependencyProperty for  property.
        ///  
        public static readonly DependencyProperty LineStackingStrategyProperty =
                Block.LineStackingStrategyProperty.AddOwner(typeof(TextBlock)); 
 
        /// 
        /// The LineStackingStrategy property specifies how lines are placed 
        /// 
        public LineStackingStrategy LineStackingStrategy
        {
            get { return (LineStackingStrategy)GetValue(LineStackingStrategyProperty); } 
            set { SetValue(LineStackingStrategyProperty, value); }
        } 
 
        /// 
        /// DependencyProperty setter for  property. 
        /// 
        /// The element to which to write the attached property.
        /// The property value to set
        public static void SetLineStackingStrategy(DependencyObject element, LineStackingStrategy value) 
        {
            if (element == null) 
            { 
                throw new ArgumentNullException("element");
            } 

            element.SetValue(LineStackingStrategyProperty, value);
        }
 
        /// 
        /// DependencyProperty getter for  property. 
        ///  
        /// The element from which to read the attached property.
        public static LineStackingStrategy GetLineStackingStrategy(DependencyObject element) 
        {
            if (element == null)
            {
                throw new ArgumentNullException("element"); 
            }
 
            return (LineStackingStrategy)element.GetValue(LineStackingStrategyProperty); 
        }
 
        /// 
        /// DependencyProperty for  property.
        /// 
        public static readonly DependencyProperty PaddingProperty = 
                Block.PaddingProperty.AddOwner(
                        typeof(TextBlock), 
                        new FrameworkPropertyMetadata( 
                                new Thickness(),
                                FrameworkPropertyMetadataOptions.AffectsMeasure)); 

        /// 
        /// The Padding property specifies the padding of the element.
        ///  
        public Thickness Padding
        { 
            get { return (Thickness)GetValue(PaddingProperty); } 
            set { SetValue(PaddingProperty, value); }
        } 

        /// 
        /// DependencyProperty for  property.
        ///  
        public static readonly DependencyProperty TextAlignmentProperty =
                Block.TextAlignmentProperty.AddOwner(typeof(TextBlock)); 
 
        /// 
        /// The TextAlignment property specifies horizontal alignment of the content. 
        /// 
        public TextAlignment TextAlignment
        {
            get { return (TextAlignment)GetValue(TextAlignmentProperty); } 
            set { SetValue(TextAlignmentProperty, value); }
        } 
 
        /// 
        /// DependencyProperty setter for  property. 
        /// 
        /// The element to which to write the attached property.
        /// The property value to set
        public static void SetTextAlignment(DependencyObject element, TextAlignment value) 
        {
            if (element == null) 
            { 
                throw new ArgumentNullException("element");
            } 

            element.SetValue(TextAlignmentProperty, value);
        }
 
        /// 
        /// DependencyProperty getter for  property. 
        ///  
        /// The element from which to read the attached property.
        public static TextAlignment GetTextAlignment(DependencyObject element) 
        {
            if (element == null)
            {
                throw new ArgumentNullException("element"); 
            }
 
            return (TextAlignment)element.GetValue(TextAlignmentProperty); 
        }
 
        /// 
        /// DependencyProperty for  property.
        /// 
        [CommonDependencyProperty] 
        public static readonly DependencyProperty TextTrimmingProperty =
                DependencyProperty.Register( 
                        "TextTrimming", 
                        typeof(TextTrimming),
                        typeof(TextBlock), 
                        new FrameworkPropertyMetadata(
                                TextTrimming.None,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender),
                        new ValidateValueCallback(IsValidTextTrimming)); 

        ///  
        /// The TextTrimming property specifies the trimming behavior situation 
        /// in case of clipping some textual content caused by overflowing the line's box.
        ///  
        public TextTrimming TextTrimming
        {
            get { return (TextTrimming)GetValue(TextTrimmingProperty); }
            set { SetValue(TextTrimmingProperty, value); } 
        }
 
        ///  
        /// DependencyProperty for  property.
        ///  
        [CommonDependencyProperty]
        public static readonly DependencyProperty TextWrappingProperty =
                DependencyProperty.Register(
                        "TextWrapping", 
                        typeof(TextWrapping),
                        typeof(TextBlock), 
                        new FrameworkPropertyMetadata( 
                                TextWrapping.NoWrap,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender), 
                        new ValidateValueCallback(IsValidTextWrap));

        /// 
        /// The TextWrapping property controls whether or not text wraps 
        /// when it reaches the flow edge of its containing block box.
        ///  
        public TextWrapping TextWrapping 
        {
            get { return (TextWrapping)GetValue(TextWrappingProperty); } 
            set { SetValue(TextWrappingProperty, value); }
        }

        ///  
        /// DependencyProperty for hyphenation property.
        ///  
        public static readonly DependencyProperty IsHyphenationEnabledProperty = 
                Block.IsHyphenationEnabledProperty.AddOwner(typeof(TextBlock));
 
        /// 
        /// CLR property for hyphenation
        /// 
        public bool IsHyphenationEnabled 
        {
            get { return (bool)GetValue(IsHyphenationEnabledProperty); } 
            set { SetValue(IsHyphenationEnabledProperty, value); } 
        }
 
        #endregion Public Dynamic Properties

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

        #region Protected Methods 

        /// 
        ///  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 _complexContent == null ? 0 : _complexContent.VisualChildren.Count; }
        } 
 
        /// 
        ///   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 (_complexContent == null)
            {
                throw new ArgumentOutOfRangeException("index");
            } 
            return _complexContent.VisualChildren[index];
        } 
 
        /// 
        /// Content measurement. 
        /// 
        /// Constraint size.
        /// Computed desired size.
        protected sealed override Size MeasureOverride(Size constraint) 
        {
            VerifyReentrancy(); 
 
#if TEXTPANELLAYOUTDEBUG
            MS.Internal.PtsHost.TextPanelDebug.BeginScope("TextBlock.MeasureOverride", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange); 
            MS.Internal.PtsHost.TextPanelDebug.StartTimer("TextBlock.MeasureOverride", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange);
#endif

            // Clear and repopulate our text block cache. (Handles multiple measure before arrange) 
            _textBlockCache = null;
 
            EnsureTextBlockCache(); 
            LineProperties lineProperties = _textBlockCache._lineProperties;
 
            // Hook up our TextContainer event listeners if we haven't yet.
            if (CheckFlags(Flags.PendingTextContainerEventInit))
            {
                Invariant.Assert(_complexContent != null); 
                InitializeTextContainerListeners();
                SetFlags(false, Flags.PendingTextContainerEventInit); 
            } 

            // Find out if we can skip measure process. Measure cannot be skipped in following situations: 
            // a) content is dirty (properties or content)
            // b) there are inline objects (they may be dynamically sized)
            int lineCount = LineCount;
            if ((lineCount > 0) && IsMeasureValid && InlineObjects == null) 
            {
                // Assuming that all of above conditions are true, Measure can be 
                // skipped in following situations: 
                // 1) TextTrimming == None and:
                //      a) Width is the same, or 
                //      b) TextWrapping == NoWrap
                // 2) Width is the same and TextWrapping == NoWrap
                bool bypassMeasure;
                if (lineProperties.TextTrimming == TextTrimming.None) 
                {
                    bypassMeasure = DoubleUtil.AreClose(constraint.Width, _referenceSize.Width) || (lineProperties.TextWrapping == TextWrapping.NoWrap); 
                } 
                else
                { 
                    bypassMeasure =
                        DoubleUtil.AreClose(constraint.Width, _referenceSize.Width) &&
                        (lineProperties.TextWrapping == TextWrapping.NoWrap) &&
                        (DoubleUtil.AreClose(constraint.Height, _referenceSize.Height) || lineCount == 1); 
                }
                if (bypassMeasure) 
                { 
                    _referenceSize = constraint;
#if TEXTPANELLAYOUTDEBUG 
                    MS.Internal.PtsHost.TextPanelDebug.Log("MeasureOverride bypassed.", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange);
                    MS.Internal.PtsHost.TextPanelDebug.StopTimer("TextBlock.MeasureOverride", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange);
                    MS.Internal.PtsHost.TextPanelDebug.EndScope(MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange);
#endif 
                    return _previousDesiredSize;
                } 
            } 

            // Store constraint size, it is used when measuring inline objects. 
            _referenceSize = constraint;

            // Store previous ITextEmbeddable values
            bool formattedOnce = CheckFlags(Flags.FormattedOnce); 
            double baselineOffsetPrevious = _baselineOffset;
 
            // Reset inline objects cache and line metrics cache. 
            // They will be fully updated during lines formatting.
            InlineObjects = null; 

            // before erasing the line metrics, keep track of how big it was last time
            // so that we can initialize the metrics array to that size this time
            int subsequentLinesInitialSize = (_subsequentLines == null) ? 1 : _subsequentLines.Count; 

            ClearLineMetrics(); 
 
            if (_complexContent != null)
            { 
                _complexContent.TextView.Invalidate();
            }

            // To determine natural size of the text TextAlignment has to be ignored. 
            // Since for rendering/hittesting lines are recreated, it can be done without
            // any problems. 
            lineProperties.IgnoreTextAlignment = true; 
            SetFlags(true, Flags.RequiresAlignment); // Need to update LineMetrics.Start when FinalSize is known.
            SetFlags(true, Flags.FormattedOnce); 
            SetFlags(false, Flags.HasParagraphEllipses);
            SetFlags(true, Flags.MeasureInProgress | Flags.TreeInReadOnlyMode);
            Size desiredSize = new Size();
            bool exceptionThrown = true; 
            try
            { 
                // Create and format lines until end of paragraph is reached. 
                // Since we are disposing line object, it can be reused to format following lines.
                Line line = CreateLine(lineProperties); 
                bool endOfParagraph = false;
                int dcp = 0;
                TextLineBreak textLineBreakIn = null;
 
                Thickness padding = this.Padding;
                Size contentSize = new Size(Math.Max(0.0, constraint.Width - (padding.Left + padding.Right)), 
                                            Math.Max(0.0, constraint.Height - (padding.Top + padding.Bottom))); 
                // Make sure that TextFormatter limitations are not exceeded.
                // 
                TextDpi.EnsureValidLineWidth(ref contentSize);

                while (!endOfParagraph)
                { 
                    using(line)
                    { 
                        // Format line. Set showParagraphEllipsis flag to false because we do not know whether or not the line will have 
                        // paragraph ellipsis at this time. Since TextBlock is auto-sized we do not know the RenderSize until we finish Measure
                        line.Format(dcp, contentSize.Width, GetLineProperties(dcp == 0, lineProperties), textLineBreakIn, _textBlockCache._textRunCache, /*Show paragraph ellipsis*/ false); 

                        double lineHeight = CalcLineAdvance(line.Height, lineProperties);

    #if DEBUG 
                        LineMetrics metrics = new LineMetrics(contentSize.Width, line.Length, line.Width, lineHeight, line.BaselineOffset, line.HasInlineObjects(), textLineBreakIn);
    #else 
                        LineMetrics metrics = new LineMetrics(line.Length, line.Width, lineHeight, line.BaselineOffset, line.HasInlineObjects(), textLineBreakIn); 
    #endif
 
                        if (!CheckFlags(Flags.HasFirstLine))
                        {
                            SetFlags(true, Flags.HasFirstLine);
                            _firstLine = metrics; 
                        }
                        else 
                        { 
                            if (_subsequentLines == null)
                            { 
                                _subsequentLines = new List(subsequentLinesInitialSize);
                            }
                            _subsequentLines.Add(metrics);
                        } 

 
                        // Desired width is always max of calculated line widths. 
                        // Desired height is sum of all line heights. But if TextTrimming is on
                        // do not overflow the requested height with the exception for the first line. 
                        desiredSize.Width = Math.Max(desiredSize.Width, line.GetCollapsedWidth());
                        if ((lineProperties.TextTrimming == TextTrimming.None) ||
                            (contentSize.Height >= (desiredSize.Height + lineHeight)) ||
                            (dcp == 0)) 
                        {
                            // BaselineOffset is always distance from the Text's top 
                            // to the baseline offset of the last line. 
                            _baselineOffset = desiredSize.Height + line.BaselineOffset;
 
                            desiredSize.Height += lineHeight;
                        }
                        else
                        { 
                            // Note the fact that there are paragraph ellipses
                            SetFlags(true, Flags.HasParagraphEllipses); 
                        } 

                        textLineBreakIn = line.GetTextLineBreak(); 

                        endOfParagraph = line.EndOfParagraph;
                        dcp += line.Length;
                    } 
                }
 
                desiredSize.Width += (padding.Left + padding.Right); 
                desiredSize.Height += (padding.Top + padding.Bottom);
 
                Invariant.Assert(textLineBreakIn == null); // End of paragraph should have no line break record

                exceptionThrown = false;
            } 
            finally
            { 
                // Restore original line properties 
                lineProperties.IgnoreTextAlignment = false;
                SetFlags(false, Flags.MeasureInProgress | Flags.TreeInReadOnlyMode); 

                if(exceptionThrown)
                {
                    _textBlockCache._textRunCache = null; 
                    ClearLineMetrics();
                } 
            } 

            // Notify ITextHost that ITextEmbeddable values have been changed, if necessary. 
            if (!DoubleUtil.AreClose(baselineOffsetPrevious, _baselineOffset))
            {
                CoerceValue(BaselineOffsetProperty);
            } 

#if TEXTPANELLAYOUTDEBUG 
            MS.Internal.PtsHost.TextPanelDebug.StopTimer("TextBlock.MeasureOverride", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange); 
            MS.Internal.PtsHost.TextPanelDebug.EndScope(MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange);
#endif 
            _previousDesiredSize = desiredSize;

            return desiredSize;
        } 

        ///  
        /// Content arrangement. 
        /// 
        /// Size that element should use to arrange itself and its children. 
        protected sealed override Size ArrangeOverride(Size arrangeSize)
        {
            VerifyReentrancy();
 
#if TEXTPANELLAYOUTDEBUG
            MS.Internal.PtsHost.TextPanelDebug.BeginScope("TextBlock.ArrangeOverride", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange); 
            MS.Internal.PtsHost.TextPanelDebug.StartTimer("TextBlock.ArrangeOverride", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange); 
#endif
            // Remove all existing visuals. If there are inline objects, they will be added below. 
            if (_complexContent != null)
            {
                _complexContent.VisualChildren.Clear();
            } 

            ArrayList inlineObjects = InlineObjects; 
            int lineCount = LineCount; 
            if (inlineObjects != null && lineCount > 0)
            { 
                bool exceptionThrown = true;

                SetFlags(true, Flags.TreeInReadOnlyMode);
                SetFlags(true, Flags.ArrangeInProgress); 

                try 
                { 
                    EnsureTextBlockCache();
                    LineProperties lineProperties = _textBlockCache._lineProperties; 

                    double wrappingWidth = CalcWrappingWidth(arrangeSize.Width);
                    Vector contentOffset = CalcContentOffset(arrangeSize, wrappingWidth);
 
                    // Position all inline objects. Recreate only lines that have inline objects
                    // and call arrange on it. Line.Arrange enumerates all inline objects and 
                    // sets appropriate transform on them. 
                    Line line = CreateLine(lineProperties);
                    int dcp = 0; 
                    Vector lineOffset = contentOffset;

                    for (int i = 0; i < lineCount; i++)
                    { 
Debug.Assert(lineCount == LineCount);
                        LineMetrics lineMetrics = GetLine(i); 
 
                        if (lineMetrics.HasInlineObjects)
                        { 
                            using (line)
                            {
                                // Check if paragraph ellipsis are added to this line
                                bool ellipsis = ParagraphEllipsisShownOnLine(i, lineOffset.Y - contentOffset.Y); 
                                line.Format(dcp, wrappingWidth, GetLineProperties(dcp == 0, lineProperties), lineMetrics.TextLineBreak, _textBlockCache._textRunCache, ellipsis);
 
                                // Check that lineMetrics length and line length are in sync 
                                //
 

                                // We shut off text alignment for measure, ensure we treat same here.
                                //
 

 
 

 



                                // Add inline objects to visual children of the TextBlock visual and 
                                // set appropriate transforms.
                                line.Arrange(_complexContent.VisualChildren, lineOffset); 
                            } 
                        }
 
                        lineOffset.Y += lineMetrics.Height;
                        dcp += lineMetrics.Length;
                    }
 
                    exceptionThrown = false;
                } 
                finally 
                {
                    SetFlags(false, Flags.TreeInReadOnlyMode); 
                    SetFlags(false, Flags.ArrangeInProgress);
                    if(exceptionThrown)
                    {
                       _textBlockCache._textRunCache = null; 
                       ClearLineMetrics();
                    } 
                } 
            }
 
            if (_complexContent != null)
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                    new DispatcherOperationCallback(OnValidateTextView), EventArgs.Empty); 
            }
 
#if TEXTPANELLAYOUTDEBUG 
            MS.Internal.PtsHost.TextPanelDebug.StopTimer("TextBlock.ArrangeOverride", MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange);
            MS.Internal.PtsHost.TextPanelDebug.EndScope(MS.Internal.PtsHost.TextPanelDebug.Category.MeasureArrange); 
#endif
            InvalidateVisual();
            return arrangeSize;
        } 

        ///  
        /// Render control's content. 
        /// 
        /// Drawing context. 
        protected sealed override void OnRender(DrawingContext ctx)
        {
            VerifyReentrancy();
 
            if (ctx == null)
            { 
                throw new ArgumentNullException("ctx"); 
            }
 
            // If layout data is not updated do not render the content.
            if (!IsLayoutDataValid) { return; }

            // Draw background in rectangle. 
            Brush background = this.Background;
            if (background != null) 
            { 
                ctx.DrawRectangle(background, null, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
            } 

            SetFlags(false, Flags.RequiresAlignment);
            SetFlags(true, Flags.TreeInReadOnlyMode);
            try 
            {
                // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing. 
                EnsureTextBlockCache(); 
                LineProperties lineProperties = _textBlockCache._lineProperties;
 

                double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
                Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);
                Point lineOffset = new Point(contentOffset.X, contentOffset.Y); 

                // NOTE: All inline objects are UIElements and all of them are direct children of 
                // the TextBlock. Hence visuals for those inline objects are already attached. 
                // The only responsibility of OnRender is to render text, since transforms for inline
                // objects are set during OnArrange. 

                // Create / format / render all lines.
                // Since we are disposing line object, it can be reused to format following lines.
                Line line = CreateLine(lineProperties); 
                int dcp = 0;
                bool showParagraphEllipsis = false; 
                SetFlags(CheckFlags(Flags.HasParagraphEllipses), Flags.RequiresAlignment); 

 
                int lineCount = LineCount;
                for (int i = 0; i < lineCount; i++)
                {
Debug.Assert(lineCount == LineCount); 
                    LineMetrics lineMetrics = GetLine(i);
                    double contentBottom = Math.Max(0.0, RenderSize.Height - Padding.Bottom); 
 
                    // Find out if this is the last rendered line
                    if (CheckFlags(Flags.HasParagraphEllipses)) 
                    {
                        if (i + 1 < lineCount)
                        {
                            // Calculate bottom offset for next line 
                            double nextLineBottomOffset = GetLine(i + 1).Height + lineMetrics.Height + lineOffset.Y;
 
                            // If the next line will exceed render height by a large margin, we cannot render 
                            // it at all and so we should show ellipsis on this one. However if the next line
                            // almost fits, we will render it and so there should be no ellipsis 
                            showParagraphEllipsis = DoubleUtil.GreaterThan(nextLineBottomOffset, contentBottom) && !DoubleUtil.AreClose(nextLineBottomOffset, contentBottom);
                        }
                    }
 
                    // If paragraph ellipsis are enabled, do not render lines that
                    // extend computed layout size. But if the first line does not fit completely, 
                    // render it anyway. 
                    if (!CheckFlags(Flags.HasParagraphEllipses) ||
                        (DoubleUtil.LessThanOrClose(lineMetrics.Height + lineOffset.Y, contentBottom) || i == 0)) 
                    {
                        using (line)
                        {
                            line.Format(dcp, wrappingWidth, GetLineProperties(dcp == 0, showParagraphEllipsis, lineProperties), lineMetrics.TextLineBreak, _textBlockCache._textRunCache, showParagraphEllipsis); 

                            // 
 

 



 

 
 

 


                            if (!CheckFlags(Flags.HasParagraphEllipses))
                            { 
                                lineMetrics = UpdateLine(i, lineMetrics, line.Start, line.Width);
                            } 
 
                            line.Render(ctx, lineOffset);
 
                            lineOffset.Y += lineMetrics.Height;
                            dcp += lineMetrics.Length;
                        }
                    } 

                } 
            } 
            finally
            { 
                SetFlags(false, Flags.TreeInReadOnlyMode);
                _textBlockCache = null;
            }
        } 

        ///  
        /// Notification that a specified property has been invalidated 
        /// 
        /// EventArgs that contains the property, metadata, old value, and new value for this change 
        protected sealed override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            // Always call base.OnPropertyChanged, otherwise Property Engine will not work.
            base.OnPropertyChanged(e); 

            if (e.IsAValueChange || e.IsASubPropertyChange) 
            { 
                if (CheckFlags(Flags.FormattedOnce))
                { 
                    FrameworkPropertyMetadata fmetadata = e.Metadata as FrameworkPropertyMetadata;
                    if (fmetadata != null)
                    {
                        bool affectsRender = (fmetadata.AffectsRender && 
                            (e.IsAValueChange || !fmetadata.SubPropertiesDoNotAffectRender));
 
                        if (fmetadata.AffectsMeasure || fmetadata.AffectsArrange || affectsRender) 
                        {
                            // Will throw an exception, if during measure/arrange/render process. 
                            VerifyTreeIsUnlocked();

                            // TextRunCache stores properties for every single run fetched so far.
                            // If there are any property changes, which affect measure, arrange or 
                            // render, invalidate TextRunCache. It will force TextFormatter to refetch
                            // runs and properties. 
                           // _lineProperties = null; 
                            _textBlockCache = null;
                        } 
                    }
                }
            }
        } 

        ///  
        /// HitTestCore implements precise hit testing against render contents 
        /// 
        protected sealed override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) 
        {
            VerifyReentrancy();

            if (hitTestParameters == null) 
            {
                throw new ArgumentNullException("hitTestParameters"); 
            } 

            Rect r = new Rect(new Point(), RenderSize); 

            if (r.Contains(hitTestParameters.HitPoint))
            {
                return new PointHitTestResult(this, hitTestParameters.HitPoint); 
            }
            return null; 
        } 

        ///  
        /// Hit tests to the correct ContentElement within the ContentHost
        /// that the mouse is over.
        /// 
        /// Mouse coordinates relative to the ContentHost. 
        protected virtual IInputElement InputHitTestCore(Point point)
        { 
            // If layout data is not updated return 'this'. 
            if (!IsLayoutDataValid) { return this; }
 
            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties();

            // If there is only one line and it is already cached, use it to do hit-testing. 
            // Otherwise, do following:
            // a) use cached line information to find which line has been hit, 
            // b) re-create the line that has been hit, 
            // c) hit-test the line.
            IInputElement ie = null; 
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);
            point -= contentOffset; // // Take into account content offset.
 
            if (point.X < 0 || point.Y < 0) return this;
 
            ie = null; 
            int dcp = 0;
            double lineOffset = 0; 

            TextRunCache textRunCache = new TextRunCache();

            int lineCount = LineCount; 
            for (int i = 0; i < lineCount; i++)
            { 
Debug.Assert(lineCount == LineCount); 
                LineMetrics lineMetrics = GetLine(i);
 
                if (lineOffset + lineMetrics.Height > point.Y)
                {
                    // The current line has been hit. Format the line and
                    // retrieve IInputElement from the hit position. 
                    Line line = CreateLine(lineProperties);
                    using (line) 
                    { 
                        // Check if paragraph ellipsis are rendered
                        bool ellipsis = ParagraphEllipsisShownOnLine(i, lineOffset); 
                        line.Format(dcp, wrappingWidth, GetLineProperties(dcp == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, ellipsis);

                        // Verify consistency of line formatting
                        // Check that lineMetrics.Length is in sync with line.Length 
                        //
 
 
                        //
 



 

                        if ((line.Start <= point.X) && (line.Start + line.Width >= point.X)) 
                        { 
                            ie = line.InputHitTest(point.X);
                        } 
                    }
                    break; // Line covering the point has been found; no need to continue.
                }
 
                dcp += lineMetrics.Length;
                lineOffset += lineMetrics.Height; 
            } 

            // If nothing has been hit, assume that element itself has been hit. 
            return (ie != null) ? ie : this;
        }

        ///  
        /// Returns an ICollection of bounding rectangles for the given ContentElement
        ///  
        ///  
        /// Content element for which rectangles are required
        ///  
        /// 
        /// Looks at the ContentElement e line by line and gets rectangle bounds for each line
        /// 
        protected virtual ReadOnlyCollection GetRectanglesCore(ContentElement child) 
        {
            if (child == null) 
            { 
                throw new ArgumentNullException("e");
            } 

            // If layout data is not updated we assume that we will not be able to find the element we need and throw excception
            if (!IsLayoutDataValid)
            { 
                // return empty collection
                return new ReadOnlyCollection(new List(0)); 
            } 

            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing. 
            LineProperties lineProperties = GetLineProperties();

            // Check for complex content
            if (_complexContent == null || !(_complexContent.TextContainer is TextContainer)) 
            {
                // return empty collection 
                return new ReadOnlyCollection(new List(0)); 
            }
 
            // First find the element start and end position
            TextPointer start = FindElementPosition((IInputElement)child);
            if (start == null)
            { 
                return new ReadOnlyCollection(new List(0));
            } 
 
            TextPointer end = null;
            if (child is TextElement) 
            {
                end = new TextPointer(((TextElement)child).ElementEnd);
            }
            else if (child is FrameworkContentElement) 
            {
                end = new TextPointer(start); 
                end.MoveByOffset(+1); 
            }
 
            if (end == null)
            {
                return new ReadOnlyCollection(new List(0));
            } 

            int startOffset = _complexContent.TextContainer.Start.GetOffsetToPosition(start); 
            int endOffset = _complexContent.TextContainer.Start.GetOffsetToPosition(end); 

            int lineIndex = 0; 
            int lineOffset = 0;
            double lineHeightOffset = 0;
            int lineCount = LineCount;
            while (startOffset >= (lineOffset + GetLine(lineIndex).Length) && lineIndex < lineCount) 
            {
Debug.Assert(lineCount == LineCount); 
                lineOffset += GetLine(lineIndex).Length; 
                lineIndex++;
                lineHeightOffset += GetLine(lineIndex).Height; 
            }
            Debug.Assert(lineIndex < lineCount);

            int lineStart = lineOffset; 
            List rectangles = new List();
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width); 
 
            TextRunCache textRunCache = new TextRunCache();
 
            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);
            do
            {
Debug.Assert(lineCount == LineCount); 
                // Check that line index never exceeds line count
                Debug.Assert(lineIndex < lineCount); 
 
                // Create lines as long as they are spanned by the element
                LineMetrics lineMetrics = GetLine(lineIndex); 

                Line line = CreateLine(lineProperties);

                using (line) 
                {
                    // Check if paragraph ellipsis are rendered 
                    bool ellipsis = ParagraphEllipsisShownOnLine(lineIndex, lineOffset); 
                    line.Format(lineStart, wrappingWidth, GetLineProperties(lineIndex == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, ellipsis);
 
                    // Verify consistency of line formatting
                    //
                    if (lineMetrics.Length == line.Length)
                    { 
                        //MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync");
                        //Debug.Assert(DoubleUtil.AreClose(CalcLineAdvance(line.Height, lineProperties), lineMetrics.Height), "Line height is out of sync."); 
 
                        int boundStart = (startOffset >= lineStart) ? startOffset : lineStart;
                        int boundEnd = (endOffset < lineStart + lineMetrics.Length) ? endOffset : lineStart + lineMetrics.Length; 

                        double xOffset = contentOffset.X;
                        double yOffset = contentOffset.Y + lineHeightOffset;
                        List lineBounds = line.GetRangeBounds(boundStart, boundEnd - boundStart, xOffset, yOffset); 
                        Debug.Assert(lineBounds.Count > 0);
                        rectangles.AddRange(lineBounds); 
                    } 
                }
 
                lineStart += lineMetrics.Length;
                lineHeightOffset += lineMetrics.Height;
                lineIndex++;
            } 
            while (endOffset > lineStart);
 
            // Rectangles collection must be non-null 
            Invariant.Assert(rectangles != null);
            return new ReadOnlyCollection(rectangles); 
        }

        /// 
        /// Returns elements hosted by the content host as an enumerator class 
        /// 
        protected virtual IEnumerator HostedElementsCore 
        { 
            get
            { 
                if(CheckFlags(Flags.ContentChangeInProgress))
                {
                    #pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception
                    throw new InvalidOperationException(SR.Get(SRID.TextContainerChangingReentrancyInvalid)); 
                }
 
                if (_complexContent == null || !(_complexContent.TextContainer is TextContainer)) 
                {
                    // Return empty collection 
                    return new HostedElements(new ReadOnlyCollection(new List(0)));
                }

                // Create a TextSegment from TextContainer, use it to return enumerator 
                System.Collections.Generic.List textSegmentsList = new System.Collections.Generic.List(1);
                TextSegment textSegment = new TextSegment(_complexContent.TextContainer.Start, _complexContent.TextContainer.End); 
                textSegmentsList.Insert(0, textSegment); 
                ReadOnlyCollection textSegments = new ReadOnlyCollection(textSegmentsList);
 
                // Return enumerator created from textSegments
                return new HostedElements(textSegments);
            }
        } 

        ///  
        /// Called when a UIElement-derived class which is hosted by a IContentHost changes its DesiredSize 
        /// 
        ///  
        /// Child element whose DesiredSize has changed
        /// 
        protected virtual void OnChildDesiredSizeChangedCore(UIElement child)
        { 
            this.OnChildDesiredSizeChanged(child);
        } 
 
        /// 
        /// Creates AutomationPeer () 
        /// 
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new TextBlockAutomationPeer(this); 
        }
 
        #endregion Protected Methods 

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

        #region Internal Methods 
 
        /// 
        /// remove a child from TextBlock's collection (only Visually) 
        /// Used by ComplexLine.cs, etc
        /// 
        internal void RemoveChild(Visual child)
        { 
            if (_complexContent != null)
            { 
                _complexContent.VisualChildren.Remove(child); 
            }
        } 

        /// 
        /// Sets external text container as a content for this TextBlock element.
        ///  
        /// 
        ///  
        /// External text container will remain owned by its current owner. 
        /// This TextBlock element is not supposed to own it,
        /// which means that this TextBlock will have empty Children collection, 
        /// and all top level elements in passed TextContainer will
        /// remain parented in their respective owner.
        /// 
        internal void SetTextContainer(ITextContainer textContainer) 
        {
            // Detach from a previous container 
            if (_complexContent != null) 
            {
                _complexContent.Detach(this); 
                _complexContent = null;
                SetFlags(false, Flags.PendingTextContainerEventInit);
            }
            // Attach new container 
            if (textContainer != null)
            { 
                _complexContent = null; 
                EnsureComplexContent(textContainer);
            } 
            SetFlags(false, Flags.ContentChangeInProgress);
            // Invalidate measure in responce to invalidated content.
            InvalidateMeasure();
            InvalidateVisual(); //invlaidate rendering in addition to sizing info 

        } 
 
        //--------------------------------------------------------------------
        // Measure child UIElement. 
        //
        //      inlineObject - hosted inline object to measure.
        //
        // Returns: Size of the inline object. 
        //-------------------------------------------------------------------
        internal Size MeasureChild(InlineObject inlineObject) 
        { 
            Debug.Assert(_complexContent != null, "Inline objects are supported only in complex content.");
 
            Size desiredSize;
            // Measure child only during measure pass. If not during measuring
            // use RenderSize.
            if (CheckFlags(Flags.MeasureInProgress)) 
            {
                // Measure inline objects. Original size constraint is passed, 
                // because inline object size should not be dependent on position 
                // inside a text line. It should not be also bigger than Text itself.
                Thickness padding = this.Padding; 
                Size contentSize = new Size(Math.Max(0.0, _referenceSize.Width - (padding.Left + padding.Right)),
                                            Math.Max(0.0, _referenceSize.Height - (padding.Top + padding.Bottom)));
                inlineObject.Element.Measure(contentSize);
                desiredSize = inlineObject.Element.DesiredSize; 

                // Store inline object in the cache. 
                ArrayList inlineObjects = InlineObjects; 
                bool alreadyCached = false;
                if (inlineObjects == null) 
                {
                    InlineObjects = inlineObjects = new ArrayList(1);
                }
                else 
                {
                    // Find out if inline object is already cached. 
                    for (int index = 0; index < inlineObjects.Count; index++) 
                    {
                        if (((InlineObject)inlineObjects[index]).Dcp == inlineObject.Dcp) 
                        {
                            Debug.Assert(((InlineObject)inlineObjects[index]).Element == inlineObject.Element, "InlineObject cache is out of sync.");
                            alreadyCached = true;
                            break; 
                        }
                    } 
                } 
                if (!alreadyCached)
                { 
                    inlineObjects.Add(inlineObject);
                }
            }
            else 
            {
                desiredSize = inlineObject.Element.DesiredSize; 
            } 
            return desiredSize;
        } 

        /// 
        ///     Gives a string representation of this object.
        ///  
        internal override string GetPlainText()
        { 
            if (_complexContent != null) 
            {
                return TextRangeBase.GetTextInternal(_complexContent.TextContainer.Start, _complexContent.TextContainer.End); 
            }
            else
            {
                if (_contentCache != null) 
                {
                    return _contentCache; 
                } 
            }
 
            return String.Empty;
        }

        ///  
        /// Returns a new array of LineResults for the paragraph's lines.
        ///  
        internal ReadOnlyCollection GetLineResults() 
        {
#if TEXTPANELLAYOUTDEBUG 
            MS.Internal.PtsHost.TextPanelDebug.IncrementCounter("TextBlock.GetLines", MS.Internal.PtsHost.TextPanelDebug.Category.TextView);
#endif
            Invariant.Assert(IsLayoutDataValid);
 
            // Proper line alignment has to be done bofore LineResults are created.
            // Owherwise Line.Start may have wrong value. 
            if (CheckFlags(Flags.RequiresAlignment)) 
            {
                AlignContent(); 
            }

            // Calculate content offset.
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width); 
            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);
 
            // Create line results 
            int lineCount = LineCount;
            List lines = new List(lineCount); 
            int dcp = 0;
            double lineOffset = 0;
            for (int lineIndex = 0; lineIndex < lineCount; lineIndex++)
            { 
Debug.Assert(lineCount == LineCount);
                LineMetrics lineMetrics = GetLine(lineIndex); 
 
                Rect layoutBox = new Rect(contentOffset.X + lineMetrics.Start, contentOffset.Y + lineOffset, lineMetrics.Width, lineMetrics.Height);
                lines.Add(new TextLineResult(this, dcp, lineMetrics.Length, layoutBox, lineMetrics.Baseline, lineIndex)); 

                lineOffset += lineMetrics.Height;
                dcp += lineMetrics.Length;
            } 
            return new ReadOnlyCollection(lines);
        } 
 
        /// 
        /// Retrieves detailed information about a line of text. 
        /// 
        /// Index of the first character in the line.
        ///  Index of the line
        ///  Vertical offset of the line 
        /// Number of content characters in the line.
        /// Number of content characters hidden by ellipses. 
        internal void GetLineDetails(int dcp, int index, double lineVOffset, out int cchContent, out int cchEllipses) 
        {
            Invariant.Assert(IsLayoutDataValid); 
            Invariant.Assert(index >= 0 && index < LineCount);

            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties(); 
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
 
            TextRunCache textRunCache = new TextRunCache(); 

            // Retrieve details from the line. 
            using(Line line = CreateLine(lineProperties))
            {
                // Format line. Set showParagraphEllipsis flag to false
                TextLineBreak textLineBreak = GetLine(index).TextLineBreak; 
                bool ellipsis = ParagraphEllipsisShownOnLine(index, lineVOffset);
                line.Format(dcp, wrappingWidth, GetLineProperties(dcp == 0, lineProperties), textLineBreak, textRunCache, ellipsis); 
 
                MS.Internal.Invariant.Assert(GetLine(index).Length == line.Length, "Line length is out of sync");
 
                cchContent = line.ContentLength;
                cchEllipses = line.GetEllipsesLength();
            }
        } 

        ///  
        /// Retrieve text position from the distance (relative to the beginning 
        /// of specified line).
        ///  
        /// Index of the first character in the line.
        /// Distance relative to the beginning of the line.
        /// 
        /// Vertical offset of the line in which the position lies, 
        /// 
        ///  
        /// Index of the line 
        /// 
        ///  
        /// A text position and its orientation matching or closest to the distance.
        /// 
        internal ITextPointer GetTextPositionFromDistance(int dcp, double distance, double lineVOffset, int index)
        { 
#if TEXTPANELLAYOUTDEBUG
            MS.Internal.PtsHost.TextPanelDebug.StartTimer("TextBlock.GetTextPositionFromDistance", MS.Internal.PtsHost.TextPanelDebug.Category.TextView); 
#endif 
            Invariant.Assert(IsLayoutDataValid);
 
            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties();
            EnsureComplexContent(); // TextOM access requires complex content.
 
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth); 
            distance -= contentOffset.X; 
            lineVOffset -= contentOffset.Y;
 
            TextRunCache textRunCache = new TextRunCache();
            ITextPointer pos;
            using(Line line = CreateLine(lineProperties))
            { 
                MS.Internal.Invariant.Assert(index >= 0 && index < LineCount);
                TextLineBreak textLineBreak = GetLine(index).TextLineBreak; 
                bool ellipsis = ParagraphEllipsisShownOnLine(index, lineVOffset); 
                line.Format(dcp, wrappingWidth, GetLineProperties(dcp == 0, lineProperties), textLineBreak, textRunCache, ellipsis);
 
                MS.Internal.Invariant.Assert(GetLine(index).Length == line.Length, "Line length is out of sync");

                CharacterHit charIndex = line.GetTextPositionFromDistance(distance);
                LogicalDirection logicalDirection; 

                logicalDirection = (charIndex.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; 
                pos = _complexContent.TextContainer.Start.CreatePointer(charIndex.FirstCharacterIndex + charIndex.TrailingLength, logicalDirection); 
            }
 
#if TEXTPANELLAYOUTDEBUG
            MS.Internal.PtsHost.TextPanelDebug.StopTimer("TextBlock.GetTextPositionFromDistance", MS.Internal.PtsHost.TextPanelDebug.Category.TextView);
#endif
            return pos; 
        }
 
        ///  
        /// Retrieves bounds of an object/character at the specified TextPointer.
        /// Throws IndexOutOfRangeException if position is out of range. 
        /// 
        /// Position of an object/character.
        /// Bounds of an object/character.
        internal Rect GetRectangleFromTextPosition(ITextPointer orientedPosition) 
        {
#if TEXTPANELLAYOUTDEBUG 
            MS.Internal.PtsHost.TextPanelDebug.StartTimer("TextBlock.GetRectangleFromTextPosition", MS.Internal.PtsHost.TextPanelDebug.Category.TextView); 
#endif
            Invariant.Assert(IsLayoutDataValid); 
            Invariant.Assert(orientedPosition != null);

            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties(); 
            EnsureComplexContent();
 
            // From TextFormatter get rectangle of a single character. 
            // If orientation is Backward, get the length of th previous character.
            int characterIndex = _complexContent.TextContainer.Start.GetOffsetToPosition(orientedPosition); 
            int originalCharacterIndex = characterIndex;
            if (orientedPosition.LogicalDirection == LogicalDirection.Backward && characterIndex > 0)
            {
                --characterIndex; 
            }
 
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width); 
            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);
 
            double lineOffset = 0;
            int dcp = 0;

            TextRunCache textRunCache = new TextRunCache(); 

            Rect rect = Rect.Empty; 
            FlowDirection flowDirection = FlowDirection.LeftToRight; 

            int lineCount = LineCount; 
            for (int i = 0; i < lineCount; i++)
            {
Debug.Assert(lineCount == LineCount);
                LineMetrics lineMetrics = GetLine(i); 

                // characterIndex needs to be within line range. If position points to 
                // dcp + line.Length, it means that the next line starts from such position, 
                // hence go to the next line.
                // But if this is the last line (EOP character), get rectangle form the last 
                // character of the line.
                if (dcp + lineMetrics.Length > characterIndex ||
                    ((dcp + lineMetrics.Length == characterIndex) && (i == lineCount - 1)))
                { 
                    using(Line line = CreateLine(lineProperties))
                    { 
 
                        bool ellipsis = ParagraphEllipsisShownOnLine(i, lineOffset);
                        line.Format(dcp, wrappingWidth, GetLineProperties(dcp == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, ellipsis); 

                        // Check consistency of line length
                        MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync");
 
                        rect = line.GetBoundsFromTextPosition(characterIndex, out flowDirection);
                    } 
 
                    break;
                } 

                dcp += lineMetrics.Length;
                lineOffset += lineMetrics.Height;
            } 

            if (!rect.IsEmpty) // Empty rects can't be modified 
            { 
                rect.X += contentOffset.X;
                rect.Y += contentOffset.Y + lineOffset; 

                // 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 (orientedPosition.LogicalDirection == LogicalDirection.Forward || originalCharacterIndex == 0) 
                    {
                        rect.X = rect.Right; 
                    }
                }
                else
                { 
                    // NOTE: check for 'originalCharacterIndex > 0' is only required for position at the beginning
                    //       content with Backward orientation. This should not be a valid position. 
                    //       Remove it later 
                    if (orientedPosition.LogicalDirection == LogicalDirection.Backward && originalCharacterIndex > 0)
                    { 
                        rect.X = rect.Right;
                    }
                }
                rect.Width = 0; 
            }
#if TEXTPANELLAYOUTDEBUG 
            MS.Internal.PtsHost.TextPanelDebug.StopTimer("TextBlock.GetRectangleFromTextPosition", MS.Internal.PtsHost.TextPanelDebug.Category.TextView); 
#endif
 
            return rect;
        }

        ///  
        /// Implementation of TextParagraphView.GetTightBoundingGeometryFromTextPositions.
        ///  
        ///  
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition)
        { 
#if TEXTPANELLAYOUTDEBUG
            MS.Internal.PtsHost.TextPanelDebug.StartTimer("TextBlock.GetTightBoundingGeometryFromTextPositions", MS.Internal.PtsHost.TextPanelDebug.Category.TextView);
#endif
            Invariant.Assert(IsLayoutDataValid); 
            Invariant.Assert(startPosition != null);
            Invariant.Assert(endPosition != null); 
            Invariant.Assert(startPosition.CompareTo(endPosition) <= 0); 

            Geometry geometry = null; 

            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties();
            EnsureComplexContent(); // TextOM access requires complex content. 

            int dcpPositionStart = _complexContent.TextContainer.Start.GetOffsetToPosition(startPosition); 
            int dcpPositionEnd = _complexContent.TextContainer.Start.GetOffsetToPosition(endPosition); 

            double wrappingWidth = CalcWrappingWidth(RenderSize.Width); 
            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);

            TextRunCache textRunCache = new TextRunCache();
            Line line = CreateLine(lineProperties); 

            int dcpLineStart = 0; 
            ITextPointer endOfLineTextPointer = _complexContent.TextContainer.Start.CreatePointer(0); 
            double lineOffset = 0;
 
            int lineCount = LineCount;
            for (int i = 0, count = lineCount; i < count; ++i)
            {
                LineMetrics lineMetrics = GetLine(i); 

                if (dcpPositionEnd <= dcpLineStart) 
                { 
                    //  this line starts after the range's end.
                    //  safe to break from the loop. 
                    break;
                }

                int dcpLineEnd = dcpLineStart + lineMetrics.Length; 
                endOfLineTextPointer.MoveByOffset(lineMetrics.Length);
 
                if (dcpPositionStart < dcpLineEnd) 
                {
                    using (line) 
                    {
                        bool ellipsis = ParagraphEllipsisShownOnLine(i, lineOffset);
                        line.Format(dcpLineStart, wrappingWidth, GetLineProperties(dcpLineStart == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, ellipsis);
 
                        if (Invariant.Strict)
                        { 
                            // Check consistency of line formatting 
                            MS.Internal.Invariant.Assert(GetLine(i).Length == line.Length, "Line length is out of sync");
                        } 

                        int dcpStart = Math.Max(dcpLineStart, dcpPositionStart);
                        int dcpEnd = Math.Min(dcpLineEnd, dcpPositionEnd);
 
                        IList aryTextBounds = line.GetRangeBounds(dcpStart, dcpEnd - dcpStart, contentOffset.X, contentOffset.Y + lineOffset);
 
                        if (aryTextBounds.Count > 0) 
                        {
                            int j = 0; 
                            int c = aryTextBounds.Count;

                            do
                            { 
                                Rect rect = aryTextBounds[j];
 
                                if (    j == (c - 1) 
                                   &&   dcpPositionEnd >= dcpLineEnd
                                   &&   TextPointerBase.IsNextToAnyBreak(endOfLineTextPointer, LogicalDirection.Backward)   ) 
                                {
                                    double endOfParaGlyphWidth = FontSize * CaretElement.c_endOfParaMagicMultiplier;
                                    rect.Width = rect.Width + endOfParaGlyphWidth;
                                } 

                                RectangleGeometry rectGeometry = new RectangleGeometry(rect); 
                                CaretElement.AddGeometry(ref geometry, rectGeometry); 
                            } while (++j < c);
                        } 
                    }
                }

                dcpLineStart += lineMetrics.Length; 
                lineOffset += lineMetrics.Height;
            } 
 
#if TEXTPANELLAYOUTDEBUG
            MS.Internal.PtsHost.TextPanelDebug.StopTimer("TextBlock.GetTightBoundingGeometryFromTextPositions", MS.Internal.PtsHost.TextPanelDebug.Category.TextView); 
#endif
            return (geometry);
        }
 
        /// 
        /// Determines if the given position is at the edge of a caret unit 
        /// in the specified direction, and returns true if it is and false otherwise. 
        /// Used by the ITextView.IsCaretAtUnitBoundary(ITextPointer position) in
        /// TextParagraphView 
        /// 
        /// 
        /// Position to test.
        ///  
        /// 
        /// Offset of the current position from start of TextContainer 
        ///  
        /// 
        /// Index of line in which position is found 
        /// 
        internal bool IsAtCaretUnitBoundary(ITextPointer position, int dcp, int lineIndex)
        {
            Invariant.Assert(IsLayoutDataValid); 
            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties(); 
            EnsureComplexContent(); 

            TextRunCache textRunCache = new TextRunCache(); 
            bool isAtCaretUnitBoundary = false;

            int characterIndex = _complexContent.TextContainer.Start.GetOffsetToPosition(position);
            CharacterHit charHit = new CharacterHit(); 
            if (position.LogicalDirection == LogicalDirection.Backward)
            { 
                if (characterIndex > dcp) 
                {
                    // Go to trailing edge of previous character 
                    charHit = new CharacterHit(characterIndex - 1, 1);
                }
                else
                { 
                    // We should not be at line's start dcp with backward context, except in case this is the first line. This is not
                    // a unit boundary 
                    return false; 
                }
            } 
            else if (position.LogicalDirection == LogicalDirection.Forward)
            {
                // Get leading edge of this character index
                charHit = new CharacterHit(characterIndex, 0); 
            }
 
            LineMetrics lineMetrics = GetLine(lineIndex); 
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
 
            using(Line line = CreateLine(lineProperties))
            {
                // Format line. Set showParagraphEllipsis flag to false since we are not using information about
                // ellipsis to change line offsets in this case. 
                line.Format(dcp, wrappingWidth, GetLineProperties(lineIndex == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, false);
 
                // Check consistency of line formatting 
                MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync");
                isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit); 
            }

            return isAtCaretUnitBoundary;
        } 

        ///  
        /// Finds and returns the next position at the edge of a caret unit in 
        /// specified direction.
        ///  
        /// 
        /// Initial text position of an object/character.
        /// 
        ///  
        /// If Forward, this method returns the "caret unit" position following
        /// the initial position. 
        /// If Backward, this method returns the caret unit" position preceding 
        /// the initial position.
        ///  
        /// 
        /// Offset of the current position from start of TextContainer
        /// 
        ///  
        /// Index of line in which position is found
        ///  
        internal ITextPointer GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction, int dcp, int lineIndex) 
        {
            Invariant.Assert(IsLayoutDataValid); 

            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties();
            EnsureComplexContent(); 

            int characterIndex = _complexContent.TextContainer.Start.GetOffsetToPosition(position); 
 
            // Process special cases
            if (characterIndex == dcp && direction == LogicalDirection.Backward) 
            {
                // Start of line
                if (lineIndex == 0)
                { 
                    // First line. Cannot go back any further
                    return position; 
 
                }
                else 
                {
                    // Change lineIndex and dcp
                    Debug.Assert(lineIndex > 0);
                    --lineIndex; 
                    dcp -= GetLine(lineIndex).Length;
                    Debug.Assert(dcp >= 0); 
                } 
            }
            else if (characterIndex == (dcp + GetLine(lineIndex).Length) && direction == LogicalDirection.Forward) 
            {
                // End of line
                int lineCount = LineCount;
                if (lineIndex == lineCount - 1) 
                {
                    // Cannot go down any further 
                    return position; 
                }
                else 
                {
                    // Change lineIndex and dcp to next line
                    Debug.Assert(lineIndex < lineCount - 1);
                    dcp += GetLine(lineIndex).Length; 
                    ++lineIndex;
                } 
            } 

            TextRunCache textRunCache = new TextRunCache(); 

            // Creat CharacterHit from characterIndex and call line APIs
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
            CharacterHit textSourceCharacterIndex = new CharacterHit(characterIndex, 0); 

            CharacterHit nextCharacterHit; 
            LineMetrics lineMetrics = GetLine(lineIndex); 

            using(Line line = CreateLine(lineProperties)) 
            {
                // Format line. Set showParagraphEllipsis flag to false since we are not using information about
                // ellipsis to change line offsets in this case.
                line.Format(dcp, wrappingWidth, GetLineProperties(lineIndex == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, false); 

                // Check consistency of line formatting 
                MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync"); 

                if (direction == LogicalDirection.Forward) 
                {
                    // Get the next caret position from the line
                    nextCharacterHit = line.GetNextCaretCharacterHit(textSourceCharacterIndex);
                } 
                else
                { 
                    // Get previous caret position from the line 
                    nextCharacterHit = line.GetPreviousCaretCharacterHit(textSourceCharacterIndex);
                } 
            }

            // Determine logical direction for next caret index and create TextPointer from it
            LogicalDirection logicalDirection; 
            if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == (dcp + GetLine(lineIndex).Length)) && direction == LogicalDirection.Forward)
            { 
                // Going forward brought us to the end of a line, context must be forward for next line 
                if (lineIndex == LineCount - 1)
                { 
                    // last line so context must stay backward
                    logicalDirection = LogicalDirection.Backward;
                }
                else 
                {
                    logicalDirection = LogicalDirection.Forward; 
                } 
            }
            else if ((nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == dcp) && direction == LogicalDirection.Backward) 
            {
                // Going forward brought us to the start of a line, context must be backward for previous line
                if (dcp == 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 nextCaretPosition = _complexContent.TextContainer.Start.CreatePointer(nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength, logicalDirection);
 

            // Return nextCaretPosition
            return nextCaretPosition;
        } 

        ///  
        /// Finds and returns the position after backspace at the edge of a caret unit in 
        /// specified direction.
        ///  
        /// 
        /// Initial text position of an object/character.
        /// 
        ///  
        /// Offset of the current position from start of TextContainer
        ///  
        ///  
        /// Index of line in which position is found
        ///  
        internal ITextPointer GetBackspaceCaretUnitPosition(ITextPointer position, int dcp, int lineIndex)
        {
            Invariant.Assert(IsLayoutDataValid);
 
            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties(); 
            EnsureComplexContent(); 

            // Get character index for position 
            int characterIndex = _complexContent.TextContainer.Start.GetOffsetToPosition(position);

            // Process special cases
            if (characterIndex == dcp) 
            {
                if (lineIndex == 0) 
                { 
                    // Cannot go back any further
                    return position; 
                }
                else
                {
                    // Change lineIndex and dcp to previous line 
                    Debug.Assert(lineIndex > 0);
                    --lineIndex; 
                    dcp -= GetLine(lineIndex).Length; 
                    Debug.Assert(dcp >= 0);
                } 
            }

            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
            CharacterHit textSourceCharacterIndex = new CharacterHit(characterIndex, 0); 
            CharacterHit backspaceCharacterHit;
            LineMetrics lineMetrics = GetLine(lineIndex); 
 
            TextRunCache textRunCache = new TextRunCache();
            // Create and Format line 
            using(Line line = CreateLine(lineProperties))
            {
                // Format line. Set showParagraphEllipsis flag to false since we are not using information about
                // ellipsis to change line offsets in this case. 
                line.Format(dcp, wrappingWidth, GetLineProperties(lineIndex == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, false);
 
                // Check consistency of line formatting 
                MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync");
 
                backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(textSourceCharacterIndex);
            }
            // Get CharacterHit and call line API
 
            // Determine logical direction for next caret index and create TextPointer from it
            LogicalDirection logicalDirection; 
            if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == dcp) 
            {
                // Going forward brought us to the start of a line, context must be backward for previous line 
                if (dcp == 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 backspaceCaretPosition = _complexContent.TextContainer.Start.CreatePointer(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection); 
 
            // Return backspaceCaretPosition
            return backspaceCaretPosition; 
        }

        #endregion Internal methods
 
        //--------------------------------------------------------------------
        // 
        //  Internal Properties 
        //
        //-------------------------------------------------------------------- 

        #region Internal Properties

        //------------------------------------------------------------------- 
        // Text formatter object
        //-------------------------------------------------------------------- 
        internal TextFormatter TextFormatter 
        {
            get 
            {
                if (_textFormatter == null)
                {
                    _textFormatter = TextFormatter.FromCurrentDispatcher(); 
                }
                return _textFormatter; 
            } 
        }
 
        //-------------------------------------------------------------------
        // Text container.
        //-------------------------------------------------------------------
        internal ITextContainer TextContainer 
        {
            get 
            { 
                EnsureComplexContent();
                return _complexContent.TextContainer; 
            }
        }

        //------------------------------------------------------------------- 
        // TextView
        //-------------------------------------------------------------------- 
        internal ITextView TextView 
        {
            get 
            {
                EnsureComplexContent();
                return _complexContent.TextView;
            } 
        }
 
        //------------------------------------------------------------------- 
        // Highlights
        //-------------------------------------------------------------------- 
        internal Highlights Highlights
        {
            get
            { 
                EnsureComplexContent();
                return _complexContent.Highlights; 
            } 
        }
 
        //--------------------------------------------------------------------
        // TextBlock paragraph properties.
        //-------------------------------------------------------------------
        internal LineProperties ParagraphProperties 
        {
            get 
            { 
                LineProperties lineProperties = GetLineProperties();
                return lineProperties; 
            }
        }

 
        //--------------------------------------------------------------------
        // IsLayoutDataValid 
        //------------------------------------------------------------------- 
        internal bool IsLayoutDataValid
        { 
            get
            {
                return  IsMeasureValid && IsArrangeValid &&         // Measure and Arrange are valid
                        CheckFlags(Flags.HasFirstLine) && 
                        !CheckFlags(Flags.ContentChangeInProgress) &&
                        !CheckFlags(Flags.MeasureInProgress) && 
                        !CheckFlags(Flags.ArrangeInProgress);  // Content is not currently changeing 
            }
        } 

        //-------------------------------------------------------------------
        // HasComplexContent
        //------------------------------------------------------------------- 
        internal bool HasComplexContent
        { 
            get 
            {
                return (_complexContent != null); 
            }
        }

        //-------------------------------------------------------------------- 
        // IsTypographyDefaultValue
        //------------------------------------------------------------------- 
        internal bool IsTypographyDefaultValue 
        {
            get 
            {
                return !CheckFlags(Flags.IsTypographySet);
            }
        } 

        //-------------------------------------------------------------------- 
        // InlineObjects 
        //--------------------------------------------------------------------
        private ArrayList InlineObjects 
        {
            get { return (_complexContent == null) ? null : _complexContent.InlineObjects; }
            set { if (_complexContent != null) _complexContent.InlineObjects = value; }
        } 

        //------------------------------------------------------------------- 
        // Is this TextBlock control being used by a ContentPresenter/ HyperLink 
        // to host its content. If it is then TextBlock musn't try to disconnect the
        // logical parent pointer for the content. This flag allows the TextBlock 
        // to discover this special scenario and behave differently.
        //--------------------------------------------------------------------
        internal bool IsContentPresenterContainer
        { 
            get { return CheckFlags(Flags.IsContentPresenterContainer); }
            set { SetFlags(value, Flags.IsContentPresenterContainer); } 
        } 

        //  typography properties changed, no cache for this, just reset the flag 
        private static void OnTypographyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TextBlock) d).SetFlags(true, Flags.IsTypographySet);
        } 

        #endregion Internal Properties 
 
        //-------------------------------------------------------------------
        // 
        //  Private Methods
        //
        //-------------------------------------------------------------------
 
        #region Private Methods
 
        ///  
        /// Raise TextView.Updated event, if TextView is in a valid state.
        ///  
        private object OnValidateTextView(object arg)
        {
            if (IsLayoutDataValid && _complexContent != null)
            { 
                _complexContent.TextView.OnUpdated();
            } 
            return null; 
        }
 
        // Inserts text run into TextBlock in a form consistent with flow schema requirements.
        //
        // TextBlock has dual role as a text container -
        // plain text for TextBox and rich inline-collection content for everything else. 
        // In case of plain text we insert only string into the text container,
        // in all other cases we must wrap eact rext run by Run inline element. 
        private static void InsertTextRun(ITextPointer position, string text, bool whitespacesIgnorable) 
        {
            // We distinguish these two cases by parent of TextContainer: 
            // for plain text case it is TextBox.
            if (!(position is TextPointer) || ((TextPointer)position).Parent == null || ((TextPointer)position).Parent is TextBox)
            {
                position.InsertTextInRun(text); 
            }
            else 
            { 
                if (!whitespacesIgnorable || text.Trim().Length > 0)
                { 
                    Run implicitRun = Inline.CreateImplicitRun(((TextPointer)position).Parent);

                    ((TextPointer)position).InsertTextElement(implicitRun);
                    implicitRun.Text = text; 
                }
            } 
        } 

        // ----------------------------------------------------------------- 
        // Create appropriate line object.
        // a) SimpleLine, if the content is represented by a string.
        // b) ComplexLine, if the content is represented by a TextContainer.
        // ------------------------------------------------------------------ 
        private Line CreateLine(LineProperties lineProperties)
        { 
            Line line; 
            if (_complexContent == null)
                line = new SimpleLine(this, Text, lineProperties.DefaultTextRunProperties); 
            else
                line = new ComplexLine(this);
            return line;
        } 

        // ----------------------------------------------------------------- 
        // Make sure that complex content is enabled.  Creates a default TextContainer. 
        // ------------------------------------------------------------------
        private void EnsureComplexContent() 
        {
            EnsureComplexContent(null);
        }
 
        // ------------------------------------------------------------------
        // Make sure that complex content is enabled. 
        // ----------------------------------------------------------------- 
        private void EnsureComplexContent(ITextContainer textContainer)
        { 
            if (_complexContent == null)
            {
                if (textContainer == null)
                { 
                    textContainer = new TextContainer(IsContentPresenterContainer ? null : this, false /* plainTextOnly */);
                } 
 
                _complexContent = new ComplexContent(this, textContainer, false, Text);
                _contentCache = null; 

                if (CheckFlags(Flags.FormattedOnce))
                {
                    // If we've already measured at least once, hook up the TextContainer 
                    // listeners now.
                    Invariant.Assert(!CheckFlags(Flags.PendingTextContainerEventInit)); 
                    InitializeTextContainerListeners(); 

                    // Line layout data cached up to this point will become invalid 
                    // becasue of content structure change (implicit Run added).
                    // So we need to clear the cache - we call InvalidateMeasure for this
                    // purpose. However, we do not want to produce a side effect
                    // of making layout invalid as a result of touching ContentStart/ContentEnd 
                    // and other properties. For that we need to UpdateLayout when it was
                    // dirtied by our switch. 
                    bool wasLayoutValid = this.IsMeasureValid && this.IsArrangeValid; 
                    InvalidateMeasure();
                    InvalidateVisual(); //ensure re-rendering too 
                    if (wasLayoutValid)
                    {
                        UpdateLayout();
                    } 
                }
                else 
                { 
                    // Otherwise, wait until our first measure.
                    // This lets us skip the work for all content invalidation 
                    // during load, before the first measure.
                    SetFlags(true, Flags.PendingTextContainerEventInit);
                }
            } 
        }
 
        // ------------------------------------------------------------------ 
        // Make sure that complex content is cleared.
        // ----------------------------------------------------------------- 
        private void ClearComplexContent()
        {
            if (_complexContent != null)
            { 
                _complexContent.Detach(this);
                _complexContent = null; 
                Invariant.Assert(_contentCache == null, "Content cache should be null when complex content exists."); 
            }
        } 

        // -----------------------------------------------------------------
        // Invalidates a portion of text affected by a highlight change.
        // ----------------------------------------------------------------- 
        private void OnHighlightChanged(object sender, HighlightChangedEventArgs args)
        { 
            Invariant.Assert(args != null); 
            Invariant.Assert(args.Ranges != null);
            Invariant.Assert(CheckFlags(Flags.FormattedOnce), "Unexpected Highlights.Changed callback before first format!"); 

            // The only supported highlight type for TextBlock is SpellerHightlight.
            // TextSelection and HighlightComponent are ignored, because they are handled by
            // separate layer. 
            if (args.OwnerType != typeof(SpellerHighlightLayer))
            { 
                return; 
            }
 
            // NOTE: Assuming that only rendering only properties are changeing
            //       through highlights.
            InvalidateVisual();
        } 

        // ------------------------------------------------------------------ 
        // Handler for TextContainer changing notifications. 
        // -----------------------------------------------------------------
        private void OnTextContainerChanging(object sender, EventArgs args) 
        {
            Debug.Assert(sender == _complexContent.TextContainer, "Received text change for foreign TextContainer.");

            if (CheckFlags(Flags.FormattedOnce)) 
            {
                // Will throw an exception, if during measure/arrange/render process. 
                VerifyTreeIsUnlocked(); 

                // Remember the fact that content is changing. 
                // OnTextContainerEndChanging has to be received after this event.
                SetFlags(true, Flags.ContentChangeInProgress);
            }
        } 

        // ------------------------------------------------------------------ 
        // Handler for TextContainer changed notifications. 
        // ------------------------------------------------------------------
        private void OnTextContainerChange(object sender, TextContainerChangeEventArgs args) 
        {
            Invariant.Assert(args != null);

            if (_complexContent == null) 
            {
                // This shouldn't ever happen (we only hook up this handler when we have complex 
                // content)... except that it does happen, in cases where TextBlock is part of 
                // a style that gets changed in response to TextContainer.Changed events.  In such a case,
                // we're an obsolete text control and we don't want to do anything, so just return. 
                return;
            }
            Invariant.Assert(sender == _complexContent.TextContainer, "Received text change for foreign TextContainer.");
 
            if (args.Count == 0)
            { 
                // A no-op for this control.  Happens when IMECharCount updates happen 
                // without corresponding SymbolCount changes.
                return; 
            }

            if (CheckFlags(Flags.FormattedOnce))
            { 
                // Will throw an exception, if during measure/arrange/render process.
                VerifyTreeIsUnlocked(); 
                // Content has been changed, so reset appropriate flag. 
                SetFlags(false, Flags.ContentChangeInProgress);
                // Invalidate measure in responce to invalidated content. 
                InvalidateMeasure();
            }

            if (!CheckFlags(Flags.TextContentChanging)) 
            {
                SetFlags(true, Flags.TextContentChanging); 
                try 
                {
                    // Use a DeferredTextReference instead of calculating the new 
                    // value now for better performance.  Most of the time no
                    // one cares what the new is, and loading our content into a
                    // string can be expensive.
                    SetDeferredValue(TextProperty, new DeferredTextReference(this.TextContainer)); 
                }
                finally 
                { 
                    SetFlags(false, Flags.TextContentChanging);
                } 
            }
        }

        private void EnsureTextBlockCache() 
        {
            if (null == _textBlockCache) 
            { 
                _textBlockCache = new TextBlockCache();
                _textBlockCache._lineProperties = GetLineProperties(); 
                _textBlockCache._textRunCache = new TextRunCache();
            }
        }
 
        // -----------------------------------------------------------------
        // Refetch and cache line properties, if needed. 
        // ------------------------------------------------------------------ 
        private LineProperties GetLineProperties()
        { 
            // For default text properties always set background to null.
            // REASON: If element associated with the text run is TextBlock element, ignore background
            //         brush, because it is handled outside as FrameworkElement's background.
 
            TextProperties defaultTextProperties = new TextProperties(this, this.IsTypographyDefaultValue);
 
            // Do not allow hyphenation for plain Text so always pass null for IHyphenate. 
            // Pass page width and height as double.MaxValue when creating LineProperties, since TextBlock does not restrict
            // TextIndent or LineHeight 
            LineProperties lineProperties = new LineProperties(this, this, defaultTextProperties, null);

            bool isHyphenationEnabled = (bool) this.GetValue(IsHyphenationEnabledProperty);
            if(isHyphenationEnabled) 
            {
                lineProperties.Hyphenator = EnsureHyphenator(); 
            } 

 
            return lineProperties;

        }
 
        //-------------------------------------------------------------------
        // Get line properties 
        // 
        //      firstLine - is it for the first line?
        // 
        // Returns: Line properties for first/following lines.
        //-------------------------------------------------------------------
        private TextParagraphProperties GetLineProperties(bool firstLine, LineProperties lineProperties)
        { 
            return GetLineProperties(firstLine, false, lineProperties);
        } 
        private TextParagraphProperties GetLineProperties(bool firstLine, bool showParagraphEllipsis, LineProperties lineProperties) 
        {
            GetLineProperties(); 
            firstLine = firstLine && lineProperties.HasFirstLineProperties;
            if (!showParagraphEllipsis)
            {
                return firstLine ? lineProperties.FirstLineProps : lineProperties; 
            }
            else 
            { 
                return lineProperties.GetParaEllipsisLineProps(firstLine);
            } 
        }

        //-------------------------------------------------------------------
        // Calculate line advance distance. This functionality will go away 
        // when TextFormatter will be able to handle line height/stacking.
        // 
        //      lineHeight - calculated line height 
        //
        // Returns: Line advance distance.. 
        //--------------------------------------------------------------------
        private double CalcLineAdvance(double lineHeight, LineProperties lineProperties)
        {
            return lineProperties.CalcLineAdvance(lineHeight); 
        }
 
        //------------------------------------------------------------------- 
        // Calculate offset of the content taking into account horizontal / vertical
        // content alignment. 
        //
        // Returns: Content offset value.
        //--------------------------------------------------------------------
        private Vector CalcContentOffset(Size computedSize, double wrappingWidth) 
        {
            Vector contentOffset = new Vector(); 
 
            Thickness padding = this.Padding;
            Size contentSize = new Size(Math.Max(0.0, computedSize.Width - (padding.Left + padding.Right)), 
                                        Math.Max(0.0, computedSize.Height - (padding.Top + padding.Bottom)));

            switch (TextAlignment)
            { 
                case TextAlignment.Right:
                    contentOffset.X = contentSize.Width - wrappingWidth; 
                    break; 

                case TextAlignment.Center: 
                    contentOffset.X = (contentSize.Width - wrappingWidth) / 2;
                    break;

                // Default is Left alignment, in this case offset is 0. 
            }
 
            contentOffset.X += padding.Left; 
            contentOffset.Y += padding.Top;
 
            return contentOffset;
        }

        ///  
        /// Returns true if paragraph ellipsis will be rendered on this line
        ///  
        ///  
        /// Index of the line
        ///  
        /// 
        /// Vertical offset at which line starts
        /// 
        private bool ParagraphEllipsisShownOnLine(int lineIndex, double lineVOffset) 
        {
            if (lineIndex >= LineCount - 1) 
            { 
                // Last line. No paragraph ellipses
                return false; 
            }

            // Find out if this is the last rendered line
            if (!CheckFlags(Flags.HasParagraphEllipses)) 
            {
                return false; 
            } 

            // Calculate bottom offset for next line 
            double nextLineBottomOffset = GetLine(lineIndex + 1).Height + GetLine(lineIndex).Height + lineVOffset;
            // If the next line will exceed render height by a large margin, we cannot render
            // it at all and so we should show ellipsis on this one. However if the next line
            // almost fits, we will render it and so there should be no ellipsis 
            double contentBottom = Math.Max(0.0, RenderSize.Height - Padding.Bottom);
            if (DoubleUtil.GreaterThan(nextLineBottomOffset, contentBottom) && !DoubleUtil.AreClose(nextLineBottomOffset, contentBottom)) 
            { 
                return true;
            } 

            return false;
        }
 
        //--------------------------------------------------------------------
        // Calculate wrapping width for lines. 
        // 
        // Returns: Wrapping width.
        //------------------------------------------------------------------- 
        private double CalcWrappingWidth(double width)
        {
            // Reflowing will not happen when Width is between _previousDesiredSize.Width and ReferenceWidth.
            // In some cases _previousDesiredSize.Width > ReferenceSize, use ReferenceSize in those scenarios. 
            if (width < _previousDesiredSize.Width)
            { 
                width = _previousDesiredSize.Width; 
            }
            if (width > _referenceSize.Width) 
            {
                width = _referenceSize.Width;
            }
 
            width = Math.Max(0.0, width - (Padding.Left + Padding.Right));
 
            // Make sure that TextFormatter limitations are not exceeded. 
            TextDpi.EnsureValidLineWidth(ref width);
 
            return width;
        }

        // ------------------------------------------------------------------ 
        // Aborts calculation by throwing exception if world has changed
        // while in measure / arrange / render process. 
        // ----------------------------------------------------------------- 
        private void VerifyTreeIsUnlocked()
        { 
            if (CheckFlags(Flags.TreeInReadOnlyMode))
            {
                throw new InvalidOperationException(SR.Get(SRID.IllegalTreeChangeDetected));
            } 
        }
 
        // ----------------------------------------------------------------- 
        // Decides if Text property needs to be serialized.
        // ----------------------------------------------------------------- 
        /// 
        /// This method is used by TypeDescriptor to determine if this property should
        /// be serialized.
        ///  
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeText() 
        { 
            // We have to allow Text property serialization (at least for simple content)
            // to preserve data-binding expressions. If we totally disable serialization 
            // data binding expression will be lost.
            bool shouldSerialize = false;

            if (_complexContent == null) 
            {
                object localValue = ReadLocalValue(TextProperty); 
 
                if (localValue != null &&
                    localValue != DependencyProperty.UnsetValue && 
                    localValue as string != String.Empty)
                {
                    // if the value is not null AND is not an unset value AND is EITHER an
                    // Expression OR a non-empty string (the last condition covers both of these 
                    // possibilities) then we should serialize the text.
 
                    shouldSerialize = true; 
                }
            } 

            return shouldSerialize;
        }
 
        // ------------------------------------------------------------------
        // Decides if Inlines property needs to be serialized. 
        // ----------------------------------------------------------------- 
        /// 
        /// This method is used by TypeDescriptor to determine if this property should 
        /// be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeInlines(XamlDesignerSerializationManager manager) 
        {
            return (_complexContent != null) && (manager != null) && (manager.XmlWriter == null); 
        } 

        // ------------------------------------------------------------------ 
        // Do content alignment.
        // ------------------------------------------------------------------
        private void AlignContent()
        { 
            Debug.Assert(IsLayoutDataValid);
            Debug.Assert(CheckFlags(Flags.RequiresAlignment)); 
 
            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties(); 

            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);
            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);
 
            // Create / format all lines.
            // Since we are disposing line object, it can be reused to format following lines. 
            Line line = CreateLine(lineProperties); 
            TextRunCache textRunCache = new TextRunCache();
 
            int dcp = 0;
            double lineOffset = 0;
            int lineCount = LineCount;
            for (int i = 0; i < lineCount; i++) 
            {
Debug.Assert(lineCount == LineCount); 
                LineMetrics lineMetrics = GetLine(i); 

                using (line) 
                {
                    bool ellipsis = ParagraphEllipsisShownOnLine(i, lineOffset);
                    line.Format(dcp, wrappingWidth, GetLineProperties(dcp == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, ellipsis);
                    double lineHeight = CalcLineAdvance(line.Height, lineProperties); 

                    // Check consistency of line formatting 
                    MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of sync"); 
                    Debug.Assert(DoubleUtil.AreClose(lineHeight, lineMetrics.Height), "Line height is out of sync.");
 
                    // Calculated line width might be different from measure width in following cases:
                    // a) dynamically sized children, when FinalSize != AvailableSize
                    // b) non-default horizontal alignment, when FinalSize != AvailableSize
                    // Hence do not assert about matching line width with cached line metrics. 

                    lineMetrics = UpdateLine(i, lineMetrics, line.Start, line.Width); 
                    dcp += lineMetrics.Length; 
                    lineOffset += lineHeight;
                } 
            }
            SetFlags(false, Flags.RequiresAlignment);
        }
 
        // -----------------------------------------------------------------
        // OnRequestBringIntoView is called from the event handler TextBlock 
        // registers for the event. 
        // Handle the event for hosted ContentElements, and raise a new BringIntoView
        // event with the following values: 
        // * object: (this)
        // * rect: A rect indicating the position of the ContentElement
        //
        //      sender - The instance handling the event. 
        //      args   - RequestBringIntoViewEventArgs indicates the element
        //               and region to scroll into view. 
        // ------------------------------------------------------------------ 
        private static void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs args)
        { 
            TextBlock textBlock = sender as TextBlock;
            ContentElement child = args.TargetObject as ContentElement;

            if (textBlock != null && child != null) 
            {
                if (TextBlock.ContainsContentElement(textBlock, child)) 
                { 
                    // Handle original event.
                    args.Handled = true; 

                    // Retrieve the first rectangle representing the child and
                    // raise a new BrightIntoView event with such rectangle.
 
                    ReadOnlyCollection rects = textBlock.GetRectanglesCore(child);
                    Invariant.Assert(rects != null, "Rect collection cannot be null."); 
                    if (rects.Count > 0) 
                    {
                        textBlock.BringIntoView(rects[0]); 
                    }
                    else
                    {
                        textBlock.BringIntoView(); 
                    }
                } 
            } 
        }
 
        private static bool ContainsContentElement(TextBlock textBlock, ContentElement element)
        {
            if (textBlock._complexContent == null || !(textBlock._complexContent.TextContainer is TextContainer))
            { 
                return false;
            } 
            else if (element is TextElement) 
            {
                if (textBlock._complexContent.TextContainer != ((TextElement)element).TextContainer) 
                {
                    return false;
                }
                else 
                {
                    return true; 
                } 
            }
            return false; 
        }

        private int LineCount
        { 
            get
            { 
                if (CheckFlags(Flags.HasFirstLine)) 
                {
                    return (_subsequentLines == null) ? 1 : _subsequentLines.Count + 1; 
                }

                return 0;
            } 
        }
 
        private LineMetrics GetLine(int index) 
        {
            return (index == 0) ? _firstLine : _subsequentLines[index - 1]; 
        }

        private LineMetrics UpdateLine(int index, LineMetrics metrics, double start, double width)
        { 
            metrics = new LineMetrics(metrics, start, width);
 
            if (index == 0) 
            {
                _firstLine = metrics; 
            }
            else
            {
                _subsequentLines[index - 1] = metrics; 
            }
 
            return metrics; 
        }
 
        // -----------------------------------------------------------------
        // SetFlags is used to set or unset one or multiple flags.
        // -----------------------------------------------------------------
        private void SetFlags(bool value, Flags flags) 
        {
            _flags = value ? (_flags | flags) : (_flags & (~flags)); 
        } 

        // ----------------------------------------------------------------- 
        // CheckFlags returns true if all of passed flags in the bitmask are set.
        // ------------------------------------------------------------------
        private bool CheckFlags(Flags flags)
        { 
            return ((_flags & flags) == flags);
        } 
 
        // -----------------------------------------------------------------
        // Ensures none of our public (or textview) methods can be called during measure/arrange/content change. 
        // ------------------------------------------------------------------
        private void VerifyReentrancy()
        {
            if(CheckFlags(Flags.MeasureInProgress)) 
            {
                throw new InvalidOperationException(SR.Get(SRID.MeasureReentrancyInvalid)); 
            } 

            if(CheckFlags(Flags.ArrangeInProgress)) 
            {
                throw new InvalidOperationException(SR.Get(SRID.ArrangeReentrancyInvalid));
            }
 
            if(CheckFlags(Flags.ContentChangeInProgress))
            { 
                throw new InvalidOperationException(SR.Get(SRID.TextContainerChangingReentrancyInvalid)); 
            }
        } 

        /// 
        /// Returns index of the line that starts at the given dcp. Returns -1 if
        /// no line or the line metrics collection starts at the given dcp 
        /// 
        ///  
        /// Start dcp of required line 
        /// 
        private int GetLineIndexFromDcp(int dcpLine) 
        {
            Invariant.Assert(dcpLine >= 0);
            int lineIndex = 0;
            int lineStartOffset = 0; 

            int lineCount = LineCount; 
            while (lineIndex < lineCount) 
            {
Debug.Assert(lineCount == LineCount); 
                if (lineStartOffset == dcpLine)
                {
                    // Found line that starts at given dcp
                    return lineIndex; 
                }
                else 
                { 
                    lineStartOffset += GetLine(lineIndex).Length;
                    ++lineIndex; 
                }
            }

            // No line found starting at this position. Return -1. 
            // We should never hit this code
            Invariant.Assert(false, "Dcp passed is not at start of any line in TextBlock"); 
            return -1; 
        }
 
        // ------------------------------------------------------------------
        // IContentHost Helpers
        // -----------------------------------------------------------------
 
        /// 
        /// Searches for an element in the _complexContent.TextContainer. If the element is found, returns the 
        /// position at which it is found. Otherwise returns null. 
        /// 
        ///  
        /// Element to be found.
        /// 
        /// 
        /// We assume that this function is called from within text if the caller knows that _complexContent exists 
        /// and contains a TextContainer. Hence we assert for this condition within the function
        ///  
        private TextPointer FindElementPosition(IInputElement e) 
        {
            // Parameter validation 
            Debug.Assert(e != null);

            // Validate that this function is only called when a TextContainer exists as complex content
            Debug.Assert(_complexContent.TextContainer is TextContainer); 

            TextPointer position; 
 
            // If e is a TextElement we can optimize by checking its TextContainer
            if (e is TextElement) 
            {
                if ((e as TextElement).TextContainer == _complexContent.TextContainer)
                {
                    // Element found 
                    position = new TextPointer((e as TextElement).ElementStart);
                    return position; 
                } 
            }
 
            // Else: search for e in the complex content
            position = new TextPointer((TextPointer)_complexContent.TextContainer.Start);
            while (position.CompareTo((TextPointer)_complexContent.TextContainer.End) < 0)
            { 
                // Search each position in _complexContent.TextContainer for the element
                switch (position.GetPointerContext(LogicalDirection.Forward)) 
                { 
                    case TextPointerContext.EmbeddedElement:
                        DependencyObject embeddedObject = position.GetAdjacentElement(LogicalDirection.Forward); 
                        if (embeddedObject is ContentElement || embeddedObject is UIElement)
                        {
                            if (embeddedObject == e as ContentElement || embeddedObject == e as UIElement)
                            { 
                                return position;
                            } 
                        } 
                        break;
                    default: 
                          break;
                }
                position.MoveByOffset(+1);
            } 

            // Reached end of complex content without finding the element 
            return null; 
        }
 
        /// 
        /// Called when the child's BaselineOffset value changes.
        /// 
        internal void OnChildBaselineOffsetChanged(DependencyObject source) 
        {
            // Ignore this notification, if currently in the measure process. 
            if (!CheckFlags(Flags.MeasureInProgress)) 
            {
                // BaselineOffset,  may affect the 
                // size. Hence invalidate measure.
                // There is no need to invalidate TextRunCache, since TextFormatter
                // regets inline object information even if TextRunCache is clean.
                InvalidateMeasure(); 
                InvalidateVisual(); //ensure re-rendering
            } 
        } 

 
        /// 
        /// Property invalidator for baseline offset
        /// 
        /// Dependency Object that the property value is being changed on. 
        /// EventArgs that contains the old and new values for this property
        private static void OnBaselineOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        { 
            //Set up our baseline changed event
 
            //fire event!
            TextElement te = TextElement.ContainerTextElementField.GetValue(d);

            if (te != null) 
            {
                DependencyObject parent = te.TextContainer.Parent; 
                TextBlock tb = parent as TextBlock; 
                if (tb != null)
                { 
                    tb.OnChildBaselineOffsetChanged(d);
                }
                else
                { 
                    FlowDocument fd = parent as FlowDocument;
                    if (fd != null && d is UIElement) 
                    { 
                        fd.OnChildDesiredSizeChanged((UIElement)d);
                    } 
                }
            }
        }
 
        // ------------------------------------------------------------------
        // Setup event handlers. 
        // Deferred until the first measure. 
        // -----------------------------------------------------------------
        private void InitializeTextContainerListeners() 
        {
            _complexContent.TextContainer.Changing += new EventHandler(OnTextContainerChanging);
            _complexContent.TextContainer.Change += new TextContainerChangeEventHandler(OnTextContainerChange);
            _complexContent.Highlights.Changed += new HighlightChangedEventHandler(OnHighlightChanged); 
        }
 
        // ----------------------------------------------------------------- 
        // Clears out line metrics array, disposes as appropriate
        // ----------------------------------------------------------------- 
        private void ClearLineMetrics()
        {
            if (CheckFlags(Flags.HasFirstLine))
            { 
                if (_subsequentLines != null)
                { 
                    int subsequentLineCount = _subsequentLines.Count; 
                    for (int i = 0; i < subsequentLineCount; i++)
                    { 
                        _subsequentLines[i].Dispose(false);
                    }

                    _subsequentLines = null; 
                }
 
                _firstLine = _firstLine.Dispose(true); 
                SetFlags(false, Flags.HasFirstLine);
            } 
        }

        // ------------------------------------------------------------------
        // Ensures the hyphenator object is created 
        // -----------------------------------------------------------------
        private NaturalLanguageHyphenator EnsureHyphenator() 
        { 
            if (CheckFlags(Flags.IsHyphenatorSet))
            { 
                return HyphenatorField.GetValue(this);
            }

            // initialize hyphenator 
            NaturalLanguageHyphenator hyphenator = new NaturalLanguageHyphenator();
 
            HyphenatorField.SetValue(this, hyphenator); 
            SetFlags(true, Flags.IsHyphenatorSet);
            return hyphenator; 
        }

        private static bool IsValidTextTrimming(object o)
        { 
            TextTrimming value = (TextTrimming)o;
            return value == TextTrimming.CharacterEllipsis 
                || value == TextTrimming.None 
                || value == TextTrimming.WordEllipsis;
        } 

        private static bool IsValidTextWrap(object o)
        {
            TextWrapping value = (TextWrapping)o; 
            return value == TextWrapping.Wrap
                || value == TextWrapping.NoWrap 
                || value == TextWrapping.WrapWithOverflow; 
        }
 
        #endregion Private methods

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

        #region Private Fields 

        //-----------------------------------------------------------------
        // Simple content.
        //-------------------------------------------------------------------- 
        private TextBlockCache _textBlockCache;
 
        //----------------------------------------------------------------- 
        // Simple content.
        //------------------------------------------------------------------- 
        private string _contentCache;

        //-------------------------------------------------------------------
        // Complex content. 
        //--------------------------------------------------------------------
        private ComplexContent _complexContent; 
 
        //-------------------------------------------------------------------
        // Text formatter object. 
        //--------------------------------------------------------------------
        private TextFormatter _textFormatter;

        //-------------------------------------------------------------------- 
        // This size was the most recent constraint passed to MeasureOverride.
        // this.PreviousConstraintcan not be used because it can be affected 
        // by Margin, Width/Min/MaxWidth propreties and ClipToBounds... 
        //-------------------------------------------------------------------
        private Size _referenceSize; 

        //--------------------------------------------------------------------
        // This size was a result returned by MeasureOverride last time.
        // this.DesiredSize can not be used because it can be affected by Margin, 
        // Width/Min/MaxWidth propreties and ClipToBounds...
        //------------------------------------------------------------------- 
        private Size _previousDesiredSize; 

        //------------------------------------------------------------------- 
        // Distance from the top of the Element to its baseline.
        //-------------------------------------------------------------------
        private double _baselineOffset;
 
        //--------------------------------------------------------------------
        // Hyphenator -- uncommon field given usage 
        //------------------------------------------------------------------- 
        private  static readonly UncommonField HyphenatorField = new UncommonField();
 
        //--------------------------------------------------------------------
        // Collection of metrics of each line. For performance reasons, we
        // have separated out the first line from the array, since a single
        // line of text is a very common usage.  The LineCount, GetLine, 
        // and UpdateLine members are used to simplify access to this
        // divided data structure. 
        //-------------------------------------------------------------------- 
        private LineMetrics _firstLine;
        private List _subsequentLines; 

        //-------------------------------------------------------------------
        // Flags reflecting various aspects of TextBlock's state.
        //-------------------------------------------------------------------- 
        private Flags _flags;
        [System.Flags] 
        private enum Flags 
        {
            FormattedOnce           = 0x1,      // Element has been formatted at least once. 
            MeasureInProgress       = 0x2,      // Measure is in progress.
            TreeInReadOnlyMode      = 0x4,      // Tree (content) is in read only mode.
            RequiresAlignment       = 0x8,      // Content requires alignment process.
            ContentChangeInProgress = 0x10,     // Content change is in progress 
                                                //(it has been started, but is is not completed yet).
            IsContentPresenterContainer = 0x20, // Is this Text control being used by a ContentPresenter to host its content 
            HasParagraphEllipses    = 0x40,     // Has paragraph ellipses 
            PendingTextContainerEventInit = 0x80, // Needs TextContainer event hookup on next Measure call.
            ArrangeInProgress       = 0x100,      // Arrange is in progress. 
            IsTypographySet         = 0x200,      // Typography properties are not at default values
            TextContentChanging     = 0x400,    // TextProperty update in progress.
            IsHyphenatorSet         = 0x800,   // used to indicate when HyphenatorField has been set
            HasFirstLine            = 0x1000, 
        }
 
        #endregion Private Fields 
        //-------------------------------------------------------------------
        // 
        //  Private Types
        //
        //-------------------------------------------------------------------
 
        #region Private Types
 
        //------------------------------------------------------------------- 
        // Represents complex content.
        //-------------------------------------------------------------------- 
        private class ComplexContent
        {
            //---------------------------------------------------------------
            // Ctor 
            //----------------------------------------------------------------
            internal ComplexContent(TextBlock owner, ITextContainer textContainer, bool foreignTextContianer, string content) 
            { 
                // Paramaters validation
                Debug.Assert(owner != null); 
                Debug.Assert(textContainer != null);

                VisualChildren = new VisualCollection(owner);
 
                // Store TextContainer that contains content of the element.
                TextContainer = textContainer; 
                ForeignTextContainer = foreignTextContianer; 

                // Add content 
                if (content != null && content.Length > 0)
                {
                    TextBlock.InsertTextRun(this.TextContainer.End, content, /*whitespacesIgnorable:*/false);
                } 

                // Create TextView associated with TextContainer. 
                this.TextView = new TextParagraphView(owner, TextContainer); 

                // Hookup TextContainer to TextView. 
                this.TextContainer.TextView = this.TextView;
            }

            //---------------------------------------------------------------- 
            // Detach event handlers.
            //--------------------------------------------------------------- 
            internal void Detach(TextBlock owner) 
            {
                this.Highlights.Changed -= new HighlightChangedEventHandler(owner.OnHighlightChanged); 
                this.TextContainer.Changing -= new EventHandler(owner.OnTextContainerChanging);
                this.TextContainer.Change -= new TextContainerChangeEventHandler(owner.OnTextContainerChange);
            }
 
            //------------------------------------------------------------------
            // Internal Visual Children. 
            //------------------------------------------------------------------- 
            internal VisualCollection VisualChildren;
 
            //---------------------------------------------------------------
            // Highlights associated with TextContainer.
            //---------------------------------------------------------------
            internal Highlights Highlights { get { return this.TextContainer.Highlights; } } 

            //---------------------------------------------------------------- 
            // Text array exposing access to the content. 
            //---------------------------------------------------------------
            internal readonly ITextContainer TextContainer; 

            //----------------------------------------------------------------
            // Is TextContainer owned by another object?
            //---------------------------------------------------------------- 
            internal readonly bool ForeignTextContainer;
 
            //--------------------------------------------------------------- 
            // TextView object associated with TextContainer.
            //---------------------------------------------------------------- 
            internal readonly TextParagraphView TextView;

            //---------------------------------------------------------------
            // Collection of inline objects hosted by the TextBlock control. 
            //---------------------------------------------------------------
            internal ArrayList InlineObjects; 
        } 

        //------------------------------------------------------------------- 
        // Simple content enumerator.
        //--------------------------------------------------------------------
        private class SimpleContentEnumerator : IEnumerator
        { 
            //---------------------------------------------------------------
            // Ctor 
            //---------------------------------------------------------------- 
            internal SimpleContentEnumerator(string content)
            { 
                _content = content;
                _initialized = false;
                _invalidPosition = false;
            } 

            //---------------------------------------------------------------- 
            // Sets the enumerator to its initial position, which is before 
            // the first element in the collection.
            //--------------------------------------------------------------- 
            void IEnumerator.Reset()
            {
                _initialized = false;
                _invalidPosition = false; 
            }
 
            //---------------------------------------------------------------- 
            // Advances the enumerator to the next element of the collection.
            //--------------------------------------------------------------- 
            bool IEnumerator.MoveNext()
            {
                if (!_initialized)
                { 
                    _initialized = true;
                    return true; 
                } 
                else
                { 
                    _invalidPosition = true;
                    return false;
                }
            } 

            //--------------------------------------------------------------- 
            // Gets the current element in the collection. 
            //---------------------------------------------------------------
            object IEnumerator.Current 
            {
                get
                {
                    if (!_initialized || _invalidPosition) 
                    {
                        throw new InvalidOperationException(); 
                    } 
                    return _content;
                } 
            }

            //----------------------------------------------------------------
            // Content. 
            //---------------------------------------------------------------
            private readonly string _content; 
            private bool _initialized; 
            private bool _invalidPosition;
        } 

        #endregion Private Types

        //-------------------------------------------------------------------- 
        //
        //  Dependency Property Helpers 
        // 
        //--------------------------------------------------------------------
 
        #region Dependency Property Helpers

        private static object CoerceBaselineOffset(DependencyObject d, object value)
        { 
            TextBlock tb = (TextBlock) d;
 
            if(DoubleUtil.IsNaN((double) value)) 
            {
                return tb._baselineOffset; 
            }

            return value;
        } 

        ///  
        /// Returns true if we should serialize the BaselineOffset property. 
        /// 
        /// True if we should serialize the BaselineOffset property. 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeBaselineOffset()
        {
            object localBaseline = ReadLocalValue(BaselineOffsetProperty); 
            return (localBaseline != DependencyProperty.UnsetValue) && !DoubleUtil.IsNaN((double) localBaseline);
        } 
 
        //-------------------------------------------------------------------
        // Text helpers 
        //--------------------------------------------------------------------

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            OnTextChanged(d, (string)e.NewValue);
        } 
 
        private static void OnTextChanged(DependencyObject d, string newText)
        { 
            TextBlock text = (TextBlock) d;

            if (text.CheckFlags(Flags.TextContentChanging))
            { 
                // The update originated in a TextContainer change -- don't update
                // the TextContainer a second time. 
                return; 
            }
 
            if (text._complexContent == null)
            {
                text._contentCache = (newText != null) ? newText : String.Empty;
            } 
            else
            { 
                text.SetFlags(true, Flags.TextContentChanging); 
                try
                { 
                    bool exceptionThrown = true;

                    Invariant.Assert(text._contentCache == null, "Content cache should be null when complex content exists.");
 
                    text._complexContent.TextContainer.BeginChange();
                    try 
                    { 
                        ((TextContainer)text._complexContent.TextContainer).DeleteContentInternal((TextPointer)text._complexContent.TextContainer.Start, (TextPointer)text._complexContent.TextContainer.End);
                        InsertTextRun(text._complexContent.TextContainer.End, newText, /*whitespacesIgnorable:*/true); 
                        exceptionThrown = false;
                    }
                    finally
                    { 
                        text._complexContent.TextContainer.EndChange();
 
                        if (exceptionThrown) 
                        {
                            text.ClearLineMetrics(); 
                        }
                    }
                }
                finally 
                {
                    text.SetFlags(false, Flags.TextContentChanging); 
                } 
            }
        } 

        //
        //  This property
        //  1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject 
        //  2. This is a performance optimization
        // 
        internal override int EffectiveValuesInitialSize 
        {
            get { return 28; } 
        }

        #endregion Dependency Property Helpers
    } 
}
 
 
// Disable pragma warnings to enable PREsharp pragmas
#pragma warning restore 1634, 1691 



// 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