File: System\Windows\Documents\FixedPage.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Documents.DocumentStructures;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MS.Internal.Documents;
using MS.Internal.Utility;
 
using BuildInfo = MS.Internal.PresentationFramework.BuildInfo;
 
//
// Description:
//      Implements the FixedPage element
// Spec FixedPanelPage.mht
//
 
namespace System.Windows.Documents
{
    //=====================================================================
    /// <summary>
    /// FixedPage is the container element for a metafile that represents
    /// a single page of portable, high-fidelity content.
    ///
    /// As an object that represents a static page of content, the primary
    /// usage scenario for a FixedPage is inside a FixedDocument, a control
    /// that is specialized to represent FixedPages to the pagination architecture.
    /// The secondary scenario is to place a FixedPage inside a generic paginating
    /// control such as the FlowDocument; for this scenario, the FixedPage is configured
    /// to automatically set page breaks at the beginning and end of its content.
    /// </summary>
    [ContentProperty("Children")]
    public sealed class FixedPage : FrameworkElement, IAddChildInternal, IFixedNavigate, IUriContext
    {
        //--------------------------------------------------------------------
        //
        // Constructors
        //
        //---------------------------------------------------------------------
 
        #region Constructors
        static FixedPage()
        {
            FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(FlowDirection.LeftToRight, FrameworkPropertyMetadataOptions.AffectsParentArrange);
            metadata.CoerceValueCallback = new CoerceValueCallback(CoerceFlowDirection);
            FlowDirectionProperty.OverrideMetadata(typeof(FixedPage), metadata);
            // This puts the origin always at the top left of the page and prevents mirroring unless this is overridden.
        }
 
        /// <summary>
        ///     Default FixedPage constructor
        /// </summary>
        /// <remarks>
        ///     Automatic determination of current Dispatcher. Use alternative constructor
        ///     that accepts a Dispatcher for best performance.
        /// </remarks>
        public FixedPage() : base()
        {
            Init();
        }
        #endregion Constructors
 
 
        //--------------------------------------------------------------------
        //
        // Public Methods
        //
        //---------------------------------------------------------------------
 
        #region Public Methods
 
        /// <summary>
        /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
        /// </summary>
        protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
        {
            return new System.Windows.Automation.Peers.FixedPageAutomationPeer(this);
        }
 
        /// <summary>
        /// Responds to mouse wheel event, used to update debug visuals.
        /// MouseWheelEvent handler, initializes the context menu.
        /// </summary>
        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
#if DEBUG
            if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
            {
                int delta = e.Delta;
                e.Handled = true;
 
                if (delta > 0)
                {
                    _drawDebugVisual--;
                }
                else
                {
                    _drawDebugVisual++;
                }
 
                _drawDebugVisual = _drawDebugVisual % (int)DrawDebugVisual.LastOne;
 
                if (_drawDebugVisual < 0)
                {
                    _drawDebugVisual += (int)DrawDebugVisual.LastOne;
                }
 
                InvalidateVisual();
 
                //
                // For container, the first child of element is always a Path with Fill.
                //
                if (_uiElementCollection.Count != 0)
                {
                    Path path = _uiElementCollection[0] as Path;
                    if (path != null)
                    {
                        if (_drawDebugVisual == 0)
                        {
                            path.Visibility = Visibility.Visible;
                        }
                        else
                        {
                            path.Visibility = Visibility.Hidden;
                        }
                    }
                }
            }
#endif
        }
 
        /// <summary>
        /// Override from UIElement
        /// </summary>
        protected override void OnRender(DrawingContext dc)
        {
            // Draw background in rectangle inside border.
            Brush background = this.Background;
            if (background != null)
            {
                dc.DrawRectangle(background,
                                 null,
                                 new Rect(0, 0, RenderSize.Width, RenderSize.Height));
            }
#if DEBUG
 
            AdornerLayer al = AdornerLayer.GetAdornerLayer(this);
 
            if (al != null)
            {
                Adorner[] adorners = al.GetAdorners(this);
                if (adorners != null && adorners.Length > 0)
                {
                    al.Update(this);
                }
            }
 
#endif
        }
 
        ///<summary>
        /// This method is called to Add the object as a child of the Panel.  This method is used primarily
        /// by the parser.
        ///</summary>
        /// <exception cref="ArgumentNullException">value is NULL.</exception>
        /// <exception cref="ArgumentException">value is not of type UIElement.</exception>
        ///<param name="value">
        /// The object to add as a child; it must be a UIElement.
        ///</param>
        /// <ExternalAPI/>
        void IAddChild.AddChild (Object value)
        {
            ArgumentNullException.ThrowIfNull(value);
 
            UIElement uie = value as UIElement;
 
            if (uie == null)
            {
                throw new ArgumentException(SR.Format(SR.UnexpectedParameterType, value.GetType(), typeof(UIElement)), "value");
            }
 
            Children.Add(uie);
        }
 
        ///<summary>
        /// This method is called by the parser when text appears under the tag in markup.
        /// As default Panels do not support text, calling this method has no effect if the
        /// text is all whitespace.  Passing non-whitespace text throws an exception.
        ///</summary>
        /// <exception cref="ArgumentException">text contains non-whitespace text.</exception>
        ///<param name="text">
        /// Text to add as a child.
        ///</param>
        void IAddChild.AddText (string text)
        {
            XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this);
        }
 
        /// <summary>
        /// Reads the attached property Left from the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element from which to read the Left attached property.</param>
        /// <returns>The property's Length value.</returns>
        /// <seealso cref="Canvas.LeftProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetLeft(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(LeftProperty);
        }
 
        /// <summary>
        /// Writes the attached property Left to the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element to which to write the Left attached property.</param>
        /// <param name="length">The Length to set</param>
        /// <seealso cref="Canvas.LeftProperty" />
        public static void SetLeft(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(LeftProperty, length);
        }
 
        /// <summary>
        /// Reads the attached property Top from the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element from which to read the Top attached property.</param>
        /// <returns>The property's Length value.</returns>
        /// <seealso cref="Canvas.TopProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetTop(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(TopProperty);
        }
 
        /// <summary>
        /// Writes the attached property Top to the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element to which to write the Top attached property.</param>
        /// <param name="length">The Length to set</param>
        /// <seealso cref="Canvas.TopProperty" />
        public static void SetTop(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(TopProperty, length);
        }
 
        /// <summary>
        /// Reads the attached property Right from the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element from which to read the Right attached property.</param>
        /// <returns>The property's Length value.</returns>
        /// <seealso cref="Canvas.RightProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetRight(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(RightProperty);
        }
 
        /// <summary>
        /// Writes the attached property Right to the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element to which to write the Right attached property.</param>
        /// <param name="length">The Length to set</param>
        /// <seealso cref="Canvas.RightProperty" />
        public static void SetRight(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(RightProperty, length);
        }
 
        /// <summary>
        /// Reads the attached property Bottom from the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element from which to read the Bottom attached property.</param>
        /// <returns>The property's Length value.</returns>
        /// <seealso cref="Canvas.BottomProperty" />
        [TypeConverter($"System.Windows.LengthConverter, PresentationFramework, Version={BuildInfo.WCP_VERSION}, Culture=neutral, PublicKeyToken={BuildInfo.WCP_PUBLIC_KEY_TOKEN}, Custom=null")]
        [AttachedPropertyBrowsableForChildren()]
        public static double GetBottom(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (double)element.GetValue(BottomProperty);
        }
 
        /// <summary>
        /// Writes the attached property Bottom to the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <param name="element">The element to which to write the Bottom attached property.</param>
        /// <param name="length">The Length to set</param>
        /// <seealso cref="Canvas.BottomProperty" />
        public static void SetBottom(UIElement element, double length)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(BottomProperty, length);
        }
 
        /// <summary>
        /// Reads the attached property NavigateUri from the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <remarks>Should be kept here for compatibility since the attached property has moved from FixedPage to Hyperlink.</remarks>
        [AttachedPropertyBrowsableForChildren()]
        public static Uri GetNavigateUri(UIElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (Uri)element.GetValue(NavigateUriProperty);
        }
 
        /// <summary>
        /// Writes the attached property NavigateUri to the given element.
        /// </summary>
        /// <exception cref="ArgumentNullException">element is NULL.</exception>
        /// <remarks>Should be kept here for compatibility since the attached property has moved from FixedPage to Hyperlink.</remarks>
        public static void SetNavigateUri(UIElement element, Uri uri)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(NavigateUriProperty, uri);
        }
 
        #endregion
 
        #region IUriContext
        /// <summary>
        /// <see cref="IUriContext.BaseUri" />
        /// </summary>
        Uri IUriContext.BaseUri
        {
            get { return (Uri) GetValue(BaseUriHelper.BaseUriProperty); }
            set { SetValue(BaseUriHelper.BaseUriProperty, value); }
        }
 
        /// <summary>
        /// Returns enumerator to logical children.
        /// </summary>
        protected internal override IEnumerator LogicalChildren
        {
            get
            {
                return this.Children.GetEnumerator();
            }
        }
 
 
        #endregion IUriContext
 
        //--------------------------------------------------------------------
        //
        // Public Properties
        //
        //---------------------------------------------------------------------
 
        #region Public Properties
 
        /// <summary>
        /// Returns a UIElementCollection of children for user to add/remove children manually
        /// Returns null if Panel is data-bound (no manual control of children is possible,
        /// the associated ItemsControl completely overrides children)
        /// Note: the derived Panel classes should never use this collection for any
        /// internal purposes! They should use Children instead, because Children
        /// is always present and either is a mirror of public Children collection (in case of Direct Panel)
        /// or is generated from data binding.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public UIElementCollection Children
        {
            get
            {
                if(_uiElementCollection == null) //nobody used it yet
                {
                    _uiElementCollection = CreateUIElementCollection(this);
                }
 
                return _uiElementCollection;
            }
        }
 
        /// <summary>
        ///
        /// </summary>
        public static readonly DependencyProperty PrintTicketProperty =
                DependencyProperty.RegisterAttached(
                        "PrintTicket",
                        typeof(object),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata((object)null));
 
        /// <summary>
        /// Get/Set PrintTicket Property
        /// </summary>
        public object PrintTicket
        {
            get { return GetValue(PrintTicketProperty); }
            set { SetValue(PrintTicketProperty,value); }
        }
 
        /// <summary>
        /// The Background property defines the brush used to fill the area between borders.
        /// </summary>
        public Brush Background
        {
            get { return (Brush) GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }
 
        /// <summary>
        /// DependencyProperty for <see cref="Background" /> property.
        /// </summary>
        public static readonly DependencyProperty BackgroundProperty =
                Panel.BackgroundProperty.AddOwner(
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata((Brush)Brushes.White, FrameworkPropertyMetadataOptions.AffectsRender));
 
        /// <summary>
        /// This is the dependency property registered for the Canvas' Left attached property.
        ///
        /// The Left property is read by a Canvas on its children to determine where to position them.
        /// The child's offset from this property does not have an effect on the Canvas' own size.
        /// If you want offset to affect size, set the child's Margin property instead.
        /// Conflict between the Left and Right properties is resolved in favor of Left.
        /// Percentages are with respect to the Canvas' size.
        /// </summary>
        /// <seealso cref="FrameworkElement.Margin" />
        public static readonly DependencyProperty LeftProperty =
                DependencyProperty.RegisterAttached(
                        "Left",
                        typeof(double),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsParentArrange));
 
        /// <summary>
        /// This is the dependency property registered for the Canvas' Top attached property.
        ///
        /// The Top property is read by a Canvas on its children to determine where to position them.
        /// The child's offset from this property does not have an effect on the Canvas' own size.
        /// If you want offset to affect size, set the child's Margin property instead.
        /// Conflict between the Top and Bottom properties is resolved in favor of Top.
        /// Percentages are with respect to the Canvas' size.
        /// </summary>
        /// <seealso cref="FrameworkElement.Margin" />
        public static readonly DependencyProperty TopProperty =
                DependencyProperty.RegisterAttached(
                        "Top",
                        typeof(double),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsParentArrange));
 
        /// <summary>
        /// This is the dependency property registered for the Canvas' Right attached property.
        ///
        /// The Right property is read by a Canvas on its children to determine where to position them.
        /// The child's offset from this property does not have an effect on the Canvas' own size.
        /// If you want offset to affect size, set the child's Margin property instead.
        /// Conflict between the Left and Right properties is resolved in favor of Right.
        /// Percentages are with respect to the Canvas' size.
        /// </summary>
        /// <seealso cref="FrameworkElement.Margin" />
        public static readonly DependencyProperty RightProperty =
                DependencyProperty.RegisterAttached(
                        "Right",
                        typeof(double),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsParentArrange));
 
 
        /// <summary>
        /// This is the dependency property registered for the Canvas' Bottom attached property.
        ///
        /// The Bottom property is read by a Canvas on its children to determine where to position them.
        /// The child's offset from this property does not have an effect on the Canvas' own size.
        /// If you want offset to affect size, set the child's Margin property instead.
        /// Conflict between the Top and Bottom properties is resolved in favor of Bottom.
        /// Percentages are with respect to the Canvas' size.
        /// </summary>
        /// <seealso cref="FrameworkElement.Margin" />
        public static readonly DependencyProperty BottomProperty =
                DependencyProperty.RegisterAttached(
                        "Bottom",
                        typeof(double),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata(Double.NaN, FrameworkPropertyMetadataOptions.AffectsParentArrange));
 
        /// <summary>
        ///     The DependencyProperty for the ContentBox property.
        /// </summary>
        public Rect ContentBox
        {
            get { return (Rect) GetValue(ContentBoxProperty); }
            set { SetValue(ContentBoxProperty, value); }
        }
 
        /// <summary>
        ///     The DependencyProperty for the ContentBox property.
        /// </summary>
        public static readonly DependencyProperty ContentBoxProperty =
                DependencyProperty.Register(
                        "ContentBox",
                        typeof(Rect),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata(Rect.Empty));
 
        /// <summary>
        ///     The DependencyProperty for the BleedBox property.
        /// </summary>
        public Rect BleedBox
        {
            get { return (Rect) GetValue(BleedBoxProperty); }
            set { SetValue(BleedBoxProperty, value); }
        }
 
        /// <summary>
        ///     The DependencyProperty for the BleedBox property.
        /// </summary>
        public static readonly DependencyProperty BleedBoxProperty =
                DependencyProperty.Register(
                        "BleedBox",
                        typeof(Rect),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata(Rect.Empty));
 
        /// <summary>
        /// Contains the target URI to navigate when a hyperlink is clicked
        /// </summary>
        public static readonly DependencyProperty NavigateUriProperty =
                        DependencyProperty.RegisterAttached(
                        "NavigateUri",
                        typeof(Uri),
                        typeof(FixedPage),
                        new FrameworkPropertyMetadata(
                                (Uri) null,
                                new PropertyChangedCallback(Hyperlink.OnNavigateUriChanged),
                                new CoerceValueCallback(Hyperlink.CoerceNavigateUri)));
 
        #endregion
 
        protected internal override void OnVisualParentChanged(DependencyObject oldParent)
        {
            base.OnVisualParentChanged(oldParent);
 
            if (oldParent == null)
            {
                HighlightVisual highlightVisual = HighlightVisual.GetHighlightVisual(this);
 
                AdornerLayer al = AdornerLayer.GetAdornerLayer(this);
 
                if (highlightVisual == null && al != null)
                {
                    //Get Page Content
                    PageContent pc = LogicalTreeHelper.GetParent(this) as PageContent;
                    if (pc != null)
                    {
                        //Get FixedDocument
                        FixedDocument doc = LogicalTreeHelper.GetParent(pc) as FixedDocument;
                        if (doc != null)
                        {
                            if (al != null)
                            {
                                //The Text Selection adorner must have predefined ZOrder MaxInt/2,
                                //we assign the ZOrder to annotation adorners respectively
                                int zOrder = System.Int32.MaxValue / 2;
                                al.Add(new HighlightVisual(doc, this),zOrder);
                            }
                        }
                    }
                }
#if DEBUG
                DebugVisualAdorner debugVisualAd = DebugVisualAdorner.GetDebugVisual(this);
                if (debugVisualAd == null && al != null)
                {
                    al.Add(new DebugVisualAdorner(this), System.Int32.MaxValue / 4);
                }
#endif
            }
        }
 
        private static object CoerceFlowDirection(DependencyObject page, Object flowDirection)
        {
            return FlowDirection.LeftToRight;
        }
 
        internal static Uri GetLinkUri(IInputElement element, Uri inputUri)
        {
            DependencyObject dpo = element as DependencyObject;
            Debug.Assert(dpo != null, "GetLinkUri shouldn't be called for non-DependencyObjects.");
 
            if (inputUri != null)
            {
                //
                // First remove the fragment, this is to prevent escape in file URI case, for example,
                // if the inputUri = "..\..\myFile.xaml#fragment", without removing the fragment first,
                // the absoluteUri would be "file:///...../myFile.xaml%23fragment", note # is escaped to
                // %23.
                // If indeed the file contains # such as "This#File.xaml", it should set
                // FixedPage.NavigateUri="This%23File.xaml"
                //
 
                //
                // Copy from BindUriHelper.GetFragment STARTS.
                // It should have a version return #, otherwise, you can
                // not tell betweeen myFile.xaml and myFile.xaml#
                //
                Uri workuri = inputUri;
                if (inputUri.IsAbsoluteUri == false)
                {
                    // this is a relative uri, and Fragement() doesn't work with relative uris.  The base uri is completley irrelevant
                    // here and will never affect the returned fragment, but the method requires something to be there.  Therefore,
                    // we will use "http://microsoft.com" as a convenient substitute.
                    workuri = new Uri(new Uri("http://microsoft.com/"), inputUri);
                }
                // Copy from BindUriHelper.GetFragment ENDS.
 
                // the fragmene will include # sign
                String fragment = workuri.Fragment;
 
                int fragmentLength = (fragment == null) ? 0 : fragment.Length;
                if (fragmentLength != 0)
                {
                    String inputUriString = inputUri.ToString();
                    String inputUriStringWithoutFragment = inputUriString.Substring(0, inputUriString.IndexOf('#'));
                    inputUri = new Uri(inputUriStringWithoutFragment, UriKind.RelativeOrAbsolute);
 
                    //Only Check for the startpart uri if the hyperlink is relative, else it's not part of the package
                    if (inputUri.IsAbsoluteUri == false)
                    {
                        String startPartUriString = GetStartPartUriString(dpo);
                        if (startPartUriString != null)
                        {
                            inputUri = new Uri(startPartUriString, UriKind.RelativeOrAbsolute);
                        }
                    }
                }
                //
                // Resolve to absolute URI
                //
                Uri baseUri = BaseUriHelper.GetBaseUri(dpo);
                Uri absoluteUri = BindUriHelper.GetUriToNavigate(dpo, baseUri, inputUri);
                if (fragmentLength != 0)
                {
                    absoluteUri = new Uri(absoluteUri.ToString() + fragment, UriKind.RelativeOrAbsolute);
                }
 
                return absoluteUri;
            }
            return null;
        }
        //--------------------------------------------------------------------
        //
        // Public Events
        //
        //---------------------------------------------------------------------
 
        //--------------------------------------------------------------------
        //
        // Protected Methods
        //
        //---------------------------------------------------------------------
 
        #region Protected Methods
 
 
        /// <summary>
        /// Gets the Visual children count.
        /// </summary>
        protected override int VisualChildrenCount
        {
            get
            {
                if (_uiElementCollection == null)
                {
                    return 0;
                }
                else
                {
                    return _uiElementCollection.Count;
                }
            }
        }
 
        /// <summary>
        /// Gets the Visual child at the specified index.
        /// </summary>
        protected override Visual GetVisualChild(int index)
        {
            if (_uiElementCollection == null)
            {
                throw new ArgumentOutOfRangeException("index", index, SR.Visual_ArgumentOutOfRange);
            }
            return _uiElementCollection[index];
        }
 
        /// <summary>
        /// Creates a new UIElementCollection. Panel-derived class can create its own version of
        /// UIElementCollection -derived class to add cached information to every child or to
        /// intercept any Add/Remove actions (for example, for incremental layout update)
        /// </summary>
        private UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)
        {
            return new UIElementCollection(this, logicalParent);
        }
 
 
        /// <summary>
        /// Updates DesiredSize of the Canvas.  Called by parent UIElement.  This is the first pass of layout.
        /// </summary>
        /// <remarks>
        /// Canvas measures each of its children accounting for any of their FrameworkElement properties.
        /// Children will be passed either the parent's constraint less any margin on the child or their
        /// explicitly specified (Min/Max)Width/Height properties.
        ///
        /// If it has enough space, Canvas will return a size large enough to accommodate all children's
        /// desired sizes and margins.  Children's Top/Left/Bottom/Right properties are not considered in
        /// this method.  If it does not have enough space to accommodate its children in a dimension, this
        /// will simply return the full constraint in that dimension.
        /// </remarks>
        /// <param name="constraint">Constraint size is an "upper limit" that Canvas should not exceed.</param>
        /// <returns>Canvas' desired size.</returns>
        protected override Size MeasureOverride(Size constraint)
        {
            Size childConstraint = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
 
            foreach (UIElement child in Children)
            {
                child.Measure(childConstraint);
            }
 
            return new Size();
        }
 
        /// <summary>
        /// Canvas computes a position for each of its children taking into account their margin and
        /// attached Canvas properties: Top, Left, Bottom, and Right.  If specified, Top or Left take
        /// priority over Bottom or Right.
        ///
        /// Canvas will also arrange each of its children.
        /// </summary>
        /// <param name="arrangeSize">Size that Canvas will assume to position children.</param>
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            //Canvas arranges children at their DesiredSize.
            //This means that Margin on children is actually respected and added
            //to the size of layout partition for a child.
            //Therefore, is Margin is 10 and Left is 20, the child's ink will start at 30.
 
            foreach (UIElement child in Children)
            {
                double x = 0;
                double y = 0;
 
 
                //Compute offset of the child:
                //If Left is specified, then Right is ignored
                //If Left is not specified, then Right is used
                //If both are not there, then 0
                double left = GetLeft(child);
                if(!double.IsNaN(left))
                {
                    x = left;
                }
                else
                {
                    double right = GetRight(child);
 
                    if(!double.IsNaN(right))
                    {
                        x = arrangeSize.Width - child.DesiredSize.Width - right;
                    }
                }
 
                double top = GetTop(child);
                if(!double.IsNaN(top))
                {
                    y = top;
                }
                else
                {
                    double bottom = GetBottom(child);
 
                    if(!double.IsNaN(bottom))
                    {
                        y = arrangeSize.Height - child.DesiredSize.Height - bottom;
                    }
                }
 
                child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
            }
            return arrangeSize;
        }
 
 
        #endregion
 
        //--------------------------------------------------------------------
        //
        // Internal Methods
        //
        //---------------------------------------------------------------------
 
        #region Internal methods
 
        void IFixedNavigate.NavigateAsync(string elementID)
        {
            FixedHyperLink.NavigateToElement(this, elementID);
        }
 
 
        UIElement IFixedNavigate.FindElementByID(string elementID, out FixedPage rootFixedPage)
        {
            UIElement uiElementRet = null;
            rootFixedPage = this;
 
            // We need iterate through the PageContentCollect first.
            UIElementCollection elementCollection = this.Children;
            UIElement uiElement;
            DependencyObject  node ;
 
            for (int i = 0, n = elementCollection.Count; i < n; i++)
            {
                uiElement = elementCollection[i];
 
                node = LogicalTreeHelper.FindLogicalNode(uiElement, elementID);
                if (node != null)
                {
                    uiElementRet = node as UIElement;
                    break;
                }
            }
 
            return uiElementRet;
        }
 
        // Create a FixedNode representing this glyphs or image
        internal FixedNode CreateFixedNode(int pageIndex, UIElement e)
        {
            // Should we check here to make sure this is a selectable element?
            Debug.Assert(e != null);
            return _CreateFixedNode(pageIndex, e);
        }
 
        internal Glyphs GetGlyphsElement(FixedNode node)
        {
            return GetElement(node) as Glyphs;
        }
 
        // FixedNode represents a leaf node. It contains a path
        // from root to the leaf in the form of child index.
        //  [Level1 ChildIndex] [Level2 ChildIndex] [Level3 ChildIndex]...
        internal DependencyObject GetElement(FixedNode node)
        {
            int currentLevelIndex = node[1];
            if (!(currentLevelIndex >= 0 && currentLevelIndex <= this.Children.Count))
            {
                return null;
            }
 
#if DEBUG
            if (node.ChildLevels > 1)
            {
                DocumentsTrace.FixedFormat.FixedDocument.Trace($"FixedPage.GetUIElement {node} is nested element");
            }
#endif
 
            DependencyObject element = this.Children[currentLevelIndex];
            for (int level = 2; level <= node.ChildLevels; level++)
            {
                // Follow the path if necessary
                currentLevelIndex = node[level];
                if (element is Canvas)
                {
                    // Canvas is a known S0 grouping element.
                    // Boundary Node only would appear in first level!
                    Debug.Assert(currentLevelIndex >= 0 && currentLevelIndex <= ((Canvas)element).Children.Count);
                    element = ((Canvas)element).Children[currentLevelIndex];
                }
                else
                {
                    DocumentsTrace.FixedFormat.FixedDocument.Trace($"FixedPage.GeElement {node} is non S0 grouping element in L[{level}]!");
                    IEnumerable currentChildrens = LogicalTreeHelper.GetChildren((DependencyObject)element);
                    if (currentChildrens == null)
                    {
                        DocumentsTrace.FixedFormat.FixedDocument.Trace($"FixedPage.GetElement {node} is NOT a grouping element in L[{level}]!!!");
                        return null;
                    }
 
                    // We have no uniform way to do random access for this element.
                    // This should never happen for S0 conforming document.
                    int childIndex = -1;
                    IEnumerator itor = currentChildrens.GetEnumerator();
                    while (itor.MoveNext())
                    {
                        childIndex++;
                        if (childIndex == currentLevelIndex)
                        {
                            element = (DependencyObject)itor.Current;
                            break;
                        }
                    }
                }
            }
 
#if DEBUG
            if (!(element is Glyphs))
            {
                DocumentsTrace.FixedFormat.FixedDocument.Trace($"FixedPage.GetElement{node} is non-Glyphs");
            }
#endif
            return element;
        }
 
#endregion Internal Methods
 
        #region Internal Properties
 
        internal String StartPartUriString
        {
            get
            {
                return _startPartUriString;
            }
            set
            {
                _startPartUriString = value;
            }
        }
 
        private String _startPartUriString;
#if DEBUG
        internal FixedPageStructure FixedPageStructure
        {
            get
            {
                return _fixedPageStructure;
            }
            set
            {
                _fixedPageStructure = value;
            }
 
        }
 
        internal int DrawDebugVisualSelection
        {
            get
            {
                return _drawDebugVisual;
            }
        }
 
#endif
 
#endregion Internal Properties
 
        //--------------------------------------------------------------------
        //
        // private Properties
        //
        //---------------------------------------------------------------------
        private UIElementCollection _uiElementCollection;
 
        //--------------------------------------------------------------------
        //
        // Private Methods
        //
        //---------------------------------------------------------------------
 
        #region Private Methods
 
        private void Init()
        {
            if (XpsValidatingLoader.DocumentMode)
            {
                this.InheritanceBehavior = InheritanceBehavior.SkipAllNext;
            }
        }
 
        internal StoryFragments GetPageStructure()
        {
            StoryFragments sf;
 
            sf = FixedDocument.GetStoryFragments(this);
 
            return sf;
        }
 
        internal int[] _CreateChildIndex(DependencyObject e)
        {
            List<int> childPath = new List<int>();
            while (e != this)
            {
                DependencyObject parent = LogicalTreeHelper.GetParent(e);
                int childIndex = -1;
                if (parent is FixedPage parentFP)
                {
                    childIndex = parentFP.Children.IndexOf((UIElement)e);
                }
                else if (parent is Canvas parentC)
                {
                    childIndex = parentC.Children.IndexOf((UIElement)e);
                }
                else
                {
                    IEnumerable currentChildrens = LogicalTreeHelper.GetChildren(parent);
                    Debug.Assert(currentChildrens != null);
 
                    // We have no uniform way to do random access for this element.
                    // This should never happen for S0 conforming document.
                    IEnumerator itor = currentChildrens.GetEnumerator();
                    while (itor.MoveNext())
                    {
                        childIndex++;
                        if (itor.Current == e)
                        {
                            break;
                        }
                    }
                }
                childPath.Insert(0, childIndex);
                e = parent;
            }
            while (e != this) ;
 
            return childPath.ToArray();
        }
 
        // Making this function private and only expose the versions
        // that take S0 elements as parameter.
        private FixedNode _CreateFixedNode(int pageIndex, UIElement e)
        {
            return FixedNode.Create(pageIndex, _CreateChildIndex(e));
        }
 
        /// <summary>
        /// This function walks the logical tree for the FixedDocumentSequence and then
        /// retrieves its URI.  We use this Uri for fragment navigation during a FixedPage.OnClick
        /// event.
        /// </summary>
        /// <param name="current"></param>
        /// <returns></returns>
        private static String GetStartPartUriString(DependencyObject current)
        {
            //1) Get FixedPage from DependcyObject's InheritanceObject property
 
            DependencyObject obj = current;
            FixedPage fixedPage = current as FixedPage;
            while (fixedPage == null && obj != null)
            {
                obj = obj.InheritanceParent;
                fixedPage = obj as FixedPage;
            }
 
            if (fixedPage == null)
            {
                return null;
            }
 
            //2) Check if fixedPage StartPartUri is null
            if (fixedPage.StartPartUriString == null)
            {
                //3) Walk Logical Tree to the FixedDocumentSequence
                DependencyObject parent = LogicalTreeHelper.GetParent(current);
                while (parent != null)
                {
                    FixedDocumentSequence docSequence = parent as FixedDocumentSequence;
                    if (docSequence != null)
                    {
                        //4) Retrieve DocumentSequence Uri
                        Uri startPartUri = ((IUriContext)docSequence).BaseUri;
                        if (startPartUri != null)
                        {
                            String startPartUriString = startPartUri.ToString();
 
                            // If there is a fragment we need to strip it off
                            String fragment = startPartUri.Fragment;
 
                            int fragmentLength = (fragment == null) ? 0 : fragment.Length;
                            if (fragmentLength != 0)
                            {
                                fixedPage.StartPartUriString = startPartUriString.Substring(0, startPartUriString.IndexOf('#'));
                            }
                            else
                            {
                                fixedPage.StartPartUriString = startPartUri.ToString();
                            }
                        }
                        break;
                    }
                    parent = LogicalTreeHelper.GetParent(parent);
                }
 
                //If we don't have a starting part Uri, assign fixedPage.StartPartUriString to Empty so
                //we don't try to look it up again.
                if (fixedPage.StartPartUriString == null)
                {
                    fixedPage.StartPartUriString = String.Empty;
                }
            }
 
            if (fixedPage.StartPartUriString == String.Empty)
            {
                return null;
            }
            else
            {
                return fixedPage.StartPartUriString;
            }
        }
 
        #endregion
 
        //--------------------------------------------------------------------
        //
        // Private Fields
        //
        //---------------------------------------------------------------------
 
 
#if DEBUG
        private FixedPageStructure _fixedPageStructure;
        //
        // The debugging features: set to true will draw the bounding box for each
        // line from the analyzed layout results.
        //
        private int _drawDebugVisual = 0;
 
#endif
    }
 
#if DEBUG
 
    internal sealed class DebugVisualAdorner: Adorner
    {
        internal DebugVisualAdorner(FixedPage page) : base(page)
        {
            _fixedPage = page;
        }
 
        override protected void OnRender(DrawingContext dc)
        {
            if (_fixedPage.DrawDebugVisualSelection == (int) DrawDebugVisual.None)
            {
                return;
            }
 
            FixedPageStructure pageStructure = _fixedPage.FixedPageStructure;
            Debug.Assert(pageStructure != null);
 
            if (_fixedPage.DrawDebugVisualSelection == (int) DrawDebugVisual.Glyphs)
            {
                if (pageStructure.FixedNodes != null)
                {
                    _RenderMarkupOrder(dc, pageStructure.FixedNodes);
                }
            }
            else if (_fixedPage.DrawDebugVisualSelection == (int) DrawDebugVisual.Lines)
            {
                pageStructure.RenderLines(dc);
            }
            else
            {
                if (pageStructure.FixedSOMPage != null)
                {
                    pageStructure.FixedSOMPage.Render(dc, null, (DrawDebugVisual) _fixedPage.DrawDebugVisualSelection);
                }
                else
                {
                    //Explicit document structure
                    FlowNode[] pageNodes = _fixedPage.FixedPageStructure.FlowNodes;
                    int flowOrder = 0;
 
                    foreach (FlowNode node in pageNodes)
                    {
                        if (node != null && node.FixedSOMElements != null)
                        {
                            foreach (FixedSOMElement somElement in node.FixedSOMElements)
                            {
                                somElement.Render(dc, flowOrder.ToString(), (DrawDebugVisual) _fixedPage.DrawDebugVisualSelection);
                                flowOrder++;
                            }
                        }
                    }
                }
            }
        }
 
        internal static DebugVisualAdorner GetDebugVisual(FixedPage page)
        {
            AdornerLayer al = AdornerLayer.GetAdornerLayer(page);
            DebugVisualAdorner debugVisualAd;
 
            if (al == null)
            {
                return null;
            }
 
            Adorner[] adorners = al.GetAdorners(page);
 
            if (adorners != null)
            {
                foreach (Adorner ad in adorners)
                {
                    debugVisualAd = ad as DebugVisualAdorner;
                    if (debugVisualAd != null)
                    {
                        return debugVisualAd;
                    }
                }
            }
 
            return null;
        }
 
        private void _RenderMarkupOrder(DrawingContext dc, List<FixedNode> markupOrder)
        {
            int order = 0;
            foreach (FixedNode node in markupOrder)
            {
                DependencyObject ob = _fixedPage.GetElement(node);
                Glyphs glyphs = ob as Glyphs;
                Path path = ob as Path;
                if (glyphs != null)
                {
                    GlyphRun glyphRun = glyphs.ToGlyphRun();
                    Rect alignmentBox = glyphRun.ComputeAlignmentBox();
                    alignmentBox.Offset(glyphs.OriginX, glyphs.OriginY);
                    GeneralTransform transform = glyphs.TransformToAncestor(_fixedPage);
                    alignmentBox = transform.TransformBounds(alignmentBox);
 
                    Pen pen = new Pen(Brushes.Green, 1);
                    dc.DrawRectangle(null, pen , alignmentBox);
                    _RenderLabel(dc, order.ToString(), alignmentBox);
 
                    ++order;
                }
                else if (path != null)
                {
                    Geometry renderGeom = path.RenderedGeometry;
                    Pen backgroundPen = new Pen(Brushes.Black,1);
                    dc.DrawGeometry(null, backgroundPen, renderGeom);
                    _RenderLabel(dc, order.ToString(), renderGeom.Bounds);
                    ++order;
                }
            }
        }
 
        private void _RenderLabel(DrawingContext dc, string label, Rect boundingRect)
        {
            FormattedText ft = new FormattedText(label,
                                        System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS,
                                        FlowDirection.LeftToRight,
                                        new Typeface("Arial"),
                                        10,
                                        Brushes.White,
                                        GetDpi().PixelsPerDip);
            Point labelLocation = new Point(boundingRect.Left-25, (boundingRect.Bottom + boundingRect.Top)/2 - 10);
            Geometry geom = ft.BuildHighlightGeometry(labelLocation);
            Pen backgroundPen = new Pen(Brushes.Black,1);
            dc.DrawGeometry(Brushes.Black, backgroundPen, geom);
            dc.DrawText(ft, labelLocation);
 
        }
 
        private FixedPage _fixedPage;
 
    }
 
 
 
    internal enum DrawDebugVisual
    {
        None = 0,
        Glyphs = 1,
        Lines = 2,
        TextRuns = 3,
        Paragraphs = 4,
        Groups = 5,
        LastOne = 6
    }
#endif
}