File: System\Windows\Documents\TextElement.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
// Description: Base class for content in text based FrameworkElement.
//
 
// Enable presharp pragma warning suppress directives.
#pragma warning disable 1634, 1691
 
using System.Collections;
using System.ComponentModel;
using System.Windows.Markup;
using System.Windows.Media;
using MS.Internal;
using MS.Internal.PresentationFramework;
using MS.Internal.PtsHost.UnsafeNativeMethods; // PTS restrictions
using MS.Internal.Text;
 
namespace System.Windows.Documents
{
    /// <summary>
    /// TextElement is an  base class for content in text based FrameworkElement
    /// controls such as Text, FlowDocument, or RichTextBox.  TextElements span
    /// other content, applying property values or providing structural information.
    /// </summary>
    /// <remarks>
    /// </remarks>
    public abstract class TextElement : FrameworkContentElement, IAddChild
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        static TextElement()
        {
            // For attached properties metadata specific to the type needs to be set using OverrideMetadata
            // instead of passing it during property registration. Otherwise all types will get it.
            PropertyChangedCallback typographyChanged = new PropertyChangedCallback(OnTypographyChanged);
 
            // Registering typography properties metadata
            DependencyProperty[] typographyProperties = Typography.TypographyPropertiesList;
            for (int i = 0; i < typographyProperties.Length; i++)
            {
                typographyProperties[i].OverrideMetadata(typeof(TextElement), new FrameworkPropertyMetadata(typographyChanged));
            }
        }
 
        /// <summary>
        /// Internal constructor to prevent publicly derived classes.
        /// </summary>
        internal TextElement() : base()
        {
        }
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
 
        /// <summary>
        /// Extracts this TextElement from its current position, if any, and
        /// inserts it at a specified location.
        /// </summary>
        /// <param name="start">
        /// New start position.
        /// </param>
        /// <param name="end">
        /// New end position.
        /// </param>
        /// <exception cref="System.ArgumentException">
        /// Throws an ArgumentException if start and end are not
        /// positioned within the same document, or if start is positioned
        /// after end, or if start == null but end != null.
        /// </exception>
        /// <remarks>
        /// This method extracts the TextElement from its current position,
        /// leaving behind any contained content, before inserting the TextElement
        /// at a new location.
        ///
        /// If start is null, end must also be null, and the TextElement will
        /// not be inserted into a new document.
        /// </remarks>
        internal void Reposition(TextPointer start, TextPointer end)
        {
            TextContainer tree;
 
            if (start != null)
            {
                ValidationHelper.VerifyPositionPair(start, end);
            }
            else if (end != null)
            {
                throw new ArgumentException(SR.TextElement_UnmatchedEndPointer);
            }
 
            if (start != null)
            {
                // start/end must be equally scoped.  But we want to discount
                // this TextElement when considering scoping -- it will be
                // extracted before the final insert.
 
                SplayTreeNode startNode = start.GetScopingNode();
                // Suppress presharp 6506: Parameter 'end' to this public method must be validated.
                // We already validated it indirectly above, when calling ValidationHelper.VerifyPositionPair.
                #pragma warning suppress 6506
                SplayTreeNode endNode = end.GetScopingNode();
 
                if (startNode == _textElementNode)
                {
                    startNode = _textElementNode.GetContainingNode();
                }
                if (endNode == _textElementNode)
                {
                    endNode = _textElementNode.GetContainingNode();
                }
 
                if (startNode != endNode)
                {
                    throw new ArgumentException(SR.Format(SR.InDifferentScope, "start", "end"));
                }
            }
 
            if (this.IsInTree)
            {
                tree = EnsureTextContainer();
 
                if (start == null)
                {
                    //
                    // Case 0: Extract this element from its tree.
                    //
 
                    tree.BeginChange();
                    try
                    {
                        tree.ExtractElementInternal(this);
                    }
                    finally
                    {
                        tree.EndChange();
                    }
                }
                else
                {
                    // Presharp doesn't understand that by design TextPointer.TextContainer can never be null.
                    #pragma warning suppress 6506
                    if (tree == start.TextContainer)
                    {
                        //
                        // Case 1: extract and insert this TextElement within the same tree.
                        //
 
                        tree.BeginChange();
                        try
                        {
                            tree.ExtractElementInternal(this);
                            tree.InsertElementInternal(start, end, this);
                        }
                        finally
                        {
                            tree.EndChange();
                        }
                    }
                    else
                    {
                        //
                        // Case 2: extract and insert this TextElement from one tree to another tree.
                        //
 
                        tree.BeginChange();
                        try
                        {
                            tree.ExtractElementInternal(this);
                        }
                        finally
                        {
                            tree.EndChange();
                        }
 
                        // Presharp doesn't understand that by design TextPointer.TextContainer can never be null.
                        #pragma warning suppress 56506
                        start.TextContainer.BeginChange();
                        try
                        {
                            start.TextContainer.InsertElementInternal(start, end, this);
                        }
                        finally
                        {
                            start.TextContainer.EndChange();
                        }
                    }
                }
            }
            else if (start != null)
            {
                //
                // Case 3: insert this TextElement to a new tree (this is no current tree).
                //
 
                start.TextContainer.BeginChange();
                try
                {
                    start.TextContainer.InsertElementInternal(start, end, this);
                }
                finally
                {
                    start.TextContainer.EndChange();
                }
            }
        }
 
        /// <summary>
        /// Extracts this TextElement and its content from its current
        /// position, if any, and inserts it at a specified location.
        /// </summary>
        /// <param name="textPosition">
        /// New position.
        /// </param>
        /// <remarks>
        /// This method extracts the TextElement from its current position,
        /// including any contained content, before inserting the TextElement
        /// at a new location.
        ///
        /// If textPosition is null, the TextElement will not be inserted into
        /// a new document.
        /// </remarks>
        internal void RepositionWithContent(TextPointer textPosition)
        {
            TextContainer tree;
 
            if (textPosition == null)
            {
                if (this.IsInTree)
                {
                    tree = EnsureTextContainer();
 
                    // Presharp doesn't understand that by design EnsureTextContainer can never return null.
                    #pragma warning suppress 6506
                    tree.BeginChange();
                    try
                    {
                        tree.DeleteContentInternal(this.ElementStart, this.ElementEnd);
                    }
                    finally
                    {
                        tree.EndChange();
                    }
                }
            }
            else
            {
                tree = textPosition.TextContainer;
 
                // Presharp doesn't understand that by design TextPointer.TextContainer can never be null.
                #pragma warning suppress 56506
                tree.BeginChange();
                try
                {
                    tree.InsertElementInternal(textPosition, textPosition, this);
                }
                finally
                {
                    tree.EndChange();
                }
            }
        }
 
        #endregion Public Methods
 
        //------------------------------------------------------
        //
        //  Public Properties
        //
        //------------------------------------------------------
 
        #region Public Properties
 
        internal static readonly UncommonField<TextElement> ContainerTextElementField = new UncommonField<TextElement>();
 
        /// <summary>
        /// A TextRange spanning the content of this element.
        /// </summary>
        internal TextRange TextRange
        {
            get
            {
                VerifyAccess();
 
                TextContainer tree = EnsureTextContainer();
 
                TextPointer contentStart = new TextPointer(tree, _textElementNode, ElementEdge.AfterStart, LogicalDirection.Backward);
                contentStart.Freeze();
 
                TextPointer contentEnd = new TextPointer(tree, _textElementNode, ElementEdge.BeforeEnd, LogicalDirection.Forward);
                contentEnd.Freeze();
 
                return new TextRange(contentStart, contentEnd);
            }
        }
 
        /// <summary>
        /// A TextPointer located just before the start edge of this TextElement.
        /// </summary>
        /// <Remarks>
        /// The TextPointer returned always has its IsFrozen property set true
        /// and LogicalDirection set Forward.
        /// </Remarks>
        public TextPointer ElementStart
        {
            get
            {
                TextContainer tree;
                TextPointer elementStart;
 
                tree = EnsureTextContainer();
 
                elementStart = new TextPointer(tree, _textElementNode, ElementEdge.BeforeStart, LogicalDirection.Forward);
                elementStart.Freeze();
 
                return elementStart;
            }
        }
 
        internal StaticTextPointer StaticElementStart
        {
            get
            {
                TextContainer tree = EnsureTextContainer();
 
                return new StaticTextPointer(tree, _textElementNode, 0);
            }
        }
 
        /// <summary>
        /// A TextPointer located just past the start edge of this TextElement.
        /// </summary>
        /// <Remarks>
        /// The TextPointer returned always has its IsFrozen property set true
        /// and LogicalDirection set Backward.
        /// </Remarks>
        public TextPointer ContentStart
        {
            get
            {
                TextContainer tree;
                TextPointer contentStart;
 
                tree = EnsureTextContainer();
 
                contentStart = new TextPointer(tree, _textElementNode, ElementEdge.AfterStart, LogicalDirection.Backward);
                contentStart.Freeze();
 
                return contentStart;
            }
        }
 
        internal StaticTextPointer StaticContentStart
        {
            get
            {
                TextContainer tree = EnsureTextContainer();
 
                return new StaticTextPointer(tree, _textElementNode, 1);
            }
        }
 
        /// <summary>
        /// A TextPointer located just before the end edge of this TextElement.
        /// </summary>
        /// <Remarks>
        /// The TextPointer returned always has its IsFrozen property set true
        /// and LogicalDirection set Forward.
        /// </Remarks>
        public TextPointer ContentEnd
        {
            get
            {
                TextContainer tree;
                TextPointer contentEnd;
 
                tree = EnsureTextContainer();
 
                contentEnd = new TextPointer(tree, _textElementNode, ElementEdge.BeforeEnd, LogicalDirection.Forward);
                contentEnd.Freeze();
 
                return contentEnd;
            }
        }
 
        internal StaticTextPointer StaticContentEnd
        {
            get
            {
                TextContainer tree = EnsureTextContainer();
 
                return new StaticTextPointer(tree, _textElementNode, _textElementNode.SymbolCount - 1);
            }
        }
 
        // Returns true if a position belongs to inner part of this TextElement (ContentStart and ContentEnd including).
        internal bool Contains(TextPointer position)
        {
            TextContainer tree = EnsureTextContainer();
            ValidationHelper.VerifyPosition(tree, position);
            return this.ContentStart.CompareTo(position) <= 0 && this.ContentEnd.CompareTo(position) >= 0;
        }
 
        /// <summary>
        /// A TextPointer located just after the end edge of this TextElement.
        /// </summary>
        /// <Remarks>
        /// The TextPointer returned always has its IsFrozen property set true
        /// and LogicalDirection set Backward.
        /// </Remarks>
        public TextPointer ElementEnd
        {
            get
            {
                TextContainer tree;
                TextPointer elementEnd;
 
                tree = EnsureTextContainer();
 
                elementEnd = new TextPointer(tree, _textElementNode, ElementEdge.AfterEnd, LogicalDirection.Backward);
                elementEnd.Freeze();
 
                return elementEnd;
            }
        }
 
        internal StaticTextPointer StaticElementEnd
        {
            get
            {
                TextContainer tree = EnsureTextContainer();
 
                return new StaticTextPointer(tree, _textElementNode, _textElementNode.SymbolCount);
            }
        }
 
        #region DependencyProperties
 
        /// <summary>
        /// DependencyProperty for <see cref="FontFamily" /> property.
        /// </summary>
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontFamilyProperty =
                DependencyProperty.RegisterAttached(
                        "FontFamily",
                        typeof(FontFamily),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata(
                                SystemFonts.MessageFontFamily,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits),
                        new ValidateValueCallback(IsValidFontFamily));
 
        /// <summary>
        /// The FontFamily property specifies the name of font family.
        /// </summary>
        [Localizability(
            LocalizationCategory.Font,
            Modifiability = Modifiability.Unmodifiable
        )]
        public FontFamily FontFamily
        {
            get { return (FontFamily) GetValue(FontFamilyProperty); }
            set { SetValue(FontFamilyProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty setter for <see cref="FontFamily" /> property.
        /// </summary>
        /// <param name="element">The element to which to write the attached property.</param>
        /// <param name="value">The property value to set</param>
        public static void SetFontFamily(DependencyObject element, FontFamily value)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            element.SetValue(FontFamilyProperty, value);
        }
 
        /// <summary>
        /// DependencyProperty getter for <see cref="FontFamily" /> property.
        /// </summary>
        /// <param name="element">The element from which to read the attached property.</param>
        public static FontFamily GetFontFamily(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return (FontFamily)element.GetValue(FontFamilyProperty);
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="FontStyle" /> property.
        /// </summary>
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontStyleProperty =
                DependencyProperty.RegisterAttached(
                        "FontStyle",
                        typeof(FontStyle),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata(
                                SystemFonts.MessageFontStyle,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
 
        /// <summary>
        /// The FontStyle property requests normal, italic, and oblique faces within a font family.
        /// </summary>
        public FontStyle FontStyle
        {
            get { return (FontStyle) GetValue(FontStyleProperty); }
            set { SetValue(FontStyleProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty setter for <see cref="FontStyle" /> property.
        /// </summary>
        /// <param name="element">The element to which to write the attached property.</param>
        /// <param name="value">The property value to set</param>
        public static void SetFontStyle(DependencyObject element, FontStyle value)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            element.SetValue(FontStyleProperty, value);
        }
 
        /// <summary>
        /// DependencyProperty getter for <see cref="FontStyle" /> property.
        /// </summary>
        /// <param name="element">The element from which to read the attached property.</param>
        public static FontStyle GetFontStyle(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return (FontStyle)element.GetValue(FontStyleProperty);
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="FontWeight" /> property.
        /// </summary>
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontWeightProperty =
                DependencyProperty.RegisterAttached(
                        "FontWeight",
                        typeof(FontWeight),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata(
                                SystemFonts.MessageFontWeight,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
 
        /// <summary>
        /// The FontWeight property specifies the weight of the font.
        /// </summary>
        public FontWeight FontWeight
        {
            get { return (FontWeight) GetValue(FontWeightProperty); }
            set { SetValue(FontWeightProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty setter for <see cref="FontWeight" /> property.
        /// </summary>
        /// <param name="element">The element to which to write the attached property.</param>
        /// <param name="value">The property value to set</param>
        public static void SetFontWeight(DependencyObject element, FontWeight value)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            element.SetValue(FontWeightProperty, value);
        }
 
        /// <summary>
        /// DependencyProperty getter for <see cref="FontWeight" /> property.
        /// </summary>
        /// <param name="element">The element from which to read the attached property.</param>
        public static FontWeight GetFontWeight(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return (FontWeight)element.GetValue(FontWeightProperty);
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="FontStretch" /> property.
        /// </summary>
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontStretchProperty =
                DependencyProperty.RegisterAttached(
                        "FontStretch",
                        typeof(FontStretch),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata(
                                FontStretches.Normal,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
 
        /// <summary>
        /// The FontStretch property selects a normal, condensed, or extended face from a font family.
        /// </summary>
        public FontStretch FontStretch
        {
            get { return (FontStretch) GetValue(FontStretchProperty); }
            set { SetValue(FontStretchProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty setter for <see cref="FontStretch" /> property.
        /// </summary>
        /// <param name="element">The element to which to write the attached property.</param>
        /// <param name="value">The property value to set</param>
        public static void SetFontStretch(DependencyObject element, FontStretch value)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            element.SetValue(FontStretchProperty, value);
        }
 
        /// <summary>
        /// DependencyProperty getter for <see cref="FontStretch" /> property.
        /// </summary>
        /// <param name="element">The element from which to read the attached property.</param>
        public static FontStretch GetFontStretch(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return (FontStretch)element.GetValue(FontStretchProperty);
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="FontSize" /> property.
        /// </summary>
        [CommonDependencyProperty]
        public static readonly DependencyProperty FontSizeProperty =
                DependencyProperty.RegisterAttached(
                        "FontSize",
                        typeof(double),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata(
                                SystemFonts.ThemeMessageFontSize,
                                FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits),
                        new ValidateValueCallback(IsValidFontSize));
 
        /// <summary>
        /// The FontSize property specifies the size of the font.
        /// </summary>
        [TypeConverter(typeof(FontSizeConverter))]
        [Localizability(LocalizationCategory.None)]
        public double FontSize
        {
            get { return (double) GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty setter for <see cref="FontSize" /> property.
        /// </summary>
        /// <param name="element">The element to which to write the attached property.</param>
        /// <param name="value">The property value to set</param>
        public static void SetFontSize(DependencyObject element, double value)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            element.SetValue(FontSizeProperty, value);
        }
 
        /// <summary>
        /// DependencyProperty getter for <see cref="FontSize" /> property.
        /// </summary>
        /// <param name="element">The element from which to read the attached property.</param>
        [TypeConverter(typeof(FontSizeConverter))]
        public static double GetFontSize(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return (double)element.GetValue(FontSizeProperty);
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="Foreground" /> property.
        /// </summary>
        [CommonDependencyProperty]
        public static readonly DependencyProperty ForegroundProperty =
                DependencyProperty.RegisterAttached(
                        "Foreground",
                        typeof(Brush),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata(
                                Brushes.Black,
                                FrameworkPropertyMetadataOptions.AffectsRender |
                                FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender |
                                FrameworkPropertyMetadataOptions.Inherits));
 
        /// <summary>
        /// The Foreground property specifies the foreground brush of an element's text content.
        /// </summary>
        public Brush Foreground
        {
            get { return (Brush) GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty setter for <see cref="Foreground" /> property.
        /// </summary>
        /// <param name="element">The element to which to write the attached property.</param>
        /// <param name="value">The property value to set</param>
        public static void SetForeground(DependencyObject element, Brush value)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            element.SetValue(ForegroundProperty, value);
        }
 
        /// <summary>
        /// DependencyProperty getter for <see cref="Foreground" /> property.
        /// </summary>
        /// <param name="element">The element from which to read the attached property.</param>
        public static Brush GetForeground(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return (Brush)element.GetValue(ForegroundProperty);
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="Background" /> property.
        /// </summary>
        [CommonDependencyProperty]
        public static readonly DependencyProperty BackgroundProperty =
                DependencyProperty.Register("Background",
                        typeof(Brush),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata((Brush)null,
                                FrameworkPropertyMetadataOptions.AffectsRender));
 
        /// <summary>
        /// The Background property defines the brush used to fill the content area.
        /// </summary>
        public Brush Background
        {
            get { return (Brush) GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="TextEffectCollection" /> property.
        /// It doesn't affect layout
        /// </summary>
        public static readonly DependencyProperty TextEffectsProperty =
                DependencyProperty.Register(
                        "TextEffects",
                        typeof(TextEffectCollection),
                        typeof(TextElement),
                        new FrameworkPropertyMetadata(
                                new FreezableDefaultValueFactory(TextEffectCollection.Empty),
                                FrameworkPropertyMetadataOptions.AffectsRender));
 
        /// <summary>
        /// The TextEffects property specifies effects that are added to the text of an element.
        /// </summary>
        public TextEffectCollection TextEffects
        {
            get { return (TextEffectCollection) GetValue(TextEffectsProperty); }
            set { SetValue(TextEffectsProperty, value); }
        }
 
        /// <summary>
        /// Class providing access to all text typography properties
        /// </summary>
        public Typography Typography
        {
            get
            {
                return new Typography(this);
            }
        }
 
        #endregion DependencyProperties
 
        #region IAddChild
 
        ///<summary>
        /// Called to add the object as a child.
        ///</summary>
        ///<param name="value">
        /// A Block to add as a child.
        ///</param>
        /// <exception cref="System.ArgumentException">
        /// o must derive from either UIElement or TextElement, or an
        /// ArgumentException will be thrown by this method.
        /// </exception>
        void IAddChild.AddChild(object value)
        {
            Type valueType = value.GetType();
 
            TextElement te = value as TextElement;
 
            if (te != null)
            {
                TextSchema.ValidateChild(/*parent:*/this, /*child:*/te, true /* throwIfIllegalChild */, true /* throwIfIllegalHyperlinkDescendent */);
                Append(te);
            }
            else
            {
                UIElement uie = value as UIElement;
                if (uie != null)
                {
                    InlineUIContainer inlineContainer = this as InlineUIContainer;
                    if (inlineContainer != null)
                    {
                        if (inlineContainer.Child != null)
                        {
                            throw new ArgumentException(SR.Format(SR.TextSchema_ThisInlineUIContainerHasAChildUIElementAlready, this.GetType().Name, ((InlineUIContainer)this).Child.GetType().Name, value.GetType().Name));
                        }
 
                        inlineContainer.Child = uie;
                    }
                    else
                    {
                        BlockUIContainer blockContainer = this as BlockUIContainer;
                        if (blockContainer != null)
                        {
                            if (blockContainer.Child != null)
                            {
                                throw new ArgumentException(SR.Format(SR.TextSchema_ThisBlockUIContainerHasAChildUIElementAlready, this.GetType().Name, ((BlockUIContainer)this).Child.GetType().Name, value.GetType().Name));
                            }
 
                            blockContainer.Child = uie;
                        }
                        else
                        {
                            if (TextSchema.IsValidChild(/*parent:*/this, /*childType:*/typeof(InlineUIContainer)))
                            {
                                // Create implicit InlineUIContainer wrapper for this UIElement
                                InlineUIContainer implicitInlineUIContainer = Inline.CreateImplicitInlineUIContainer(this);
                                Append(implicitInlineUIContainer);
                                implicitInlineUIContainer.Child = uie;
                            }
                            else
                            {
                                throw new ArgumentException(SR.Format(SR.TextSchema_ChildTypeIsInvalid, this.GetType().Name, value.GetType().Name));
                            }
                        }
                    }
                }
                else
                {
                    throw new ArgumentException(SR.Format(SR.TextSchema_ChildTypeIsInvalid, this.GetType().Name, value.GetType().Name));
                }
            }
        }
 
        ///<summary>
        /// Called when text appears under the tag in markup
        ///</summary>
        ///<param name="text">
        /// Text to Add to this TextElement
        ///</param>
        /// <ExternalAPI/>
        void IAddChild.AddText(string text)
        {
            ArgumentNullException.ThrowIfNull(text);
 
            // Check if text run is allowed in this element,
            // and create implicit Run if possible.
            if (TextSchema.IsValidChild(/*parent:*/this, /*childType:*/typeof(string)))
            {
                Append(text);
            }
            else
            {
                // Implicit Run creation
                if (TextSchema.IsValidChild(/*parent:*/this, /*childType:*/typeof(Run)))
                {
                    // NOTE: Do not use new Run(text) constructor to avoid TextContainer creation
                    // which would hit parser perf
                    Run implicitRun = Inline.CreateImplicitRun(this);
 
                    Append(implicitRun);
 
                    implicitRun.Text = text;
                }
                else
                {
                    // Otherwise text is not allowed. Throw if it is not a whitespace
                    if (text.Trim().Length > 0)
                    {
                        throw new InvalidOperationException(SR.Format(SR.TextSchema_TextIsNotAllowed, this.GetType().Name));
                    }
 
                    // As to whitespace - it can be simply ignored
                }
            }
        }
 
        #endregion IAddChild
 
        #region LogicalTree
 
        /// <summary>
        /// Returns enumerator to logical children.
        /// </summary>
        protected internal override IEnumerator LogicalChildren
        {
            get
            {
                return this.IsEmpty
                    ? new RangeContentEnumerator(null, null)
                    : new RangeContentEnumerator(this.ContentStart, this.ContentEnd);
            }
        }
 
        #endregion LogicalTree
 
        #endregion Public Properties
 
        //------------------------------------------------------
        //
        //  Protected Methods
        //
        //------------------------------------------------------
 
        #region Protected Methods
 
        /// <summary>
        /// Notification that a specified property has been invalidated
        /// </summary>
        /// <param name="e">EventArgs that contains the property, metadata, old value, and new value for this change</param>
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            // Always call base.OnPropertyChanged, otherwise Property Engine will not work.
            base.OnPropertyChanged(e);
 
            // Note whether or not this change due to a SetValue/ClearValue call.
            bool localValueChanged = e.NewValueSource == BaseValueSourceInternal.Local || e.OldValueSource == BaseValueSourceInternal.Local;
 
            if (localValueChanged || e.IsAValueChange || e.IsASubPropertyChange)
            {
                if (this.IsInTree) // No work to do if no one's listening.
                {
                    // If the modified property affects layout we have some additional
                    // bookkeeping to take care of.
                    FrameworkPropertyMetadata fmetadata = e.Metadata as FrameworkPropertyMetadata;
                    if (fmetadata != null)
                    {
                        bool affectsMeasureOrArrange = fmetadata.AffectsMeasure || fmetadata.AffectsArrange || fmetadata.AffectsParentMeasure || fmetadata.AffectsParentArrange;
                        bool affectsRender = (fmetadata.AffectsRender &&
                            (e.IsAValueChange || !fmetadata.SubPropertiesDoNotAffectRender));
                        if (affectsMeasureOrArrange || affectsRender)
                        {
                            TextContainer textContainer = EnsureTextContainer();
 
                            textContainer.BeginChange();
                            try
                            {
                                if (localValueChanged)
                                {
                                    TextTreeUndo.CreatePropertyUndoUnit(this, e);
                                }
 
                                if (e.IsAValueChange || e.IsASubPropertyChange)
                                {
                                    NotifyTypographicPropertyChanged(affectsMeasureOrArrange, localValueChanged, e.Property);
                                }
                            }
                            finally
                            {
                                textContainer.EndChange();
                            }
                        }
                    }
                }
            }
        }
 
        // Notify our TextContainer that a typographic property has changed
        // value on this TextElement.
        // This has the side effect of invalidating layout.
        internal void NotifyTypographicPropertyChanged(bool affectsMeasureOrArrange, bool localValueChanged, DependencyProperty property)
        {
            if (!this.IsInTree) // No work to do if no one's listening.
            {
                return;
            }
 
            TextContainer tree;
            TextPointer beforeStart;
 
            tree = EnsureTextContainer();
 
            // Take note that something layout related has changed.
            tree.NextLayoutGeneration();
 
            // Notify any external listeners.
            if (tree.HasListeners)
            {
                // Get the position before the start of this element.
                beforeStart = new TextPointer(tree, _textElementNode, ElementEdge.BeforeStart, LogicalDirection.Forward);
                beforeStart.Freeze();
 
                // Raise ContentAffected event that spans entire TextElement (from BeforeStart to AfterEnd).
                tree.BeginChange();
                try
                {
                    tree.BeforeAddChange();
                    if (localValueChanged)
                    {
                        tree.AddLocalValueChange();
                    }
                    tree.AddChange(beforeStart, _textElementNode.SymbolCount, _textElementNode.IMECharCount,
                        PrecursorTextChangeType.PropertyModified, property, !affectsMeasureOrArrange);
                }
                finally
                {
                    tree.EndChange();
                }
            }
        }
 
        #endregion Protected Events
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        internal static TypographyProperties GetTypographyProperties(DependencyObject element)
        {
            TypographyProperties group = new TypographyProperties();
 
            group.SetStandardLigatures((bool) element.GetValue(Typography.StandardLigaturesProperty));
            group.SetContextualLigatures((bool) element.GetValue(Typography.ContextualLigaturesProperty));
            group.SetDiscretionaryLigatures((bool) element.GetValue(Typography.DiscretionaryLigaturesProperty));
            group.SetHistoricalLigatures((bool) element.GetValue(Typography.HistoricalLigaturesProperty));
            group.SetAnnotationAlternates((int) element.GetValue(Typography.AnnotationAlternatesProperty));
            group.SetContextualAlternates((bool) element.GetValue(Typography.ContextualAlternatesProperty));
            group.SetHistoricalForms((bool) element.GetValue(Typography.HistoricalFormsProperty));
            group.SetKerning((bool) element.GetValue(Typography.KerningProperty));
            group.SetCapitalSpacing((bool) element.GetValue(Typography.CapitalSpacingProperty));
            group.SetCaseSensitiveForms((bool) element.GetValue(Typography.CaseSensitiveFormsProperty));
            group.SetStylisticSet1((bool) element.GetValue(Typography.StylisticSet1Property));
            group.SetStylisticSet2((bool) element.GetValue(Typography.StylisticSet2Property));
            group.SetStylisticSet3((bool) element.GetValue(Typography.StylisticSet3Property));
            group.SetStylisticSet4((bool) element.GetValue(Typography.StylisticSet4Property));
            group.SetStylisticSet5((bool) element.GetValue(Typography.StylisticSet5Property));
            group.SetStylisticSet6((bool) element.GetValue(Typography.StylisticSet6Property));
            group.SetStylisticSet7((bool) element.GetValue(Typography.StylisticSet7Property));
            group.SetStylisticSet8((bool) element.GetValue(Typography.StylisticSet8Property));
            group.SetStylisticSet9((bool) element.GetValue(Typography.StylisticSet9Property));
            group.SetStylisticSet10((bool) element.GetValue(Typography.StylisticSet10Property));
            group.SetStylisticSet11((bool) element.GetValue(Typography.StylisticSet11Property));
            group.SetStylisticSet12((bool) element.GetValue(Typography.StylisticSet12Property));
            group.SetStylisticSet13((bool) element.GetValue(Typography.StylisticSet13Property));
            group.SetStylisticSet14((bool) element.GetValue(Typography.StylisticSet14Property));
            group.SetStylisticSet15((bool) element.GetValue(Typography.StylisticSet15Property));
            group.SetStylisticSet16((bool) element.GetValue(Typography.StylisticSet16Property));
            group.SetStylisticSet17((bool) element.GetValue(Typography.StylisticSet17Property));
            group.SetStylisticSet18((bool) element.GetValue(Typography.StylisticSet18Property));
            group.SetStylisticSet19((bool) element.GetValue(Typography.StylisticSet19Property));
            group.SetStylisticSet20((bool) element.GetValue(Typography.StylisticSet20Property));
            group.SetFraction((FontFraction) element.GetValue(Typography.FractionProperty));
            group.SetSlashedZero((bool) element.GetValue(Typography.SlashedZeroProperty));
            group.SetMathematicalGreek((bool) element.GetValue(Typography.MathematicalGreekProperty));
            group.SetEastAsianExpertForms((bool) element.GetValue(Typography.EastAsianExpertFormsProperty));
            group.SetVariants((FontVariants) element.GetValue(Typography.VariantsProperty));
            group.SetCapitals((FontCapitals) element.GetValue(Typography.CapitalsProperty));
            group.SetNumeralStyle((FontNumeralStyle) element.GetValue(Typography.NumeralStyleProperty));
            group.SetNumeralAlignment((FontNumeralAlignment) element.GetValue(Typography.NumeralAlignmentProperty));
            group.SetEastAsianWidths((FontEastAsianWidths) element.GetValue(Typography.EastAsianWidthsProperty));
            group.SetEastAsianLanguage((FontEastAsianLanguage) element.GetValue(Typography.EastAsianLanguageProperty));
            group.SetStandardSwashes((int) element.GetValue(Typography.StandardSwashesProperty));
            group.SetContextualSwashes((int) element.GetValue(Typography.ContextualSwashesProperty));
            group.SetStylisticAlternates((int) element.GetValue(Typography.StylisticAlternatesProperty));
 
            return group;
        }
 
        // ........................................................................
        //
        // Helpers for Text Flow Initialization
        //
        // ........................................................................
 
        // Recursively calls EndInit for this element and for all its descendants
        internal void DeepEndInit()
        {
            if (!this.IsInitialized)
            {
                if (!this.IsEmpty)
                {
                    IEnumerator children = this.LogicalChildren;
 
                    while (children.MoveNext())
                    {
                        // child.Current could be FrameworkElement, FrameworkContentElement,
                        //  or anything else.  Only recursively call self for FE & FCE.
                        TextElement child = children.Current as TextElement;
                        if (child != null)
                        {
                            child.DeepEndInit();
                        }
                    }
                }
 
                // Mark the end of the initialization phase
                this.EndInit();
                Invariant.Assert(this.IsInitialized);
            }
        }
 
        // Returns the common TextElement ancestor of two TextElements.
        internal static TextElement GetCommonAncestor(TextElement element1, TextElement element2)
        {
            if (element1 != element2)
            {
                int depth1 = 0;
                int depth2 = 0;
                TextElement element;
 
                // Calculate the depths of each TextElement within the tree.
                for (element = element1; element.Parent is TextElement; element = (TextElement)element.Parent)
                {
                    depth1++;
                }
                for (element = element2; element.Parent is TextElement; element = (TextElement)element.Parent)
                {
                    depth2++;
                }
 
                // Then walk up until we reach an equal depth.
 
                while (depth1 > depth2 && element1 != element2)
                {
                    element1 = (TextElement)element1.Parent;
                    depth1--;
                }
 
                while (depth2 > depth1 && element1 != element2)
                {
                    element2 = (TextElement)element2.Parent;
                    depth2--;
                }
 
                // Then, if necessary, keep going up to the root looking for a match.
                while (element1 != element2)
                {
                    element1 = element1.Parent as TextElement;
                    element2 = element2.Parent as TextElement;
                }
            }
 
            Invariant.Assert(element1 == element2);
            return element1;
        }
 
 
        /// <summary>
        /// Derived classes override this method to get notified when a TextContainer
        /// change affects the text parented by this element.
        /// </summary>
        /// <remarks>
        /// If this TextElement is a Run, this function will be called whenever the text content 
        /// under this Run changes. If this TextElement is not a Run, this function will be called 
        /// most of the time its content changes, but not necessarily always.
        /// </remarks>
        internal virtual void OnTextUpdated()
        {
        }
 
        /// <summary>
        /// Derived classes override this method to get notified before TextContainer
        /// causes a logical tree change that affects this element.
        /// </summary>
        internal virtual void BeforeLogicalTreeChange()
        {
        }
 
        /// <summary>
        /// Derived classes override this method to get notified after TextContainer
        /// causes a logical tree change that affects this element.
        /// </summary>
        internal virtual void AfterLogicalTreeChange()
        {
        }
 
        #endregion Internal Methods
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
 
        //------------------------------------------------------
        // The TextContainer containing this TextElement.
        //------------------------------------------------------
        internal TextContainer TextContainer
        {
            get
            {
                return EnsureTextContainer();
            }
        }
 
        //------------------------------------------------------
        // Emptiness of an element
        //------------------------------------------------------
        internal bool IsEmpty
        {
            get
            {
                if (_textElementNode == null)
                    return true;
 
                return (_textElementNode.ContainedNode == null);
            }
        }
 
        //------------------------------------------------------
        // True if this TextElement is contained within a TextContainer.
        //------------------------------------------------------
        internal bool IsInTree
        {
            get
            {
                return _textElementNode != null;
            }
        }
 
        //------------------------------------------------------
        // Symbol offset of this.ElementStart.
        //------------------------------------------------------
        internal int ElementStartOffset
        {
            get
            {
                Invariant.Assert(this.IsInTree, "TextElement is not in any TextContainer, caller should ensure this.");
                return _textElementNode.GetSymbolOffset(EnsureTextContainer().Generation) - 1;
            }
        }
 
        //------------------------------------------------------
        // Symbol offset of this.ContentStart.
        //------------------------------------------------------
        internal int ContentStartOffset
        {
            get
            {
                Invariant.Assert(this.IsInTree, "TextElement is not in any TextContainer, caller should ensure this.");
                return _textElementNode.GetSymbolOffset(EnsureTextContainer().Generation);
            }
        }
 
        //------------------------------------------------------
        // Symbol offset of this.ContentEnd.
        //------------------------------------------------------
        internal int ContentEndOffset
        {
            get
            {
                Invariant.Assert(this.IsInTree, "TextElement is not in any TextContainer, caller should ensure this.");
                return _textElementNode.GetSymbolOffset(EnsureTextContainer().Generation) + _textElementNode.SymbolCount - 2;
            }
        }
 
        //------------------------------------------------------
        // Symbol offset of this.ElementEnd.
        //------------------------------------------------------
        internal int ElementEndOffset
        {
            get
            {
                Invariant.Assert(this.IsInTree, "TextElement is not in any TextContainer, caller should ensure this.");
                return _textElementNode.GetSymbolOffset(EnsureTextContainer().Generation) + _textElementNode.SymbolCount - 1;
            }
        }
 
        //------------------------------------------------------
        // Symbol count of this TextElement, including start/end
        // edges.
        //------------------------------------------------------
        internal int SymbolCount
        {
            get
            {
                return this.IsInTree ? _textElementNode.SymbolCount : 2;
            }
        }
 
        //------------------------------------------------------
        // The node in a TextContainer representing this TextElement.
        //------------------------------------------------------
        internal TextTreeTextElementNode TextElementNode
        {
            get
            {
                return _textElementNode;
            }
 
            set
            {
                _textElementNode = value;
            }
        }
 
        //-------------------------------------------------------------------
        // Typography properties group
        //-------------------------------------------------------------------
        internal TypographyProperties TypographyPropertiesGroup
        {
            get
            {
                if (_typographyPropertiesGroup == null)
                {
                    _typographyPropertiesGroup = GetTypographyProperties(this);
                }
                return _typographyPropertiesGroup;
            }
        }
 
        private static void OnTypographyChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
        {
            ((TextElement) element)._typographyPropertiesGroup = null;
        }
 
        //------------------------------------------------------
        // Derived classes override this method if they want
        // their left edges to be visible to IMEs.  This is the
        // case for structural elements like Paragraph but not
        // for formatting elements like Inline.
        //------------------------------------------------------
        internal virtual bool IsIMEStructuralElement
        {
            get
            {
                return false;
            }
        }
 
        //------------------------------------------------------
        // Plain text character count of this element's edges.
        // Used by the IME to convert Paragraph, TableCell, etc.
        // into unicode placeholders.
        //------------------------------------------------------
        internal int IMELeftEdgeCharCount
        {
            get
            {
                int leftEdgeCharCount = 0;
 
                if (this.IsIMEStructuralElement)
                {
                    if (!this.IsInTree)
                    {
                        // IMELeftEdgeCharCount depends on context, has no meaning outside a tree.
                        leftEdgeCharCount = -1;
                    }
                    else
                    {
                        // The first sibling is always invisible to the IME.
                        // This ensures we don't get into trouble creating implicit
                        // content on IME SetText calls.
                        leftEdgeCharCount = this.TextElementNode.IsFirstSibling ? 0 : 1;
                    }
                }
 
                return leftEdgeCharCount;
            }
        }
 
        //------------------------------------------------------
        // Returns true if this node is the leftmost sibling of its parent
        // and visible to the IMEs (ie, is a Block).
        //
        // This is interesting because when we do want to expose
        // element edges to the IMEs (Blocks, TableCell, etc.) we
        // have one exception: the first sibling.  Edges of first
        // siblings must be hidden because the TextEditor will
        // implicitly create first siblings when the IMEs, for example,
        // insert raw text into a TableCell that lacks a Paragraph.
        // The IMEs can't handle the implicit edge creation, so we
        // hide those edges.
        //------------------------------------------------------
        internal virtual bool IsFirstIMEVisibleSibling
        {
            get
            {
                bool isFirstIMEVisibleSibling = false;
 
                if (this.IsIMEStructuralElement)
                {
                    isFirstIMEVisibleSibling = (this.TextElementNode == null) ? true : this.TextElementNode.IsFirstSibling;
                }
 
                return isFirstIMEVisibleSibling;
            }
        }
 
        //------------------------------------------------------
        // Returns a TextElement immediately following this one
        // on the same level of siblings.
        //------------------------------------------------------
        internal TextElement NextElement
        {
            get
            {
                if (!this.IsInTree)
                {
                    return null;
                }
 
                TextTreeTextElementNode node = _textElementNode.GetNextNode() as TextTreeTextElementNode;
                return (node != null) ? node.TextElement : null;
            }
        }
 
        //------------------------------------------------------
        // Returns a TextElement immediately preceding this one
        // on the same level of siblings.
        //------------------------------------------------------
        internal TextElement PreviousElement
        {
            get
            {
                if (!this.IsInTree)
                {
                    return null;
                }
 
                TextTreeTextElementNode node = _textElementNode.GetPreviousNode() as TextTreeTextElementNode;
                return (node != null) ? node.TextElement : null;
            }
        }
 
        //------------------------------------------------------
        // Returns the first TextElement contained by this
        // TextElement.
        //------------------------------------------------------
        internal TextElement FirstChildElement
        {
            get
            {
                if (!this.IsInTree)
                {
                    return null;
                }
 
                TextTreeTextElementNode node = _textElementNode.GetFirstContainedNode() as TextTreeTextElementNode;
                return (node != null) ? node.TextElement : null;
            }
        }
 
        //------------------------------------------------------
        // Returns the last TextElement contained by this
        // TextElement.
        //------------------------------------------------------
        internal TextElement LastChildElement
        {
            get
            {
                if (!this.IsInTree)
                {
                    return null;
                }
 
                TextTreeTextElementNode node = _textElementNode.GetLastContainedNode() as TextTreeTextElementNode;
                return (node != null) ? node.TextElement : null;
            }
        }
 
        #endregion Internal Properties
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        /// <summary>
        /// Inserts a string at the end of the content spanned by this TextElement.
        /// </summary>
        /// <param name="textData">
        /// string to insert.
        /// </param>
        private void Append(string textData)
        {
            TextContainer tree;
 
            ArgumentNullException.ThrowIfNull(textData);
 
            tree = EnsureTextContainer();
 
            tree.BeginChange();
            try
            {
                // don't allocate a TextPointer, we shouldn't have to.
                // Change InsertTextInternal to take a node/edge pair instead.
                tree.InsertTextInternal(new TextPointer(tree, _textElementNode, ElementEdge.BeforeEnd), textData);
            }
            finally
            {
                tree.EndChange();
            }
        }
 
        /// <summary>
        /// Inserts a TextElement at the end of the content spanned by this
        /// TextElement.
        /// </summary>
        /// <param name="element">
        /// TextElement to insert.
        /// </param>
        /// <Remarks>
        /// This method will remove element from TextContainer it was previously
        /// positioned within.  Any content spanned by element will also
        /// be moved.
        /// </Remarks>
        private void Append(TextElement element)
        {
            TextContainer tree;
            TextPointer position;
 
            ArgumentNullException.ThrowIfNull(element);
 
            tree = EnsureTextContainer();
 
            tree.BeginChange();
            try
            {
                // don't allocate a TextPointer, we shouldn't have to.
                // Change InsertElementInternal to take a node/edge pair instead.
                position = new TextPointer(tree, _textElementNode, ElementEdge.BeforeEnd);
                tree.InsertElementInternal(position, position, element);
            }
            finally
            {
                tree.EndChange();
            }
        }
 
        // Demand creates a TextContainer if no tree is associated with this instance.
        // Otherwise returns the exisiting tree, and clears the tree's DeadPositionList.
        private TextContainer EnsureTextContainer()
        {
            TextContainer tree;
            TextPointer start;
 
            if (this.IsInTree)
            {
                tree = _textElementNode.GetTextTree();
                tree.EmptyDeadPositionList();
            }
            else
            {
                tree = new TextContainer(null, false /* plainTextOnly */);
                start = tree.Start;
 
                tree.BeginChange();
                try
                {
                    tree.InsertElementInternal(start, start, this);
                }
                finally
                {
                    // No event will be raised, since we know there are no listeners yet!
                    tree.EndChange();
                }
 
                Invariant.Assert(this.IsInTree);
            }
 
            return tree;
        }
 
        private static bool IsValidFontFamily(object o)
        {
            FontFamily value = o as FontFamily;
            return (value != null);
        }
 
        /// <summary>
        /// <see cref="DependencyProperty.ValidateValueCallback"/>
        /// </summary>
        private static bool IsValidFontSize(object value)
        {
            double fontSize = (double) value;
            double minFontSize = TextDpi.MinWidth;
            double maxFontSize = Math.Min(1000000, PTS.MaxFontSize);
 
            if (Double.IsNaN(fontSize))
            {
                return false;
            }
            if (fontSize < minFontSize)
            {
                return false;
            }
            if (fontSize > maxFontSize)
            {
                return false;
            }
            return true;
        }
 
        #endregion Private methods
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        // The node in a TextContainer representing this TextElement.
        private TextTreeTextElementNode _textElementNode;
 
        //-------------------------------------------------------------------
        // Typography Group Property
        //-------------------------------------------------------------------
        private TypographyProperties _typographyPropertiesGroup = Typography.Default;
 
        #endregion Private Fields
    }
}