TextRangeSerialization.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Documents / TextRangeSerialization.cs / 1305600 / TextRangeSerialization.cs

                            //---------------------------------------------------------------------------- 
//
// File: TextRangeSerialization.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
// Description: Set of static methods implementing text range serialization 
// 
//---------------------------------------------------------------------------
 
namespace System.Windows.Documents
{
    using MS.Internal;
    using System.Text; 
    using System.Xml;
    using System.IO; 
    using System.Windows.Markup; // TypeConvertContext, ParserContext 
    using System.Windows.Controls;
    using System.Collections; 
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Security; 

    ///  
    ///     TextRangeSerialization is a static class containing 
    ///     an implementation for TextRange serialization functionality.
    ///     It is only used from TextRange.GetXml/AppendXml methods. 
    /// 
    internal static class TextRangeSerialization
    {
        // ------------------------------------------------------------- 
        //
        // Internal Methods 
        // 
        // -------------------------------------------------------------
 
        #region Internal Methods

        internal static void WriteXaml(XmlWriter xmlWriter, ITextRange range, bool useFlowDocumentAsRoot, WpfPayload wpfPayload)
        { 
            WriteXaml(xmlWriter, range, useFlowDocumentAsRoot, wpfPayload, false);
        } 
 
        /// 
        /// Writes a content of current range in form of valid xml. 
        /// Places an artificial element xaml:FlowDocument as a root of output xml.
        /// 
        /// 
        /// XmlWriter to which the range will be serialized 
        /// 
        ///  
        /// TextRange whose content is copied into XmlWriter xmlWriter. 
        /// 
        ///  
        /// true means that we need to serialize the whole FlowDocument - used in FileSave scenario;
        /// false means that we are in copy-paste scenario and will use Section or Span as a root - depending on context.
        /// 
        ///  
        /// When this parameter is not null, images are serialized.  When null, images are stripped out.
        ///  
        ///  
        /// When TRUE, TextElements are serialized as-is.  When FALSE, they're upcast to their base type.
        ///  
        internal static void WriteXaml(XmlWriter xmlWriter, ITextRange range, bool useFlowDocumentAsRoot, WpfPayload wpfPayload, bool preserveTextElements)
        {
            // Set unindented formatting to avoid inserting insignificant whitespaces as significant ones
            Formatting saveWriterFormatting = Formatting.None; 
            if (xmlWriter is XmlTextWriter)
            { 
                saveWriterFormatting = ((XmlTextWriter)xmlWriter).Formatting; 
                ((XmlTextWriter)xmlWriter).Formatting = Formatting.None;
            } 

            // Get the default xamlTypeMapper.
            XamlTypeMapper xamlTypeMapper = XmlParserDefaults.DefaultMapper;
 
            // Identify structural scope of selection - nearest common ancestor
            ITextPointer commonAncestor = FindSerializationCommonAncestor(range); 
 
            // Decide whether we need last paragraph merging or not
            bool lastParagraphMustBeMerged = 
                !TextPointerBase.IsAfterLastParagraph(range.End) &&
                range.End.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementStart;

            // Write wrapping element with contextual properties 
            WriteRootFlowDocument(range, commonAncestor, xmlWriter, xamlTypeMapper, lastParagraphMustBeMerged, useFlowDocumentAsRoot);
 
            // The ignoreWriteHyperlinkEnd flag will be set after call WriteOpeningTags. 
            // If ignoreWriteHyperlinkEnd is true, WriteXamlTextSegment won't write Hyperlink end element
            // since Hyperlink writing opening tag is ignored by selecting the partial of Hyperlink. 
            bool ignoreWriteHyperlinkEnd;
            List ignoreList = new List();

            // Start counting tags needed to be closed. 
            // EmptyDocumentDepth==1 - counts FlowDocument opened in WriteRootFlowDocument above.
            int elementLevel = EmptyDocumentDepth + WriteOpeningTags(range, range.Start, commonAncestor, xmlWriter, xamlTypeMapper, /*reduceElement:*/wpfPayload == null, out ignoreWriteHyperlinkEnd, ref ignoreList, preserveTextElements); 
 
            if (range.IsTableCellRange)
            { 
                WriteXamlTableCellRange(xmlWriter, range, xamlTypeMapper, ref elementLevel, wpfPayload, preserveTextElements);
            }
            else
            { 
                WriteXamlTextSegment(xmlWriter, range.Start, range.End, xamlTypeMapper, ref elementLevel, wpfPayload, ignoreWriteHyperlinkEnd, ignoreList, preserveTextElements);
            } 
 
            // Close all remaining tags - scoping its End position
            Invariant.Assert(elementLevel >= 0, "elementLevel cannot be negative"); 
            while (elementLevel-- > 0)
            {
                xmlWriter.WriteFullEndElement();
            } 

            // Restore xmlWriter's Formatting property 
            if (xmlWriter is XmlTextWriter) 
            {
                ((XmlTextWriter)xmlWriter).Formatting = saveWriterFormatting; 
            }
        }

        ///  
        /// Reads a well-formed xml representing a serialized text range.
        /// It expects a root element xaml:FlowDocument and two range markers 
        /// The result of reading is pasting this text into End position 
        /// of text range.
        ///  
        /// 
        /// TextRange designating the target position for pasting.
        /// The existing content of a range will be deleted and new content
        /// will be inserted at the end. 
        /// Resulting locations of Start/End positions depend on their gravities.
        /// Normally (when gravity=Backward/Forward respectively) the resulting 
        /// range will embrace the inserted content. 
        /// 
        ///  
        /// Represents a portion of xml to insert into the range.
        /// 
        /// 
        /// We are expecting to be called with xmlReader on opening tag 
        /// of root text range element - xaml:FlowDocument.
        /// Some insignificant stuff may occur before the root though. 
        /// Otherwise exception will be thrown. 
        /// 
        internal static void PasteXml(TextRange range, TextElement fragment) 
        {
            Invariant.Assert(fragment != null);

            // Check a special case for pasing a single embedded element 
            if (PasteSingleEmbeddedElement(range, fragment))
            { 
                // All done. Return successfully. 
                return;
            } 

            // Set default value for an indicator of whether we need to merge last paragraph or not.
            // It depends on a state of a range, so do it before emptying the range.
            AdjustFragmentForTargetRange(fragment, range); 

            // Delete current content of a range 
            if (!range.IsEmpty) 
            {
                range.Text = String.Empty; 
            }
            Invariant.Assert(range.IsEmpty, "range must be empty in the beginning of pasting");

            // Chek special case of empty pasted fragment 
            if (((ITextPointer)fragment.ContentStart).CompareTo(fragment.ContentEnd) == 0)
            { 
                // Pasted fragment is empty. Nothing to insert. 
                return;
            } 

            // Transfer the content from reader to writer and merge elements on both ends
            PasteTextFragment(fragment, range);
        } 

        #endregion Internal Methods 
 
        // --------------------------------------------------------------
        // 
        // Private Methods
        //
        // -------------------------------------------------------------
 
        #region Private Methods
 
        // ............................................................. 
        //
        // Serialization 
        //
        // .............................................................

        ///  
        /// This function serializes text segment formed by rangeStart and rangeEnd to valid xml using xmlWriter.
        ///  
        ///  
        /// To mask the security exception from XamlWriter.Save in partial trust case,
        /// this function checks if the current call stack has the all clipboard permission. 
        /// 
        private static void WriteXamlTextSegment(XmlWriter xmlWriter, ITextPointer rangeStart, ITextPointer rangeEnd, XamlTypeMapper xamlTypeMapper, ref int elementLevel, WpfPayload wpfPayload, bool ignoreWriteHyperlinkEnd, List ignoreList, bool preserveTextElements)
        {
            // Special case for pure text selection - we need a Run wrapper for it. 
            if (elementLevel == EmptyDocumentDepth && typeof(Run).IsAssignableFrom(rangeStart.ParentType))
            { 
                elementLevel++; 
                xmlWriter.WriteStartElement(typeof(Run).Name);
            } 

            // Create text navigator for reading the range's content
            ITextPointer textReader = rangeStart.CreatePointer();
 
            // Exclude last opening tag from serialization - we don't need to create extra element
            // is cases when we have whole paragraphs/cells selected. 
            // NOTE: We do this slightly differently than in TextRangeEdit.AdjustRangeEnd, where we use normalization for adjusted position. 
            // In this case normalized position does not work, because we need to keep information about crossed paragraph boundary.
            while (rangeEnd.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) 
            {
                rangeEnd = rangeEnd.GetNextContextPosition(LogicalDirection.Backward);
            }
 
            // Write the range internal contents
            while (textReader.CompareTo(rangeEnd) < 0) 
            { 
                TextPointerContext runType = textReader.GetPointerContext(LogicalDirection.Forward);
 
                switch (runType)
                {
                    case TextPointerContext.ElementStart:
                        TextElement nextElement = (TextElement)textReader.GetAdjacentElement(LogicalDirection.Forward); 
                        if (nextElement is Hyperlink)
                        { 
                            // Don't write Hyperlink start element if Hyperlink is invalid 
                            // in case of having a UiElement except Image or stated the range end
                            // position before the end position of the Hyperlink. 
                            if (IsHyperlinkInvalid(textReader, rangeEnd))
                            {
                                ignoreWriteHyperlinkEnd = true;
                                textReader.MoveToNextContextPosition(LogicalDirection.Forward); 

                                continue; 
                            } 
                        }
                        else if (nextElement != null) 
                        {
                            //

 
                            TextElementEditingBehaviorAttribute att = (TextElementEditingBehaviorAttribute)Attribute.GetCustomAttribute(nextElement.GetType(), typeof(TextElementEditingBehaviorAttribute));
                            if (att != null && !att.IsTypographicOnly) 
                            { 
                                if (IsPartialNonTypographic(textReader, rangeEnd))
                                { 
                                    // Add pointer to ignore list
                                    ITextPointer ptr = textReader.CreatePointer();
                                    ptr.MoveToElementEdge(ElementEdge.BeforeEnd);
                                    ignoreList.Add(ptr.Offset); 

                                    textReader.MoveToNextContextPosition(LogicalDirection.Forward); 
 
                                    continue;
                                } 
                            }
                        }

                        elementLevel++; 
                        textReader.MoveToNextContextPosition(LogicalDirection.Forward);
                        WriteStartXamlElement(/*range:*/null, textReader, xmlWriter, xamlTypeMapper, /*reduceElement:*/wpfPayload == null, preserveTextElements); 
 
                        break;
 
                    case TextPointerContext.ElementEnd:
                        // Don't write Hyperlink end element if Hyperlink include the invalid
                        // in case of having a UiElement except Image or stated the range end
                        // before the end position of the Hyperlink or Hyperlink opening tag is 
                        // skipped from WriteOpeningTags by selecting of the partial of Hyperlink.
                        if (ignoreWriteHyperlinkEnd && (textReader.GetAdjacentElement(LogicalDirection.Forward) is Hyperlink)) 
                        { 
                            // Reset the flag to keep walk up the next Hyperlink tag
                            ignoreWriteHyperlinkEnd = false; 
                            textReader.MoveToNextContextPosition(LogicalDirection.Forward);

                            continue;
                        } 

                        // Check the ignore list 
                        ITextPointer endPointer = textReader.CreatePointer(); 
                        endPointer.MoveToElementEdge(ElementEdge.BeforeEnd);  //
                        if (ignoreList.Contains(endPointer.Offset)) 
                        {
                            ignoreList.Remove(endPointer.Offset);
                            textReader.MoveToNextContextPosition(LogicalDirection.Forward);
 
                            continue;
                        } 
 
                        elementLevel--;
                        if (TextSchema.IsBreak(textReader.ParentType)) 
                        {
                            // For LineBreak, etc. use empty element syntax
                            xmlWriter.WriteEndElement();
                        } 
                        else
                        {   // 
                            // For all other textelements use explicit closing tag. 
                            xmlWriter.WriteFullEndElement();
                        } 
                        textReader.MoveToNextContextPosition(LogicalDirection.Forward);
                        break;

                    case TextPointerContext.Text: 
                        int textLength = textReader.GetTextRunLength(LogicalDirection.Forward);
                        char[] text = new Char[textLength]; 
 
                        textLength = TextPointerBase.GetTextWithLimit(textReader, LogicalDirection.Forward, text, 0, textLength, rangeEnd);
 
                        // XmlWriter will throw an ArgumentException if text contains
                        // any invalid surrogates, so strip them out now.
                        textLength = StripInvalidSurrogateChars(text, textLength);
 
                        xmlWriter.WriteChars(text, 0, textLength);
                        textReader.MoveToNextContextPosition(LogicalDirection.Forward); 
                        break; 

                    case TextPointerContext.EmbeddedElement: 
                        object embeddedObject = textReader.GetAdjacentElement(LogicalDirection.Forward);
                        textReader.MoveToNextContextPosition(LogicalDirection.Forward);

                        WriteEmbeddedObject(embeddedObject, xmlWriter, wpfPayload); 
                        break;
 
                    default: 
                        Invariant.Assert(false, "unexpected value of runType");
                        textReader.MoveToNextContextPosition(LogicalDirection.Forward); 
                        break;
                }
            }
        } 

        ///  
        /// Serializes a rectagular table range 
        /// 
        private static void WriteXamlTableCellRange(XmlWriter xmlWriter, ITextRange range, XamlTypeMapper xamlTypeMapper, ref int elementLevel, WpfPayload wpfPayload, bool preserveTextElements) 
        {
            Invariant.Assert(range.IsTableCellRange, "range is expected to be in IsTableCellRange state");

            List textSegments = range.TextSegments; 

            int checkElementLevel = -1; // negative value as an indicator that it is not yet initialized 
 
            // Set ignoreWriteHyperlinkEnd as false initially
            bool ignoreWriteHyperlinkEnd = false; 
            List ignoreList = new List();

            for (int i = 0; i < textSegments.Count; i++)
            { 
                TextSegment textSegment = textSegments[i];
 
                // Open a row for this segment (except for the very first one, for which we opened a row in a WriteOpeningTags method) 
                if (i > 0)
                { 
                    ITextPointer pointer = textSegment.Start.CreatePointer();
                    while (!typeof(TableRow).IsAssignableFrom(pointer.ParentType))
                    {
                        Invariant.Assert(typeof(TextElement).IsAssignableFrom(pointer.ParentType), "pointer must be still in a scope of TextElement"); 
                        pointer.MoveToElementEdge(ElementEdge.BeforeStart);
                    } 
                    Invariant.Assert(typeof(TableRow).IsAssignableFrom(pointer.ParentType), "pointer must be in a scope of TableRow"); 
                    pointer.MoveToElementEdge(ElementEdge.BeforeStart);
 
                    ITextRange textRange = new TextRange(textSegment.Start, textSegment.End);

                    elementLevel += WriteOpeningTags(textRange, textSegment.Start, pointer, xmlWriter, xamlTypeMapper, /*reduceElement:*/wpfPayload == null, out ignoreWriteHyperlinkEnd, ref ignoreList, preserveTextElements);
                } 

                // Output the cell segment for one row 
                WriteXamlTextSegment(xmlWriter, textSegment.Start, textSegment.End, xamlTypeMapper, ref elementLevel, wpfPayload, ignoreWriteHyperlinkEnd, ignoreList, preserveTextElements); 

                Invariant.Assert(elementLevel >= 4, "At the minimun we expected to stay within four elements: Section(wrapper),Table,TableRowGroup,TableRow"); 
                if (checkElementLevel < 0) checkElementLevel = elementLevel; // initialize level checking variable
                Invariant.Assert(checkElementLevel == elementLevel, "elementLevel is supposed to be unchanged between segments of table cell range");

                // Assuming that the element is TableRow - close it. 
                // NOTE: Such assumption is valid because WriteXamlTextSegment moves end pointer out of all opening tags,
                // so it ends serialization immediately after the last cell's closing tag. 
                // This means that we only need to close one level - for TableRow. 
                //
                elementLevel--; 
                xmlWriter.WriteFullEndElement();
            }
        }
 
        /// 
        /// Walks the tree up from current position and writes all scoping tags 
        /// in their natural order - from root to leafs. 
        /// 
        ///  
        /// Range identifying the whole selection.
        /// Needed for
        /// - table cell range case: proper column processing: to output only columns related to the selection
        /// - text segement case: hyperlink serialization heuristics 
        /// 
        ///  
        /// ITextPointer identifying an element. 
        /// 
        ///  
        /// A position identifying the scope which should be used for serialization.
        /// All tags outside of this scope will be ignored.
        /// 
        ///  
        /// XmlWriter to write element tags.
        ///  
        ///  
        /// 
        ///  
        /// 
        /// 
        /// 
        ///  
        /// /// 
        /// Number of opening tags written into XmlWriter. 
        /// This number should be used afterwards to close all opened tags. 
        /// 
        private static int WriteOpeningTags(ITextRange range, ITextPointer thisElement, ITextPointer scope, XmlWriter xmlWriter, XamlTypeMapper xamlTypeMapper, bool reduceElement, out bool ignoreWriteHyperlinkEnd, ref List ignoreList, bool preserveTextElements) 
        {
            ignoreWriteHyperlinkEnd = false;

            // Recursion ends when we reach the scope level. We will write tags on returing path from the recursion 
            if (thisElement.HasEqualScope(scope))
            { 
                return 0; // no elements have opened at this level. Return elementCount==0. 
            }
 
            Invariant.Assert(typeof(TextElement).IsAssignableFrom(thisElement.ParentType), "thisElement is expected to be a TextElement");

            ITextPointer previousLevel = thisElement.CreatePointer();
            previousLevel.MoveToElementEdge(ElementEdge.BeforeStart); 

            // Recurse into the parent element 
            int elementLevel = WriteOpeningTags(range, previousLevel, scope, xmlWriter, xamlTypeMapper, reduceElement, out ignoreWriteHyperlinkEnd, ref ignoreList, preserveTextElements); 

            // After returning from the recursion - when all parent tags have been written, 
            // write the opening tag for this element

            // Hyperlink open tag will be skipped since the range selection of Hyperlink is the partial
            // of Hyperlink range or Hyperlink include invalid UIElement except Image. 
            bool ignoreHyperlink = false;
            bool isPartialNonTypographic = false; 
 
            if (thisElement.ParentType == typeof(Hyperlink))
            { 
                if (TextPointerBase.IsAtNonMergeableInlineStart(range.Start))
                {
                    ITextPointer position = thisElement.CreatePointer();
                    position.MoveToElementEdge(ElementEdge.BeforeStart); 

                    ignoreHyperlink = IsHyperlinkInvalid(position, range.End); 
                } 
                else
                { 
                    ignoreHyperlink = true;
                }
            }
            else 
            {
                // 
 
                TextElementEditingBehaviorAttribute att = (TextElementEditingBehaviorAttribute)Attribute.GetCustomAttribute(thisElement.ParentType, typeof(TextElementEditingBehaviorAttribute));
                if (att != null && !att.IsTypographicOnly) 
                {
                    if (TextPointerBase.IsAtNonMergeableInlineStart(range.Start))
                    {
                        ITextPointer position = thisElement.CreatePointer(); 
                        position.MoveToElementEdge(ElementEdge.BeforeStart);
 
                        isPartialNonTypographic = IsPartialNonTypographic(position, range.End); 
                    }
                    else 
                    {
                        isPartialNonTypographic = true;
                    }
                } 
            }
            int count; 
 
            if (ignoreHyperlink)
            { 
                // Ignore writing Hyperlink opening tag
                ignoreWriteHyperlinkEnd = true;

                // Set elementLevel without adding it 
                count = elementLevel;
            } 
            else if (isPartialNonTypographic) 
            {
                // Add the end pointer to the list 
                ITextPointer position = thisElement.CreatePointer();
                position.MoveToElementEdge(ElementEdge.BeforeEnd);
                ignoreList.Add(position.Offset);
 
                // Set elementLevel without adding to it
                count = elementLevel; 
            } 
            else
            { 
                // Write the opening tag
                WriteStartXamlElement(range, thisElement, xmlWriter, xamlTypeMapper, reduceElement, preserveTextElements);

                // Each opening tag adds one to the level count 
                count = elementLevel + 1;
            } 
 
            // Return the opening tag count
            return count; 
        }

        /// 
        /// Writes an opening tag of an element together with all attributes 
        /// representing Avalon properties.
        ///  
        ///  
        /// Parameter used for top-level Table element - to decide what columns to output.
        /// For all other elements it's ignored. 
        /// 
        /// 
        /// TextPointer positioned in the scope of element whose
        /// start tag is going to be written. 
        /// 
        ///  
        /// XmlWriter to output element opening tag. 
        /// 
        ///  
        /// 
        /// True value of this parameter indicates that
        /// serialization goes into XamlPackage, so all elements
        /// can be preserved as is; otherwise some of them must be 
        /// reduced into simpler representations (such as InlineUIContainer -> Run
        /// and BlockUIContainer -> Paragraph). 
        ///  
        /// 
        /// If TRUE, TextElements are serialized as-is.  If FALSE, they're upcast 
        /// to their base types.
        /// 
        private static void WriteStartXamlElement(ITextRange range, ITextPointer textReader, XmlWriter xmlWriter, XamlTypeMapper xamlTypeMapper, bool reduceElement, bool preserveTextElements)
        { 
            Type elementType = textReader.ParentType;
 
            Type elementTypeStandardized = TextSchema.GetStandardElementType(elementType, reduceElement); 

            // Get rid f UIContainers when their child is not an image 
            if (elementTypeStandardized == typeof(InlineUIContainer) || elementTypeStandardized == typeof(BlockUIContainer))
            {
                Invariant.Assert(!reduceElement);
 
                InlineUIContainer inlineUIContainer = textReader.GetAdjacentElement(LogicalDirection.Backward) as InlineUIContainer;
                BlockUIContainer blockUIContainer = textReader.GetAdjacentElement(LogicalDirection.Backward) as BlockUIContainer; 
 
                if ((inlineUIContainer == null || !(inlineUIContainer.Child is Image)) &&
                    (blockUIContainer == null || !(blockUIContainer.Child is Image))) 
                {
                    // Even when we serialize for DataFormats.XamlPackage we strip out UIElement
                    // different from Images.
                    // Note that this condition is consistent with the one in WriteEmbeddedObject - 
                    // so that when we reduce the element type fromm UIContainer to Run/Paragraph
                    // we also output just a space instead of the embedded object conntained in it. 
                    elementTypeStandardized = TextSchema.GetStandardElementType(elementType, /*reduceElement:*/true); 
                }
            } 
            else if (preserveTextElements)
            {
                elementTypeStandardized = elementType;
            } 

            bool customTextElement = preserveTextElements && !TextSchema.IsKnownType(elementType); 
            if (customTextElement) 
            {
                // If the element is not from PresentationFramework, we'll need to serialize a namespace 
                //
                int index = elementTypeStandardized.Module.Name.LastIndexOf('.');
                string assembly = (index == -1 ? elementTypeStandardized.Module.Name : elementTypeStandardized.Module.Name.Substring(0, index));
                string nameSpace = "clr-namespace:" + elementTypeStandardized.Namespace + ";" + "assembly=" + assembly; 
                string prefix = elementTypeStandardized.Namespace;
                xmlWriter.WriteStartElement(prefix, elementTypeStandardized.Name, nameSpace); 
            } 
            else
            { 
                xmlWriter.WriteStartElement(elementTypeStandardized.Name);
            }

            // Write properties 
            DependencyObject complexProperties = new DependencyObject();
            WriteInheritableProperties(elementTypeStandardized, textReader, xmlWriter, /*onlyAffected:*/true, complexProperties); 
            WriteNoninheritableProperties(elementTypeStandardized, textReader, xmlWriter, /*onlyAffected:*/true, complexProperties); 
            if (customTextElement)
            { 
                WriteLocallySetProperties(elementTypeStandardized, textReader, xmlWriter, complexProperties);
            }
            WriteComplexProperties(xmlWriter, complexProperties, elementTypeStandardized);
 
            // Special case for Table element serialization
            if (elementTypeStandardized == typeof(Table) && textReader is TextPointer) 
            { 
                // Write the columns text.
                WriteTableColumnsInformation(range, (Table)((TextPointer)textReader).Parent, xmlWriter, xamlTypeMapper); 
            }
        }

        // Write columns related to the given table cell range. 
        private static void WriteTableColumnsInformation(ITextRange range, Table table, XmlWriter xmlWriter, XamlTypeMapper xamlTypeMapper)
        { 
            TableColumnCollection columns = table.Columns; 
            int startColumn;
            int endColumn; 

            if (!TextRangeEditTables.GetColumnRange(range, table, out startColumn, out endColumn))
            {
                startColumn = 0; 
                endColumn = columns.Count - 1;
            } 
 
            Invariant.Assert(startColumn >= 0, "startColumn index is supposed to be non-negative");
 
            if(columns.Count > 0)
            {
                // Build an appropriate name for the complex property
                string complexPropertyName = table.GetType().Name + ".Columns"; 

                // Write the start element for the complex property. 
                xmlWriter.WriteStartElement(complexPropertyName); 

                for (int i = startColumn; i <= endColumn && i < columns.Count; i++) 
                {
                    WriteXamlAtomicElement(columns[i], xmlWriter, /*reduceElement:*/false);
                }
 
                // Close the element for the complex property
                xmlWriter.WriteEndElement(); 
            } 

        } 

        /// 
        /// Creates a FlowDocument element wrapping copied content and storing its contextual properties.
        ///  
        /// 
        ///  
        ///  
        /// 
        ///  
        /// 
        /// true means that we need to serialize the whole FlowDocument - used in FileSave scenario;
        /// false means that we are in copy-paste scenario and will use Section or Span as a root - depending on context.
        ///  
        private static void WriteRootFlowDocument(ITextRange range, ITextPointer context, XmlWriter xmlWriter, XamlTypeMapper xamlTypeMapper, bool lastParagraphMustBeMerged, bool useFlowDocumentAsRoot)
        { 
            Type rootType; 
            const string xmlNamespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
            const string xmlns = "xmlns"; 

            // Decide what root element to use
            if (useFlowDocumentAsRoot)
            { 
                rootType = typeof(FlowDocument);
            } 
            else 
            {
                Type contextType = context.ParentType; 
                if (contextType == null ||
                    typeof(Paragraph).IsAssignableFrom(contextType) ||
                    typeof(Inline).IsAssignableFrom(contextType) && !typeof(AnchoredBlock).IsAssignableFrom(contextType))
                { 
                    rootType = typeof(Span);
                } 
                else 
                {
                    rootType = typeof(Section); 
                }
            }

            // Create a root element FlowDocument 
            xmlWriter.WriteStartElement(rootType.Name, xmlNamespace);
 
            // Define default namespace as Avalon namespace 
            xmlWriter.WriteAttributeString(xmlns, xmlNamespace);
 
            // Set the value of xml:space to "preserve" to consider all spaces as significant
            // Note that Xml treats whitespaces as significant if they belong to some nonempty line
            // (neighbored by non-whitespace characters at least from one side)
            // That's why we only loose whitespaces if they occupy the whole textrun in xml. 
            // So alternative solution for whitespace preservation could be setting xml:space="preserve"
            // attribute to only empty runs - this would make our whitespace preservation more 
            // narrowed... 
            //
 



 

            xmlWriter.WriteAttributeString("xml:space", "preserve"); 
 
            // Write all contextual properties as attributes of root fragment
            DependencyObject complexProperties = new DependencyObject(); 
            if (useFlowDocumentAsRoot)
            {
                WriteInheritablePropertiesForFlowDocument((DependencyObject)((TextPointer)context).Parent, xmlWriter, complexProperties);
            } 
            else
            { 
                WriteInheritableProperties(rootType, context, xmlWriter, /*onlyAffected:*/false, complexProperties); 
            }
 
            if (rootType == typeof(Span))
            {
                // Root element is not real element to paste. It is just a property bag for contextual properties.
                // So we collect non-inheritable properties only for inline content; not needing it for block one. 
                WriteNoninheritableProperties(typeof(Span), context, xmlWriter, /*onlyAffected:*/false, complexProperties);
            } 
 
            // Write an indicator that last paragraph must be merged on paste
            if (rootType == typeof(Section) && lastParagraphMustBeMerged) 
            {
                xmlWriter.WriteAttributeString(Section.HasTrailingParagraphBreakOnPastePropertyName, "False");
            }
 
            // Note that we are skipping background property, because we only want to transfer it for the whole document.
            // 
 
            WriteComplexProperties(xmlWriter, complexProperties, rootType);
        } 

        private static void WriteInheritablePropertiesForFlowDocument(DependencyObject context, XmlWriter xmlWriter, DependencyObject complexProperties)
        {
            DependencyProperty[] inheritableProperties = TextSchema.GetInheritableProperties(typeof(FlowDocument)); 

            for (int i = 0; i < inheritableProperties.Length; i++) 
            { 
                DependencyProperty property = inheritableProperties[i];
                object value = context.ReadLocalValue(property); 

                if (value != DependencyProperty.UnsetValue)
                {
                    string stringValue = DPTypeDescriptorContext.GetStringValue(property, value); 
                    if (stringValue != null)
                    { 
                        stringValue = FilterNaNStringValueForDoublePropertyType(stringValue, property.PropertyType); 

                        string propertyName; 
                        if (property == FrameworkContentElement.LanguageProperty)
                        {
                            // Special case for CultureInfo property that must be represented in xaml as xml:lang attribute
                            propertyName = "xml:lang"; 
                        }
                        else 
                        { 
                            // Regular case using own property name
                            propertyName = property.OwnerType == typeof(Typography) ? "Typography." + property.Name : property.Name; 
                        }
                        xmlWriter.WriteAttributeString(propertyName, stringValue);
                    }
                    else 
                    {
                        complexProperties.SetValue(property, value); 
                    } 
                }
            } 
        }

        // Writes a collection of attributes representing inheritable properties
        // whose values has been affected by this element. 
        // Parameter onlyAffected=true means that we serialize only properties affected by
        // the current element; otherwise we output all known inheritable properties. 
        private static void WriteInheritableProperties(Type elementTypeStandardized, ITextPointer context, XmlWriter xmlWriter, bool onlyAffected, DependencyObject complexProperties) 
        {
            // Create a pointer positioned immediately outside the element 
            ITextPointer outerContext = null;
            if (onlyAffected)
            {
                outerContext = context.CreatePointer(); 
                outerContext.MoveToElementEdge(ElementEdge.BeforeStart);
            } 
 
            DependencyProperty[] inheritableProperties = TextSchema.GetInheritableProperties(elementTypeStandardized);
 
            for (int i = 0; i < inheritableProperties.Length; i++)
            {
                DependencyProperty property = inheritableProperties[i];
 
                object innerValue = context.GetValue(property);
                if (innerValue == null) 
                { 
                    // Some properties like Foreground may have null as default value.
                    // Skip them. 
                    continue;
                }

                object outerValue = null; 
                if (onlyAffected)
                { 
                    outerValue = outerContext.GetValue(property); 
                }
 
                // The property must appear in markup if the element
                if (!onlyAffected ||  // all properties requested for saving context on root
                    !TextSchema.ValuesAreEqual(innerValue, outerValue)) // or the element really affects the property
                { 
                    string stringValue = DPTypeDescriptorContext.GetStringValue(property, innerValue);
 
                    if (stringValue != null) 
                    {
                        stringValue = FilterNaNStringValueForDoublePropertyType(stringValue, property.PropertyType); 

                        string propertyName;
                        if (property == FrameworkContentElement.LanguageProperty)
                        { 
                            // Special case for CultureInfo property that must be represented in xaml as xml:lang attribute
                            propertyName = "xml:lang"; 
                        } 
                        else
                        { 
                            // Regular case: serialize a property with its own name
                            propertyName = GetPropertyNameForElement(property, elementTypeStandardized, /*forceComplexName:*/false);
                        }
                        xmlWriter.WriteAttributeString(propertyName, stringValue); 
                    }
                    else 
                    { 
                        complexProperties.SetValue(property, innerValue);
                    } 
                }
            }
        }
 

        // Writes a collection of attributes representing non-inheritable properties 
        // whose values are set inline on the given element instance. 
        // When we read properties fromContext we want all values including defaults; from text elements we only want only affected
        private static void WriteNoninheritableProperties(Type elementTypeStandardized, ITextPointer context, XmlWriter xmlWriter, bool onlyAffected, DependencyObject complexProperties) 
        {
            DependencyProperty[] elementProperties = TextSchema.GetNoninheritableProperties(elementTypeStandardized);

            // We'll need a pointer to walk the tree up when onlyAffected=false 
            ITextPointer parentContext = onlyAffected ? null : context.CreatePointer();
 
            for (int i = 0; i < elementProperties.Length; i++) 
            {
                DependencyProperty property = elementProperties[i]; 
                Type propertyOwnerType = context.ParentType;

                object propertyValue;
                if (onlyAffected) 
                {
                    // 
 
                    propertyValue = context.GetValue(property);
                } 
                else
                {
                    // This is request for contextual properties - use "manual" inheritance to collect values
                    Invariant.Assert(elementTypeStandardized == typeof(Span), "Request for contextual properties is expected for Span wrapper only"); 

                    // Get property value from this element or from one of its ancestors (the latter in case of !onlyAffeted) 
                    propertyValue = context.GetValue(property); 

                    // Get property value from its ancestors if the property is not set. 
                    // TextDecorationCollection is special-cased as its default is empty collection,
                    // and its value source cannot be distinguished from ITextPointer.
                    if (propertyValue == null || TextDecorationCollection.Empty.ValueEquals(propertyValue as TextDecorationCollection))
                    { 
                        if (property == Inline.BaselineAlignmentProperty || property == TextElement.TextEffectsProperty)
                        { 
                            // These properties do not make sense as contextual; do not include them into context. 
                            continue;
                        } 

                        parentContext.MoveToPosition(context);
                        while ((propertyValue == null || TextDecorationCollection.Empty.ValueEquals(propertyValue as TextDecorationCollection))
                            && typeof(Inline).IsAssignableFrom(parentContext.ParentType)) 
                        {
                            parentContext.MoveToElementEdge(ElementEdge.BeforeStart); 
                            propertyValue = parentContext.GetValue(property); 
                            propertyOwnerType = parentContext.ParentType;
                        } 
                    }
                }

                // 

 
 

 



                if ((property == Block.MarginProperty && (typeof(Paragraph).IsAssignableFrom(propertyOwnerType) || typeof(List).IsAssignableFrom(propertyOwnerType))) 
                    ||
                    (property == Block.PaddingProperty) && typeof(List).IsAssignableFrom(propertyOwnerType)) 
                { 
                    Thickness thickness = (Thickness)propertyValue;
                    if (Paragraph.IsMarginAuto(thickness)) 
                    {
                        continue;
                    }
                } 

                // Write the property as attribute string or add it to a list of complex properties. 
                WriteNoninheritableProperty(xmlWriter, property, propertyValue, propertyOwnerType, onlyAffected, complexProperties, context.ReadLocalValue(property)); 
            }
        } 

        // Writes a value of an individual non-inheritable property in form of attribute string.
        // If the value cannot be serialized as a string, adds the property to a collection of complexProperties.
        // To minimize the amount of xaml produced, the property is skipped if its value is equal to its default value 
        // for the given element type - the propertyOwnerType.
        // The flag onlyAffected=false means that we want to output all properties independently on 
        // if they are equal to their default values or not. 
        private static void WriteNoninheritableProperty(XmlWriter xmlWriter, DependencyProperty property, object propertyValue, Type propertyOwnerType, bool onlyAffected, DependencyObject complexProperties, object localValue)
        { 
            bool write = false;
            if (propertyValue != null &&
                propertyValue != DependencyProperty.UnsetValue)
            { 
                if (!onlyAffected)
                { 
                    write = true; 
                }
                else 
                {
                    PropertyMetadata metadata = property.GetMetadata(propertyOwnerType);

                    write = (metadata == null) || !(TextSchema.ValuesAreEqual(propertyValue, /*defaultValue*/metadata.DefaultValue) && localValue == DependencyProperty.UnsetValue); 
                }
            } 
 
            if (write)
            { 
                string stringValue = DPTypeDescriptorContext.GetStringValue(property, propertyValue);

                if (stringValue != null)
                { 
                    stringValue = FilterNaNStringValueForDoublePropertyType(stringValue, property.PropertyType);
 
                    // For the property name in this case we safe to use simple name only; 
                    // as noninheritable property would never require TypeName.PropertyName notation
                    // for attribute syntax. 
                    xmlWriter.WriteAttributeString(property.Name, stringValue);
                }
                else
                { 
                    complexProperties.SetValue(property, propertyValue);
                } 
            } 
        }
 
        // Writes a collection of attributes representing properties with local values set on them.
        // If the value cannot be serialized as a string, adds the property to a collection of complexProperties.
        private static void WriteLocallySetProperties(Type elementTypeStandardized, ITextPointer context, XmlWriter xmlWriter, DependencyObject complexProperties)
        { 
            TextPointer textPointer = context as TextPointer;
            if (textPointer == null) 
            { 
                // We can't have custom properties if we're not a TextPointer
                return; 
            }

            LocalValueEnumerator locallySetProperties = context.GetLocalValueEnumerator();
            DependencyProperty[] inheritableProperties = TextSchema.GetInheritableProperties(elementTypeStandardized); 
            DependencyProperty[] nonInheritableProperties = TextSchema.GetNoninheritableProperties(elementTypeStandardized);
 
            while (locallySetProperties.MoveNext()) 
            {
                DependencyProperty locallySetProperty = (DependencyProperty)locallySetProperties.Current.Property; 

                // Don't serialize read-only properties, or any properties registered or owned by a
                // a class in the framework (we only want to serialize custom properties), to be
                // consistent with our behavior for non-custom inlines. 
                //
                if (!locallySetProperty.ReadOnly && 
                    !IsPropertyKnown(locallySetProperty, inheritableProperties, nonInheritableProperties) && 
                    !TextSchema.IsKnownType(locallySetProperty.OwnerType))
                { 
                    object propertyValue = context.ReadLocalValue(locallySetProperty);
                    string stringValue = DPTypeDescriptorContext.GetStringValue(locallySetProperty, propertyValue);

                    if (stringValue != null) 
                    {
                        stringValue = FilterNaNStringValueForDoublePropertyType(stringValue, locallySetProperty.PropertyType); 
 
                        string propertyName = GetPropertyNameForElement(locallySetProperty, elementTypeStandardized, /*forceComplexName:*/false);
                        xmlWriter.WriteAttributeString(propertyName, stringValue); 
                    }
                    else
                    {
                        complexProperties.SetValue(locallySetProperty, propertyValue); 
                    }
                } 
            } 

// *** WE NEED TO BETTER UNDERSTAND THE IMPLICATIONS OF SERIALIZING NON-DP CLR PROPERTIES, SO THE REST OF 
// *** THIS METHOD IS DISABLED UNTIL WE DECIDE THE BEST WAY TO HANDLE THEM.
// *** CLRTypeDescriptorContext is essentially the same as DPTypeDescriptorContext.
#if false
            // Check all CLR properties 
            // Note that this is partially redundant.  TypeDescriptor.GetProperties, when called on a
            // DependencyObject, will return all properties that are set-- including all those already 
            // serialized as Inheritable, NonInheritable, or LocallySet properties.  A potential 
            // optimization, therefore, is to remove those serialization methods and simply use this one
            // for everything when we've opted into custom element serialization. 
            PropertyDescriptorCollection descriptorCollection = TypeDescriptor.GetProperties(textPointer.Parent);
            IEnumerator descriptors = descriptorCollection.GetEnumerator();
            while (descriptors.MoveNext())
            { 
                PropertyDescriptor current = (PropertyDescriptor)descriptors.Current;
                // ShouldSerializeValue() will return true for readonly properties that have explicitly 
                // been told to serialize, such as Span.Inlines.  If we serialize a read-only property, 
                // however, the parser will throw an exception when we try to deserialize.  So we
                // explicitly skip all read-only properties, and all DPs. 
                if (!current.ShouldSerializeValue(textPointer.Parent) || current.IsReadOnly || current is MS.Internal.ComponentModel.DependencyObjectPropertyDescriptor)
                {
                    continue;
                } 
                // Serialize the property
                object propertyValue = current.GetValue(textPointer.Parent); 
                if (propertyValue != null) 
                {
                    string stringValue = CLRTypeDescriptorContext.GetStringValue(current, propertyValue); 

                    if (stringValue != null)
                    {
                        stringValue = FilterNaNStringValueForDoublePropertyType(stringValue, current.PropertyType); 

                        xmlWriter.WriteAttributeString(current.Name, stringValue); 
                    } 
                    else
                    { 
                        //
                    }
                }
            } 
#endif
        } 
 
        private static bool IsPropertyKnown(DependencyProperty propertyToTest, DependencyProperty[] inheritableProperties, DependencyProperty[] nonInheritableProperties)
        { 
            for (int i = 0; i < inheritableProperties.Length; i++)
            {
                DependencyProperty property = inheritableProperties[i];
                if (property == propertyToTest) 
                {
                    return true; 
                } 
            }
            for (int i = 0; i < nonInheritableProperties.Length; i++) 
            {
                DependencyProperty property = nonInheritableProperties[i];
                if (property == propertyToTest)
                { 
                    return true;
                } 
            } 
            return false;
        } 

        /// 
        /// Writes complex properties in form of child elements with compound names
        ///  
        /// 
        /// To mask the security exception from XamlWriter.Save in partial trust case, 
        /// this function checks if the current call stack has unmanaged code permission. 
        /// 
        private static void WriteComplexProperties(XmlWriter xmlWriter, DependencyObject complexProperties, Type elementType) 
        {
            if (!SecurityHelper.CheckUnmanagedCodePermission())
            {
                // In partial trust, we cannot serialize any complex properties because 
                // XamlWriter.Save demands UnmanagedCodePermission.
                // 
                // If we're in PT, drop the properties. 
                //
                // If you're here debugging a lost complex property, consider adding 
                // code to DPTypeDescriptorContext to convert the complex property
                // into a non-complex property, or consider modifying XamlWriter.Save.
                return;
            } 

            LocalValueEnumerator properties = complexProperties.GetLocalValueEnumerator(); 
 
            properties.Reset();
            while (properties.MoveNext()) 
            {
                LocalValueEntry propertyEntry = properties.Current;

                // Build an appropriate name for the complex property 
                string complexPropertyName = GetPropertyNameForElement(propertyEntry.Property, elementType, /*forceComplexName:*/true);
 
                // Write the start element for the complex property. 
                xmlWriter.WriteStartElement(complexPropertyName);
 
                // Serialize the complex property value from SaveAsXml().
                string complexPropertyXml = System.Windows.Markup.XamlWriter.Save(propertyEntry.Value);

                // Write the serialized complext property value as Xml. 
                xmlWriter.WriteRaw(complexPropertyXml);
 
                // Close the element for the complex property 
                xmlWriter.WriteEndElement();
            } 
        }

        // Creates a name for the property which is consistent with xaml parser logic
        // When forceComplexName=true produces the TypeName.PropertyName notation unconditionally, 
        // otherwise such complex name is produced only when the TypeName is different from elementType.Name.
        private static string GetPropertyNameForElement(DependencyProperty property, Type elementType, bool forceComplexName) 
        { 
            string propertyName;
            if (DependencyProperty.FromName(property.Name, elementType) == property) 
            {
                // The elementType is an owner of this property, so we can use its name
                if (forceComplexName)
                { 
                    propertyName = elementType.Name + "." + property.Name;
                } 
                else 
                {
                    propertyName = property.Name; 
                }
            }
            else
            { 
                // The elementType does not own this property, so we use the property's registered owner type name.
                propertyName = property.OwnerType.Name + "." + property.Name; 
            } 

            return propertyName; 
        }

        // Serializes an element assuming that it does not have any children. Used for TableColumn
        // 
        private static void WriteXamlAtomicElement(DependencyObject element, XmlWriter xmlWriter, bool reduceElement)
        { 
            Type elementTypeStandardized = TextSchema.GetStandardElementType(element.GetType(), reduceElement); 
            DependencyProperty[] elementProperties = TextSchema.GetNoninheritableProperties(elementTypeStandardized);
 
            xmlWriter.WriteStartElement(elementTypeStandardized.Name);

            for (int i = 0; i < elementProperties.Length; i++)
            { 
                DependencyProperty property = elementProperties[i];
                object propertyValue = element.ReadLocalValue(property); 
                if (propertyValue != null && propertyValue != DependencyProperty.UnsetValue) 
                {
                    System.ComponentModel.TypeConverter typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(property.PropertyType); 
                    Invariant.Assert(typeConverter != null, "typeConverter==null: is not expected for atomic elements");
                    Invariant.Assert(typeConverter.CanConvertTo(typeof(string)), "type is expected to be convertable into string type");
                    string stringValue = (string)typeConverter.ConvertTo(/*ITypeDescriptorContext:*/null, CultureInfo.InvariantCulture, propertyValue, typeof(string));
                    Invariant.Assert(stringValue != null, "expecting non-null stringValue"); 
                    xmlWriter.WriteAttributeString(property.Name, stringValue);
                } 
            } 

            xmlWriter.WriteEndElement(); 
        }

        /// 
        /// Writes embeded object tag. 
        /// 
        ///  
        ///  
        /// 
        /// XmlWriter to output element opening tag. 
        /// 
        /// 
        /// 
        private static void WriteEmbeddedObject(object embeddedObject, XmlWriter xmlWriter, WpfPayload wpfPayload) 
        {
            if (wpfPayload != null && embeddedObject is Image) 
            { 
                // Writing in WPF mode: need to create an image with a Source referring into a package
                Image image = (Image)embeddedObject; 

                if (image.Source != null && !string.IsNullOrEmpty(image.Source.ToString()))
                {
                    // Add the image to the Image collection in the package 
                    // and define the reference to image into the package
                    string imageSource = wpfPayload.AddImage(image); 
                    if (imageSource != null) 
                    {
                        Type elementTypeStandardized = typeof(Image); 

                        // Write opening tag for the element
                        xmlWriter.WriteStartElement(elementTypeStandardized.Name);
 
                        // Write all properties except for Source
                        DependencyProperty[] imageProperties = TextSchema.ImageProperties; 
 
                        DependencyObject complexProperties = new DependencyObject();
 
                        for (int i = 0; i < imageProperties.Length; i++)
                        {
                            DependencyProperty property = imageProperties[i];
                            if (property != Image.SourceProperty) 
                            {
                                object value = image.GetValue(property); 
 
                                // Write the property as attribute string or add it to a list of complex properties.
                                WriteNoninheritableProperty(xmlWriter, property, value, elementTypeStandardized, /*onlyAffected:*/true, complexProperties, image.ReadLocalValue(property)); 
                            }
                        }

                        // Write Source property - as a local reference into the package container 
                        // Write Source property as the complex property to specify the BitmapImage
                        // cache option as "OnLoad" instead of the default "OnDeman". Otherwise, 
                        // we couldn't load the image by disposing WpfPayload package. 
                        xmlWriter.WriteStartElement(typeof(Image).Name + "." + Image.SourceProperty.Name);
                        xmlWriter.WriteStartElement(typeof(System.Windows.Media.Imaging.BitmapImage).Name); 
                        xmlWriter.WriteAttributeString(System.Windows.Media.Imaging.BitmapImage.UriSourceProperty.Name, imageSource);
                        xmlWriter.WriteAttributeString(System.Windows.Media.Imaging.BitmapImage.CacheOptionProperty.Name, "OnLoad");
                        xmlWriter.WriteEndElement();
                        xmlWriter.WriteEndElement(); 

                        // Write remaining complex properties 
                        WriteComplexProperties(xmlWriter, complexProperties, elementTypeStandardized); 

                        // Close the element 
                        xmlWriter.WriteEndElement();
                    }
                }
            } 
            else
            { 
                // In non-package mode we ignore all UIElements. 
                // Output a space replacing this embedded element.
                // Note that in this mode (DataFormats.Xaml) InlineUIContainer was 
                // replaced by Run and BlockUIContainer - by Paragraph,
                // so the space output here will be significant.
                xmlWriter.WriteString(" ");
            } 
        }
 
        // ............................................................. 
        //
        // Pasting 
        //
        // .............................................................

        // Handles a special case for pasting a single embedded element - 
        // needs to choose between BlockUIContainer and InlineUIContainer.
        private static bool PasteSingleEmbeddedElement(TextRange range, TextElement fragment) 
        { 
            if (fragment.ContentStart.GetOffsetToPosition(fragment.ContentEnd) == 3)
            { 
                TextElement uiContainer = fragment.ContentStart.GetAdjacentElement(LogicalDirection.Forward) as TextElement;
                FrameworkElement embeddedElement = null;
                if (uiContainer is BlockUIContainer)
                { 
                    embeddedElement = ((BlockUIContainer)uiContainer).Child as FrameworkElement;
                    if (embeddedElement != null) 
                    { 
                        ((BlockUIContainer)uiContainer).Child = null;
                    } 
                }
                else if (uiContainer is InlineUIContainer)
                {
                    embeddedElement = ((InlineUIContainer)uiContainer).Child as FrameworkElement; 
                    if (embeddedElement != null)
                    { 
                        ((InlineUIContainer)uiContainer).Child = null; 
                    }
                } 

                if (embeddedElement != null)
                {
                    range.InsertEmbeddedUIElement(embeddedElement); 
                    return true;
                } 
            } 

            return false; 
        }

        private static void PasteTextFragment(TextElement fragment, TextRange range)
        { 
            Invariant.Assert(range.IsEmpty, "range must be empty at this point - emptied by a caller");
            Invariant.Assert(fragment is Section || fragment is Span, "The wrapper element must be a Section or Span"); 
 
            // Define insertion position.
            TextPointer insertionPosition = TextRangeEditTables.EnsureInsertionPosition(range.End); 

            // Check if our insertion position has a non-splittable Inline ancestor such as Hyperlink element.
            // Since we cannot split such Inline, we must switch to Text mode for pasting.
            // Note that this also has the side effect of converting paragraph breaks to space characters. 
            if (insertionPosition.HasNonMergeableInlineAncestor)
            { 
                PasteNonMergeableTextFragment(fragment, range); 
            }
            else 
            {
                PasteMergeableTextFragment(fragment, range, insertionPosition);
            }
        } 

        // Helper for PasteTextFragment 
        private static void PasteNonMergeableTextFragment(TextElement fragment, TextRange range) 
        {
            // We cannot split Hyperlink or other non-splittable inline ancestor. 
            // Paste text content of fragment in such case.

            // Get text content to be pasted.
            string fragmentText = TextRangeBase.GetTextInternal(fragment.ElementStart, fragment.ElementEnd); 

            // Paste text into our empty target range. 
            // 
            range.Text = fragmentText;
 
            // Select pasted content
            range.Select(range.Start, range.End);
        }
 
        // Helper for PasteTextFragment
        private static void PasteMergeableTextFragment(TextElement fragment, TextRange range, TextPointer insertionPosition) 
        { 
            TextPointer fragmentStart;
            TextPointer fragmentEnd; 

            if (fragment is Span)
            {
                // Split structure at insertion point in target 
                insertionPosition = TextRangeEdit.SplitFormattingElements(insertionPosition, /*keepEmptyFormatting:*/false);
                Invariant.Assert(insertionPosition.Parent is Paragraph, "insertionPosition must be in a scope of a Paragraph after splitting formatting elements"); 
 
                // Move the whole Span into the insertion point
                fragment.RepositionWithContent(insertionPosition); 

                // Store edge positions of inserted content
                fragmentStart = fragment.ElementStart;
                fragmentEnd = fragment.ElementEnd; 

                // Remove wrapper from a tree 
                fragment.Reposition(null, null); 
                ValidateMergingPositions(typeof(Inline), fragmentStart, fragmentEnd);
 
                // Transfer inheritable contextual properties
                ApplyContextualProperties(fragmentStart, fragmentEnd, fragment);
            }
            else 
            {
                // Correct leading nested List elements in the fragment 
                CorrectLeadingNestedLists((Section)fragment); 

                // Split a paragraph at insertion position 
                bool needFirstParagraphMerging = SplitParagraphForPasting(ref insertionPosition);

                // Move the whole Section into the insertion point
                fragment.RepositionWithContent(insertionPosition); 

                // Store edge positions of inserted content 
                fragmentStart = fragment.ElementStart; 
                fragmentEnd = fragment.ElementEnd.GetPositionAtOffset(0, LogicalDirection.Forward); // need forward orientation to stick with the following content during merge at fragmentStart position
 
                // And unwrap the root Section
                fragment.Reposition(null, null);
                ValidateMergingPositions(typeof(Block), fragmentStart, fragmentEnd);
 
                // Transfer inheritable contextual properties
                ApplyContextualProperties(fragmentStart, fragmentEnd, fragment); 
 
                // Merge paragraphs on fragment boundaries
                if (needFirstParagraphMerging) 
                {
                    MergeParagraphsAtPosition(fragmentStart, /*mergingOnFragmentStart:*/true);
                }
 
                // Get an indication that we need to merge last paragraph
                if (!((Section)fragment).HasTrailingParagraphBreakOnPaste) 
                { 
                    MergeParagraphsAtPosition(fragmentEnd, /*mergingOnFragmentStart:*/false);
                } 
            }

            //
 
            // For paragraph pasting move range end to the following paragraph, because
            // it must include an ending paragraph break (in case of no-merging) 
            if (fragment is Section && ((Section)fragment).HasTrailingParagraphBreakOnPaste) 
            {
                fragmentEnd = fragmentEnd.GetInsertionPosition(LogicalDirection.Forward); 
            }

            // Select pasted content
            range.Select(fragmentStart, fragmentEnd); 
        }
 
        // Removes nested ListItems in the beginning of a fragment 
        // to avoid multiple bulleting.
        // 
        private static void CorrectLeadingNestedLists(Section fragment)
        {
            List list = fragment.Blocks.FirstBlock as List;
            while (list != null) 
            {
                ListItem listItem = list.ListItems.FirstListItem; 
                if (listItem == null) 
                {
                    return; 
                }

                if (listItem.NextListItem != null)
                { 
                    return;
                } 
 
                List nestedList = listItem.Blocks.FirstBlock as List;
                if (nestedList == null) 
                {
                    return;
                }
 
                // So we have nested list in the very beginning of the outer single-item list:
                // remove that outer list 
                listItem.Reposition(null, null); 
                list.Reposition(null, null);
 
                list = nestedList;
            }
        }
 
        // Decides whether we need to split a paragraph before pasting a fragment or not.
        // Splits the paragraph if needed, or simply moves the insertionPosition before its start. 
        // Returns true if splitting happened and consequently merging is required after pasting. 
        private static bool SplitParagraphForPasting(ref TextPointer insertionPosition)
        { 
            bool needFirstParagraphMerging = true; // we need splitting unless the position os at the bery beginniong of a paragraph

            // When the insertion position is at the beginning of a paragraph we can avoid
            // splitting and then merging paragraphs at fragment start position. 
            // This is not a pref consideration. We do not want an empty paragraph
            // would kill a formatting of a first pasted paragraphs (say, ListItem of a pasted List). 
            TextPointer positionBeforeParagraph = insertionPosition; 
            // Skip formatting tags
            while (positionBeforeParagraph.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart && 
                TextSchema.IsFormattingType(positionBeforeParagraph.Parent.GetType()))
            {
                positionBeforeParagraph = positionBeforeParagraph.GetNextContextPosition(LogicalDirection.Backward);
            } 
            while (positionBeforeParagraph.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart &&
                TextSchema.AllowsParagraphMerging(positionBeforeParagraph.Parent.GetType())) 
            { 
                needFirstParagraphMerging = false;
                positionBeforeParagraph = positionBeforeParagraph.GetNextContextPosition(LogicalDirection.Backward); 
            }
            if (!needFirstParagraphMerging)
            {
                // Insertion position was in the beginning of a paragraph. 
                // No need in splitting/merging at fragment start
                insertionPosition = positionBeforeParagraph; 
            } 
            else
            { 
                // split paragraph to create an insertion positionn at block level
                insertionPosition = TextRangeEdit.InsertParagraphBreak(insertionPosition, /*moveIntoSecondParagraph:*/false);
            }
 
            // When insertionPosition is inside a ListItem, then InsertParagraphBreak will
            // split not only a parent Paragraph but also a ListItem and return a position 
            // between ListItems. This position is not good for inserting Block elements, 
            // so we also need to split parent List element.
            // In a case when insertionPosition was at the beginning of a paragraph, 
            // we still can end up being between ListItems, so again need to split a parent List.
            if (insertionPosition.Parent is List)
            {
                insertionPosition = TextRangeEdit.SplitElement(insertionPosition); 
            }
 
            return needFirstParagraphMerging; 
        }
 
        // Merges two paragraphs preceding and following the given position
        private static void MergeParagraphsAtPosition(TextPointer position, bool mergingOnFragmentStart)
        {
            TextPointer navigator = position; 
            while (navigator != null && !(navigator.Parent is Paragraph))
            { 
                if (navigator.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementEnd) 
                {
                    navigator = navigator.GetNextContextPosition(LogicalDirection.Backward); 
                }
                else
                {
                    navigator = null; 
                }
            } 
 
            if (navigator != null)
            { 
                Invariant.Assert(navigator.Parent is Paragraph, "We suppose have a first paragraph found");
                Paragraph firstParagraph = (Paragraph)navigator.Parent;

                navigator = position; 
                while (navigator != null && !(navigator.Parent is Paragraph))
                { 
                    if (navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) 
                    {
                        navigator = navigator.GetNextContextPosition(LogicalDirection.Forward); 
                    }
                    else
                    {
                        navigator = null; 
                    }
                } 
 
                if (navigator != null)
                { 
                    Invariant.Assert(navigator.Parent is Paragraph, "We suppose a second paragraph found");
                    Paragraph secondParagraph = (Paragraph)navigator.Parent;

                    if (TextRangeEditLists.ParagraphsAreMergeable(firstParagraph, secondParagraph)) 
                    {
                        TextRangeEditLists.MergeParagraphs(firstParagraph, secondParagraph); 
                    } 
                    else if (mergingOnFragmentStart && firstParagraph.TextRange.IsEmpty)
                    { 
                        firstParagraph.RepositionWithContent(null);
                    }
                    else if (!mergingOnFragmentStart && secondParagraph.TextRange.IsEmpty)
                    { 
                        secondParagraph.RepositionWithContent(null);
                    } 
                } 
            }
        } 

        // Validates that the sibling element at this position belong to expected itemType (Inline, Block, ListItem)
        private static void ValidateMergingPositions(Type itemType, TextPointer start, TextPointer end)
        { 
            if (start.CompareTo(end) < 0)
            { 
                // Verify inner part 
                TextPointerContext forwardFromStart = start.GetPointerContext(LogicalDirection.Forward);
                TextPointerContext backwardFromEnd = end.GetPointerContext(LogicalDirection.Backward); 
                Invariant.Assert(forwardFromStart == TextPointerContext.ElementStart, "Expecting first opening tag of pasted fragment");
                Invariant.Assert(backwardFromEnd == TextPointerContext.ElementEnd, "Expecting last closing tag of pasted fragment");
                Invariant.Assert(itemType.IsAssignableFrom(start.GetAdjacentElement(LogicalDirection.Forward).GetType()), "The first pasted fragment item is expected to be a " + itemType.Name);
                Invariant.Assert(itemType.IsAssignableFrom(end.GetAdjacentElement(LogicalDirection.Backward).GetType()), "The last pasted fragment item is expected to be a " + itemType.Name); 

                // Veryfy outer part 
                TextPointerContext backwardFromStart = start.GetPointerContext(LogicalDirection.Backward); 
                TextPointerContext forwardFromEnd = end.GetPointerContext(LogicalDirection.Forward);
                Invariant.Assert(backwardFromStart == TextPointerContext.ElementStart || backwardFromStart == TextPointerContext.ElementEnd || backwardFromStart == TextPointerContext.None, "Bad context preceding a pasted fragment"); 
                Invariant.Assert(!(backwardFromStart == TextPointerContext.ElementEnd) || itemType.IsAssignableFrom(start.GetAdjacentElement(LogicalDirection.Backward).GetType()), "An element preceding a pasted fragment is expected to be a " + itemType.Name);
                Invariant.Assert(forwardFromEnd == TextPointerContext.ElementStart || forwardFromEnd == TextPointerContext.ElementEnd || forwardFromEnd == TextPointerContext.None, "Bad context following a pasted fragment");
                Invariant.Assert(!(forwardFromEnd == TextPointerContext.ElementStart) || itemType.IsAssignableFrom(end.GetAdjacentElement(LogicalDirection.Forward).GetType()), "An element following a pasted fragment is expected to be a " + itemType.Name);
            } 
        }
 
        // Helper function used to set default value for an indicator requesting to merge last paragraph. 
        private static void AdjustFragmentForTargetRange(TextElement fragment, TextRange range)
        { 
            if (fragment is Section && ((Section)fragment).HasTrailingParagraphBreakOnPaste)
            {
                // Explicit indicator is missing, we need to set it by default.
                // In a case of TextRange.Xml property assignment we assume that 
                // user expects to insert as many paragraphs new paragraphs as her pasted xaml contains.
                // The expection must be done to the case when the target range is 
                // extended beyond the last paragraph - then we must merge last paragraph 
                // to avoid extra paragraph creation at the end (one additional paragraph
                // will be created in this case by Pasting code before pasting). 
                // The other case for exception is when target TextContainer is empty -
                // in this case we as well want to merge last paragraph with the following
                // one (which will be created as part of paragraph enforcement in pasting operation).
                // The both desired conditions - IsAfterLastParagraph and "in empty container" 
                // can be identified by the following simple test - range.End is not at end-of-doc.
                ((Section)fragment).HasTrailingParagraphBreakOnPaste = range.End.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None; 
            } 
        }
 
        // Applies a whole property bag to a range from start to end to simulate inheritance of this property from source conntext
        private static void ApplyContextualProperties(TextPointer start, TextPointer end, TextElement propertyBag)
        {
            Invariant.Assert(propertyBag.IsEmpty && propertyBag.Parent == null, "propertyBag is supposed to be an empty element outside any tree"); 

            LocalValueEnumerator contextualProperties = propertyBag.GetLocalValueEnumerator(); 
 
            while (start.CompareTo(end) < 0 && contextualProperties.MoveNext())
            { 
                // Note: we repeatedly check for IsEmpty because the selection
                // may become empty as a result of normalization after formatting
                // (thai character sequence).
                LocalValueEntry propertyEntry = contextualProperties.Current; 
                DependencyProperty property = propertyEntry.Property;
                if (TextSchema.IsCharacterProperty(property) && 
                    TextSchema.IsParagraphProperty(property)) 
                {
                    // In case a property is both an Inline and Paragraph property, 
                    // propertyBag element type (section or span) decides how it should be applied.
                    if (TextSchema.IsBlock(propertyBag.GetType()))
                    {
                        ApplyContextualProperty(typeof(Block), start, end, property, propertyEntry.Value); 
                    }
                    else 
                    { 
                        ApplyContextualProperty(typeof(Inline), start, end, property, propertyEntry.Value);
                    } 
                }
                else if (TextSchema.IsCharacterProperty(property))
                {
                    ApplyContextualProperty(typeof(Inline), start, end, property, propertyEntry.Value); 
                }
                else if (TextSchema.IsParagraphProperty(property)) 
                { 
                    ApplyContextualProperty(typeof(Block), start, end, property, propertyEntry.Value);
                } 
            }

            // Merge formatting elements at end position
            TextRangeEdit.MergeFormattingInlines(start); 
            TextRangeEdit.MergeFormattingInlines(end);
        } 
 
        // Applies one property to a range from start to end to simulate inheritance of this property from source conntext
        private static void ApplyContextualProperty(Type targetType, TextPointer start, TextPointer end, DependencyProperty property, object value) 
        {
            if (TextSchema.ValuesAreEqual(start.Parent.GetValue(property), value))
            {
                return; // The property at insertion position is the same as it was in source context. Nothing to do. 
            }
 
            // Advance start pointer to enter pasted fragment 
            start = start.GetNextContextPosition(LogicalDirection.Forward);
 
            while (start != null && start.CompareTo(end) < 0)
            {
                TextPointerContext passedContext = start.GetPointerContext(LogicalDirection.Backward);
                if (passedContext == TextPointerContext.ElementStart) 
                {
                    TextElement element = (TextElement)start.Parent; 
 
                    // Check if this element affects the property in question
                    if (element.ReadLocalValue(property) != DependencyProperty.UnsetValue || 
                        !TextSchema.ValuesAreEqual(element.GetValue(property), element.Parent.GetValue(property)))
                    {
                        // The element affects this property, so we can skip it
                        start = element.ElementEnd; 
                    }
                    else if (targetType.IsAssignableFrom(element.GetType())) 
                    { 
                        start = element.ElementEnd;
 
                        if (targetType == typeof(Block) && start.CompareTo(end) > 0)
                        {
                            // Contextual properties should not apply to the last paragraph
                            // when it is merged with the following content - 
                            // to avoid affecting the folowing visible content formatting.
                            break; 
                        } 

                        // This is topmost-level inline element which inherits this property. 
                        // Set the value explicitly
                        if (!TextSchema.ValuesAreEqual(value, element.GetValue(property)))
                        {
                            element.ClearValue(property); 
                            if (!TextSchema.ValuesAreEqual(value, element.GetValue(property)))
                            { 
                                element.SetValue(property, value); 
                            }
 
                            TextRangeEdit.MergeFormattingInlines(element.ElementStart);
                        }
                    }
                    else 
                    {
                        // Traverse down into a structured (non-innline) element 
                        start = start.GetNextContextPosition(LogicalDirection.Forward); 
                    }
                } 
                else
                {
                    // Traverse up from any element
                    Invariant.Assert(passedContext != TextPointerContext.None, "TextPointerContext.None is not expected"); 
                    start = start.GetNextContextPosition(LogicalDirection.Forward);
                } 
            } 
        }
 
        // Returns a navigator scoped in the common ancestor for the start and end positions
        // The navigator is positioned in the beginning of the ancestor's content.
        // Modifies the common ancestor for hyperlink serialization heuristic - in case when the range is positioned at hyperlink boundaries.
        // Since we need to write a hyperlink wrapper in this case, navigator is positioned before hyperlink element start. 
        private static ITextPointer FindSerializationCommonAncestor(ITextRange range)
        { 
            // Create navigators for tree traversing looking for commonAncestor 
            ITextPointer commonAncestor = range.Start.CreatePointer();
            ITextPointer runningEnd = range.End.CreatePointer(); 

            // Find nearest common ancestor
            while (!commonAncestor.HasEqualScope(runningEnd))
            { 
                // Run all way from end up the tree to check if start is ancestor
                runningEnd.MoveToPosition(range.End); 
                while (typeof(TextElement).IsAssignableFrom(runningEnd.ParentType) && !runningEnd.HasEqualScope(commonAncestor)) 
                {
                    runningEnd.MoveToElementEdge(ElementEdge.AfterEnd); 
                }

                if (runningEnd.HasEqualScope(commonAncestor))
                { 
                    break;
                } 
 
                // Move start one level up
                commonAncestor.MoveToElementEdge(ElementEdge.BeforeStart); 
            }

            while (!IsAcceptableAncestor(commonAncestor, range))
            { 
                commonAncestor.MoveToElementEdge(ElementEdge.BeforeStart);
            } 
 
            if (typeof(TextElement).IsAssignableFrom(commonAncestor.ParentType))
            { 
                commonAncestor.MoveToElementEdge(ElementEdge.AfterStart);

                // Check for special case, when range start and end are at Hyperlink boundaries.
                // Need to expand commonAncestor to Hyperlink element start, so that Hyperlink wrapper is written. 
                ITextPointer hyperlinkStart = GetHyperlinkStart(range);
                if (hyperlinkStart != null) 
                { 
                    commonAncestor = hyperlinkStart;
                } 
            }
            else
            {
                commonAncestor.MoveToPosition(commonAncestor.TextContainer.Start); 
            }
 
            return commonAncestor; 
        }
 
        // Verify that a pointer is an acceptable ancestor.  Some types can't be ancestors at all, while
        // non-typographic-only elements are unacceptable if the range being serialized does not include the
        // element's start and end (because we don't want to serialize properties on such an element).
        private static bool IsAcceptableAncestor(ITextPointer commonAncestor, ITextRange range) 
        {
            if (typeof(TableRow).IsAssignableFrom(commonAncestor.ParentType) || 
                typeof(TableRowGroup).IsAssignableFrom(commonAncestor.ParentType) || 
                typeof(Table).IsAssignableFrom(commonAncestor.ParentType) ||
                typeof(BlockUIContainer).IsAssignableFrom(commonAncestor.ParentType) || 
                typeof(List).IsAssignableFrom(commonAncestor.ParentType) ||
                typeof(Inline).IsAssignableFrom(commonAncestor.ParentType) && TextSchema.HasTextDecorations(commonAncestor.GetValue(Inline.TextDecorationsProperty)))
            {
                return false; 
            }
 
            // We don't want to use any formatting from within a non-typographic-only element unless the entire 
            // element is selected (in which case, the ancestor candidate will already be outside that element.
            // If there is such an element ANYWHERE in the ancestry, the only acceptable 
            // ancestor is outside the outermost such element.
            ITextPointer navigator = commonAncestor.CreatePointer();
            while (typeof(TextElement).IsAssignableFrom(navigator.ParentType))
            { 
                TextElementEditingBehaviorAttribute behaviorAttribute = (TextElementEditingBehaviorAttribute)Attribute.GetCustomAttribute(navigator.ParentType, typeof(TextElementEditingBehaviorAttribute));
                if (behaviorAttribute != null && !behaviorAttribute.IsTypographicOnly) 
                { 
                    return false;
                } 
                navigator.MoveToElementEdge(ElementEdge.BeforeStart);
            }

            return true; 
        }
 
        // Removes (inplace) any invalid surrogate chars from an array. 
        // Returns the new array length, text.Length minus any stripped
        // chars. 
        //
        // Unicode surrogates are 32 bit references to abstract chars.
        // A valid surrogate pair consists of a 16 bit code point (the
        // high surrogate) in the range u+d800 - u+dbff, followed by a 
        // second 16 bit code point (the low surrogate) in the range
        // u+dc00 - u+dfff. 
        // 
        // An invalid surrogate is a high surrogate not followed by a
        // low surrogate, or a low surrogate not preceeded by a high 
        // surrogate.
        //
        // Length specifies the number of characters in text to examine,
        // characters past length are ignored (as if text.Length == length). 
        //
        // Also removes nul characters 
        private static int StripInvalidSurrogateChars(char[] text, int length) 
        {
            int count; 

            Invariant.Assert(text.Length >= length, "Asserting that text.Length >= length");

            int i; 
            for (i = 0; i < length; i++)
            { 
                char testChar = text[i]; 
                if (Char.IsHighSurrogate(testChar) || Char.IsLowSurrogate(testChar) || IsBadCode(testChar))
                { 
                    break;
                }
            }
 
            if (i == length)
            { 
                // No surrogates, early out. 
                count = length;
            } 
            else
            {
                count = i;
 
                for (; i < length; i++)
                { 
                    if (Char.IsHighSurrogate(text[i])) 
                    {
                        if (i + 1 < length && Char.IsLowSurrogate(text[i + 1])) 
                        {
                            // Valid surrogate encountered.
                            text[count] = text[i];
                            text[count + 1] = text[i + 1]; 
                            count += 2;
                            i++; // Skip over low surrogate. 
                        } 
                        else
                        { 
                            // Bogus high surrogte encountered -- remove it.
                            // Simply don't update destinationIndex or count.
                        }
                    } 
                    else if (Char.IsLowSurrogate(text[i]))
                    { 
                        // Bogus low surrogate encountered -- remove it. 
                        // Simply don't update destinationIndex or count.
                    } 
                    else if (IsBadCode(text[i]))
                    {
                        // nul character enountered - remove it.
                        // Simply don't update destinationIndex or count. 
                        //
                    } 
                    else 
                    {
                        // Non-surrogate encountered. 
                        text[count] = text[i];
                        count += 1;
                    }
                } 
            }
 
            return count; 
        }
 
        private static bool IsBadCode(char code)
        {
            return (code < ' ' && code != '\x0009' && code != '\x000A' && code != '\x000D');
        } 

        ///  
        /// Return true if rangeEnd is not at the end of an element. 
        ///
        /// textReader must already be at the start of the element. 
        /// 
        private static bool IsPartialNonTypographic(ITextPointer textReader, ITextPointer rangeEnd)
        {
            bool isPartial = false; 

            Invariant.Assert(textReader.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart); 
 
            ITextPointer elementNavigation = textReader.CreatePointer();
            ITextPointer elementEnd = textReader.CreatePointer(); 

            elementEnd.MoveToNextContextPosition(LogicalDirection.Forward);

            // Find the end position 
            elementEnd.MoveToElementEdge(ElementEdge.AfterEnd);
 
            if (elementEnd.CompareTo(rangeEnd) > 0) 
            {
                isPartial = true; 
            }

            return isPartial;
        } 

        ///  
        /// Return true if Hyperlink range is invalid. 
        /// Hyperlink is invalid if it include a UiElement except Image or the range end position
        /// is stated before the end position of hyperlink. 
        /// This must be called before Hyperlink start element position.
        /// 
        private static bool IsHyperlinkInvalid(ITextPointer textReader, ITextPointer rangeEnd)
        { 
            // TextRead must be on the position before the element start position of Hyperlink
            Invariant.Assert(textReader.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart); 
            Invariant.Assert(typeof(Hyperlink).IsAssignableFrom(textReader.GetElementType(LogicalDirection.Forward))); 

            bool hyperlinkInvalid = false; 

            // Get the forward adjacent element and cast Hyperlink hardly since it must be Hyperlink
            Hyperlink hyperlink = (Hyperlink)textReader.GetAdjacentElement(LogicalDirection.Forward);
 
            ITextPointer hyperlinkNavigation = textReader.CreatePointer();
            ITextPointer hyperlinkEnd = textReader.CreatePointer(); 
 
            hyperlinkEnd.MoveToNextContextPosition(LogicalDirection.Forward);
 
            // Find the hyperlink end position
            hyperlinkEnd.MoveToElementEdge(ElementEdge.AfterEnd);

            // Hyperlink end position is stated after the range end position. 
            if (hyperlinkEnd.CompareTo(rangeEnd) > 0)
            { 
                hyperlinkInvalid = true; 
            }
            else 
            {
                // Check whether the hyperlink having a UiElement except Image until hyperlink end position
                while (hyperlinkNavigation.CompareTo(hyperlinkEnd) < 0)
                { 
                    InlineUIContainer inlineUIContainer = hyperlinkNavigation.GetAdjacentElement(LogicalDirection.Forward) as InlineUIContainer;
                    if (inlineUIContainer != null && !(inlineUIContainer.Child is Image)) 
                    { 
                        hyperlinkInvalid = true;
                        break; 
                    }

                    hyperlinkNavigation.MoveToNextContextPosition(LogicalDirection.Forward);
                } 
            }
 
            return hyperlinkInvalid; 
        }
 
        // Returns a position before hyperlink element start if passed range start and end are at hyperlink boundaries. Otherwise, null.
        private static ITextPointer GetHyperlinkStart(ITextRange range)
        {
            ITextPointer hyperlinkStart = null; 

            if (TextPointerBase.IsAtNonMergeableInlineStart(range.Start) && TextPointerBase.IsAtNonMergeableInlineEnd(range.End)) 
            { 
                // Find a position at hyperlink start.
                hyperlinkStart = range.Start.CreatePointer(LogicalDirection.Forward); 
                while (hyperlinkStart.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart &&
                    !typeof(Hyperlink).IsAssignableFrom(hyperlinkStart.ParentType))
                {
                    hyperlinkStart.MoveToElementEdge(ElementEdge.BeforeStart); 
                }
                hyperlinkStart.MoveToElementEdge(ElementEdge.BeforeStart); 
                hyperlinkStart.Freeze(); 
            }
 
            return hyperlinkStart;
        }

        // 
        private static string FilterNaNStringValueForDoublePropertyType(string stringValue, Type propertyType)
        { 
            if (propertyType == typeof(double) && 
                String.Compare(stringValue, "NaN", StringComparison.OrdinalIgnoreCase) == 0)
            { 
                return "Auto"; // convert NaN to Auto, to keep parser happy
            }

            return stringValue; 
        }
 
        #endregion Private Methods 

        // -------------------------------------------------------------- 
        //
        // Private Constants
        //
        // -------------------------------------------------------------- 

        #region Private Constants 
 
        // A structural depth of a empty FlowDocument fragment
        private const int EmptyDocumentDepth = 1; 

        #endregion Private Constants
    }
} 

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