FlowDocumentPage.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / PtsHost / FlowDocumentPage.cs / 1 / FlowDocumentPage.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// Description: DocumentPage representing bottomless of finite page of 
//              a PTS host (FlowDocument).
// 
// History: 
//  09/27/2004 : [....] - created.
// 
//---------------------------------------------------------------------------
using System;
using System.IO;
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Collections; 
using System.Diagnostics; 
using System.Threading;
using System.Security; 
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Documents; 
using System.Windows.Threading;         // Dispatcher
using MS.Internal.Documents; 
using MS.Internal.Text; 

using MS.Internal.PtsHost.UnsafeNativeMethods; 

namespace MS.Internal.PtsHost
{
    //----------------------------------------------------------------------- 
    // DocumentPage representing bottomless or finite page of a PTS host.
    //----------------------------------------------------------------------- 
    internal sealed class FlowDocumentPage : DocumentPage, IServiceProvider, IDisposable, IContentHost 
    {
        //-------------------------------------------------------------------- 
        //
        //  Constructors
        //
        //------------------------------------------------------------------- 

        #region Constructors 
 
        //--------------------------------------------------------------------
        // Constructor. 
        //
        //      structuralCache - context representing data
        //--------------------------------------------------------------------
        internal FlowDocumentPage(StructuralCache structuralCache) : base(null) 
        {
            _structuralCache = structuralCache; 
            _ptsPage = new PtsPage(structuralCache.Section); 
        }
 
        // -----------------------------------------------------------------
        // Finalizer
        // ------------------------------------------------------------------
        ~FlowDocumentPage() 
        {
            Dispose(false); 
        } 

        #endregion Constructors 

        //-------------------------------------------------------------------
        //
        //  Public Methods 
        //
        //------------------------------------------------------------------- 
 
        #region Public Methods
 
        //-------------------------------------------------------------------
        // Dispose the page.
        //--------------------------------------------------------------------
        public override void Dispose() 
        {
            Dispose(true); 
            GC.SuppressFinalize(this); 

            base.Dispose(); 
        }

        #endregion Public Methods
 
        //-------------------------------------------------------------------
        // 
        //  Public Properties 
        //
        //-------------------------------------------------------------------- 

        #region Public Properties

        //-------------------------------------------------------------------- 
        // Visual node representing content of the page.
        //------------------------------------------------------------------- 
        public override Visual Visual 
        {
            get 
            {
                if (IsDisposed)
                {
                    return null; 
                }
                UpdateVisual(); 
                return base.Visual; 
            }
        } 

        #endregion Public Properties

        //-------------------------------------------------------------------- 
        //
        //  Internal Methods 
        // 
        //-------------------------------------------------------------------
 
        #region Internal Methods

        //-------------------------------------------------------------------
        // Format content into a single bottomless page. 
        //
        //      pageSize - size of the page 
        //------------------------------------------------------------------- 
        internal void FormatBottomless(Size pageSize, Thickness pageMargin)
        { 
            Invariant.Assert(!IsDisposed);

            // Every time full format is done reset formatted lines count to 0.
            _formattedLinesCount = 0; 

            // Make sure that PTS limitations are not exceeded. 
            TextDpi.EnsureValidPageSize(ref pageSize); 
            _pageMargin = pageMargin;
            SetSize(pageSize); 

            if(!DoubleUtil.AreClose(_lastFormatWidth, pageSize.Width) || !DoubleUtil.AreClose(_pageMargin.Left, pageMargin.Left) ||
               !DoubleUtil.AreClose(_pageMargin.Right, pageMargin.Right))
            { 
                // No incremental update if width changes.
                _structuralCache.InvalidateFormatCache(false); 
            } 

            _lastFormatWidth = pageSize.Width; 

            using(_structuralCache.SetDocumentFormatContext(this))
            {
                OnBeforeFormatPage(); 

                if (_ptsPage.PrepareForBottomlessUpdate()) 
                { 
                    _structuralCache.CurrentFormatContext.PushNewPageData(pageSize, _pageMargin, true, false);
                    _ptsPage.UpdateBottomlessPage(); 
                }
                else
                {
                    _structuralCache.CurrentFormatContext.PushNewPageData(pageSize, _pageMargin, false, false); 
                    _ptsPage.CreateBottomlessPage();
                } 
 
                // In bottomless page scenario, need to update PageSize to reflect
                // calculated size of the page. 
                pageSize = _ptsPage.CalculatedSize;
                pageSize.Width += pageMargin.Left + pageMargin.Right;
                pageSize.Height += pageMargin.Top + pageMargin.Bottom;
                SetSize(pageSize); 
                SetContentBox(new Rect(pageMargin.Left, pageMargin.Top, _ptsPage.CalculatedSize.Width, _ptsPage.CalculatedSize.Height));
                _structuralCache.CurrentFormatContext.PopPageData(); 
 
                OnAfterFormatPage();
 
                _structuralCache.DetectInvalidOperation();
            }
        }
 
        //--------------------------------------------------------------------
        // Format content into a single finite page. 
        // 
        //       pageSize - size of the page
        //       pageMargin - margin of the page 
        //       breakRecord - input BreakRecor for the page
        //
        // Returns: Returns output break record.
        //------------------------------------------------------------------- 
        internal PageBreakRecord FormatFinite(Size pageSize, Thickness pageMargin, PageBreakRecord breakRecord)
        { 
            Invariant.Assert(!IsDisposed); 

            // Every time full format is done reset formatted lines count to 0. 
            _formattedLinesCount = 0;

            // Make sure that PTS limitations are not exceeded.
            TextDpi.EnsureValidPageSize(ref pageSize); 
            TextDpi.EnsureValidPageMargin(ref pageMargin, pageSize);
 
            double pageMarginAdjustment = PtsHelper.CalculatePageMarginAdjustment(_structuralCache, pageSize.Width - (pageMargin.Left + pageMargin.Right)); 
            if (!DoubleUtil.IsZero(pageMarginAdjustment))
            { 
                // Potentially some FP drift here, as we're anticipating that our column count will now work out exactly. Add a small fraction back to prevent this
                pageMargin.Right += pageMarginAdjustment - (pageMarginAdjustment / 100.0);
            }
            _pageMargin = pageMargin; 

            SetSize(pageSize); 
            SetContentBox(new Rect(pageMargin.Left, pageMargin.Top, 
                pageSize.Width - (pageMargin.Left + pageMargin.Right),
                pageSize.Height - (pageMargin.Top + pageMargin.Bottom))); 

            using(_structuralCache.SetDocumentFormatContext(this))
            {
                OnBeforeFormatPage(); 

                if (_ptsPage.PrepareForFiniteUpdate(breakRecord)) 
                { 
                    _structuralCache.CurrentFormatContext.PushNewPageData(pageSize, _pageMargin, true, true);
                    _ptsPage.UpdateFinitePage(breakRecord); 
                }
                else
                {
                    _structuralCache.CurrentFormatContext.PushNewPageData(pageSize, _pageMargin, false, true); 
                    _ptsPage.CreateFinitePage(breakRecord);
                } 
                _structuralCache.CurrentFormatContext.PopPageData(); 

                OnAfterFormatPage(); 
                _structuralCache.DetectInvalidOperation();
            }

            return _ptsPage.BreakRecord; 
        }
 
        //-------------------------------------------------------------------- 
        // Arrange the page contents.
        //-------------------------------------------------------------------- 
        internal void Arrange(Size partitionSize)
        {
            Invariant.Assert(!IsDisposed);
 
            _partitionSize = partitionSize;
 
            using(_structuralCache.SetDocumentArrangeContext(this)) 
            {
                _ptsPage.ArrangePage(); 
                _structuralCache.DetectInvalidOperation();
            }

            ValidateTextView(); 
        }
 
        //------------------------------------------------------------------- 
        // Page update may be requested more than once before rendering is
        // done. But PTS is not able to merge update info. 
        // To protect against loosing incremental changes delta, need
        // to force full formatting for the conent.
        //--------------------------------------------------------------------
        internal void ForceReformat() 
        {
            Invariant.Assert(!IsDisposed); 
            // Clear update info for PTS page. 
            _ptsPage.ClearUpdateInfo();
            // Force reformat 
            _structuralCache.ForceReformat = true;
        }

        //------------------------------------------------------------------- 
        // Hit tests to the correct ContentElement within the ContentHost
        // that the mouse is over. 
        // 
        //       point - mouse coordinates relative to the ContentHost
        // 
        // Returns: IInputElement from specified position.
        //-------------------------------------------------------------------
        internal IInputElement InputHitTestCore(Point point)
        { 
            Invariant.Assert(!IsDisposed);
 
            // Core services require that the IInputElement returned from hittesting 
            // is a UIElement or it has a parent that is a UIElement.
            // When using DocumentPageView.DocumentPaginator directly, we may run 
            // into case when FlowDocument does not have a logical parent. In
            // such case it is better to disable all core services.
            DependencyObject frameworkParent = FrameworkElement.GetFrameworkParent(_structuralCache.FormattingOwner);
            if (frameworkParent == null) 
            {
                return null; 
            } 

            IInputElement ie = null; 
            if (this.IsLayoutDataValid)
            {
                // Transform point to PtsPage coordinate system.
                // NOTE: TransformToAncestor is safe (will never throw an exception). 
                GeneralTransform transform = this.PageVisual.Child.TransformToAncestor(this.PageVisual);
                transform = transform.Inverse; 
 
                // Hittest PtsPage only when transform can be inverted in order to calculate
                // point within PtsPage. If transform cannot be inverted, return the owner of this page. 
                if (transform != null)
                {
                    point = transform.Transform(point);
                    ie = _ptsPage.InputHitTest(point); 
                }
            } 
            return (ie != null) ? ie : _structuralCache.FormattingOwner as IInputElement; 
        }
 
        /// 
        /// Returns rectangles for element. First finds element by navigating in FlowDocumentPage.
        /// If element is not found or if call to get rectangles from FlowDocumentPage returns null
        /// we return an empty collection. If the layout is not valid we return null. 
        /// 
        ///  
        /// Content element for which rectangles are required 
        /// 
        ///  
        /// Indicates whether search should be restricted only to those text segments within the page's text view
        /// 
        internal ReadOnlyCollection GetRectanglesCore(ContentElement child, bool isLimitedToTextView)
        { 
            Invariant.Assert(!IsDisposed);
 
            List rectangles = new List(); 
            Debug.Assert(child != null);
            if (IsLayoutDataValid) 
            {
                TextPointer elementStart = FindElementPosition(child, isLimitedToTextView);
                if (elementStart != null)
                { 
                    // Element exists within this Page, calculate its length
                    int elementStartOffset = _structuralCache.TextContainer.Start.GetOffsetToPosition(elementStart); 
                    int elementLength = 1; 
                    if (child is TextElement)
                    { 
                        TextPointer elementEnd = new TextPointer(((TextElement)child).ElementEnd);
                        elementLength = elementStart.GetOffsetToPosition(elementEnd);
                    }
 
                    rectangles = _ptsPage.GetRectangles(child, elementStartOffset, elementLength);
                } 
            } 

            if(this.PageVisual != null && rectangles.Count > 0) 
            {
                List transformedRectangles = new List(rectangles.Count);
                // NOTE: TransformToAncestor is safe (will never throw an exception).
                GeneralTransform transform = this.PageVisual.Child.TransformToAncestor(this.PageVisual); 

                for(int index = 0; index < rectangles.Count; index++) 
                { 
                    transformedRectangles.Add(transform.TransformBounds(rectangles[index]));
                } 

                rectangles = transformedRectangles;
            }
 
            // We should never return null for rectangles from public API, only empty ArrayList
            Invariant.Assert(rectangles != null); 
 
            return new ReadOnlyCollection(rectangles);
        } 

        /// 
        /// Returns elements hosted by the content host as an enumerator class
        ///  
        internal IEnumerator HostedElementsCore
        { 
            get 
            {
                if (IsLayoutDataValid) 
                {
                    // At this point, we should create TextView if it doesn't exist
                    _textView = GetTextView();
                    Invariant.Assert(_textView != null && ((ITextView)_textView).TextSegments.Count > 0); 
                    return new HostedElements(((ITextView)_textView).TextSegments);
                } 
                else 
                {
                    // Return empty collection 
                    return new HostedElements(new ReadOnlyCollection(new List(0)));
                }
            }
        } 

        // Floating element list 
        internal ReadOnlyCollection FloatingElementResults 
        {
            get 
            {
                List floatingElements = new List(0);
                List floatingElementList = _ptsPage.PageContext.FloatingElementList;
                if (floatingElementList != null) 
                {
                    for (int i = 0; i < floatingElementList.Count; i++) 
                    { 
                        ParagraphResult paragraphResult = floatingElementList[i].CreateParagraphResult();
                        floatingElements.Add(paragraphResult); 
                    }
                }
                return new ReadOnlyCollection(floatingElements);
            } 
        }
 
        ///  
        /// Called when a UIElement-derived class which is hosted by a IContentHost changes it�s DesiredSize
        ///  
        /// 
        /// Child element whose DesiredSize has changed
        /// 
        internal void OnChildDesiredSizeChangedCore(UIElement child) 
        {
            _structuralCache.FormattingOwner.OnChildDesiredSizeChanged(child); 
        } 

        //------------------------------------------------------------------- 
        // Returns a new collection of ColumnResults for the page. Will always
        // have at least one column.
        //  hasTextContent -  True if any column in the page has text
        //                    content, i.e. does not contain only figures/floaters 
        //--------------------------------------------------------------------
        ///  
        /// Critical - as this call Critical functions FsQueryPageDetails, FsQueryTrackDetails, 
        ///            FsQuerySectionDetails and some PtsHelper functions.
        /// Safe - as this can't be be used to pass arbitrary parameters.  All parameters passed 
        ///        in are either Critical for set or are generated within the function.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal ReadOnlyCollection GetColumnResults(out bool hasTextContent) 
        {
            Invariant.Assert(!IsDisposed); 
            List columnResults = new List(0); 

            // hasTextContent is set to true if any of the columns in the page has text content. This is determined by checking the columns' 
            // paragraph collections
            hasTextContent = false;

            // There are 3 cases: 
            // (1) PTS page is not created - no columns are available.
            // (2) PTS page - use page PTS APIs to get columns. 
            if (_ptsPage.PageHandle == IntPtr.Zero) 
            {
                // (1) PTS page is not created 
            }
            else
            {
               // (2) PTS page - use page PTS APIs to get columns. 
                PTS.FSPAGEDETAILS pageDetails;
                PTS.Validate(PTS.FsQueryPageDetails(StructuralCache.PtsContext.Context, _ptsPage.PageHandle, out pageDetails)); 
 
                // There are 2 different types of PTS page:
                // (a) simple page (contains only one track) - 1 column. 
                // (b) complex page (contains header, page body, footnotes and footer) - get columns
                //     from the page body.
                if (PTS.ToBoolean(pageDetails.fSimple))
                { 
                    // (a) simple page (contains only one track) - 1 column.
                    PTS.FSTRACKDETAILS trackDetails; 
                    PTS.Validate(PTS.FsQueryTrackDetails(StructuralCache.PtsContext.Context, pageDetails.u.simple.trackdescr.pfstrack, out trackDetails)); 
                    if (trackDetails.cParas > 0)
                    { 
                        columnResults = new List(1);
                        ColumnResult columnResult = new ColumnResult(this, ref pageDetails.u.simple.trackdescr, new Vector());
                        columnResults.Add(columnResult);
                        if (columnResult.HasTextContent) 
                        {
                            hasTextContent = true; 
                        } 
                    }
                } 
                else if (pageDetails.u.complex.cSections > 0)
                {
                    // (b) complex page (contains header, page body, footnotes and footer) - get columns
                    //     from the page body. 
                    Debug.Assert(pageDetails.u.complex.cSections == 1); // Only one section is supported right now.
 
                    // Retrieve description for each section. 
                    PTS.FSSECTIONDESCRIPTION[] arraySectionDesc;
                    PtsHelper.SectionListFromPage(StructuralCache.PtsContext, _ptsPage.PageHandle, ref pageDetails, out arraySectionDesc); 

                    // Get section details
                    PTS.FSSECTIONDETAILS sectionDetails;
                    PTS.Validate(PTS.FsQuerySectionDetails(StructuralCache.PtsContext.Context, arraySectionDesc[0].pfssection, out sectionDetails)); 

                    // There are 2 types of sections: 
                    // (1) with page notes - footnotes in section treated as endnotes 
                    // (2) with column notes - footnotes in section treated as column notes
                    if (PTS.ToBoolean(sectionDetails.fFootnotesAsPagenotes)) 
                    {
                        // (1) with page notes - footnotes in section treated as endnotes
                        Debug.Assert(sectionDetails.u.withpagenotes.cEndnoteColumns == 0); // Footnotes are not supported yet.
 
                        //
 
                        Debug.Assert(sectionDetails.u.withpagenotes.cSegmentDefinedColumnSpanAreas == 0); 
                        Debug.Assert(sectionDetails.u.withpagenotes.cHeightDefinedColumnSpanAreas == 0);
 
                        // cBasicColumns == 0, means that section content is empty.
                        // In such case there is nothing to render.
                        if (sectionDetails.u.withpagenotes.cBasicColumns > 0)
                        { 
                            // Retrieve description for each column.
                            PTS.FSTRACKDESCRIPTION[] arrayColumnDesc; 
                            PtsHelper.TrackListFromSection(StructuralCache.PtsContext, arraySectionDesc[0].pfssection, ref sectionDetails, out arrayColumnDesc); 

                            columnResults = new List(sectionDetails.u.withpagenotes.cBasicColumns); 
                            for (int i = 0; i < arrayColumnDesc.Length; i++)
                            {
                                PTS.FSTRACKDESCRIPTION columnDesc = arrayColumnDesc[i];
 
                                // Column may have null track, in which case we should not add it
                                if (columnDesc.pfstrack != IntPtr.Zero) 
                                { 
                                    PTS.FSTRACKDETAILS trackDetails;
                                    PTS.Validate(PTS.FsQueryTrackDetails(StructuralCache.PtsContext.Context, columnDesc.pfstrack, out trackDetails)); 
                                    if (trackDetails.cParas > 0)
                                    {
                                        ColumnResult columnResult = new ColumnResult(this, ref columnDesc, new Vector());
                                        columnResults.Add(columnResult); 
                                        if (columnResult.HasTextContent)
                                        { 
                                            hasTextContent = true; 
                                        }
                                    } 
                                }
                            }
                        }
                        // else; section empty => no columns 
                    }
                    else 
                    { 
                        // (2) with column notes - footnotes in section treated as column notes
                        Debug.Assert(false); // Complex columns are not supported yet. 
                    }
                }
            }
 
            Invariant.Assert(columnResults != null);
            return new ReadOnlyCollection(columnResults); 
        } 

        //------------------------------------------------------------------- 
        // Retrieves text range for contents of the column represented
        // by 'pfstrack'.
        //
        //      pfstrack - pointer to PTS track representing a column 
        //
        // Returns: text range for contents of the column represented by 'pfstrack' 
        //-------------------------------------------------------------------- 
        /// 
        /// Critical - as this calls Critical function PTS.FsQueryTrackDetails and 
        ///            passes an IntPtr directly.
        /// 
        [SecurityCritical]
        internal TextContentRange GetTextContentRangeFromColumn(IntPtr pfstrack) 
        {
            Invariant.Assert(!IsDisposed); 
            // Get track details 
            PTS.FSTRACKDETAILS trackDetails;
            PTS.Validate(PTS.FsQueryTrackDetails(StructuralCache.PtsContext.Context, pfstrack, out trackDetails)); 

            // Combine ranges from all nested paragraphs.
            TextContentRange textContentRange = new TextContentRange();
            if (trackDetails.cParas != 0) 
            {
                PTS.FSPARADESCRIPTION[] arrayParaDesc; 
                PtsHelper.ParaListFromTrack(StructuralCache.PtsContext, pfstrack, ref trackDetails, out arrayParaDesc); 

                // Merge TextContentRanges for all paragraphs 
                BaseParaClient paraClient;
                for (int i = 0; i < arrayParaDesc.Length; i++)
                {
                    paraClient = this.StructuralCache.PtsContext.HandleToObject(arrayParaDesc[i].pfsparaclient) as BaseParaClient; 
                    PTS.ValidateHandle(paraClient);
                    textContentRange.Merge(paraClient.GetTextContentRange()); 
                } 
            }
            return textContentRange; 
        }

        //--------------------------------------------------------------------
        // Returns a collection of ParagraphResults for the column's paragraphs. 
        //
        //      pfstrack - pointer to PTS track representing a column 
        //      parentOffset - parent offset from the top of the page 
        //      hasTextContent - true if any paragraph in the column has some text content
        // 
        // Returns: collection of ParagraphResults for the column's paragraphs
        //-------------------------------------------------------------------
        /// 
        /// Critical - as this calls Critical function PTS.FsQueryTrackDetails and 
        ///            passes an IntPtr directly.
        ///  
        [SecurityCritical] 
        internal ReadOnlyCollection GetParagraphResultsFromColumn(IntPtr pfstrack, Vector parentOffset, out bool hasTextContent)
        { 
            Invariant.Assert(!IsDisposed);
            // Get track details
            PTS.FSTRACKDETAILS trackDetails;
            PTS.Validate(PTS.FsQueryTrackDetails(StructuralCache.PtsContext.Context, pfstrack, out trackDetails)); 
            hasTextContent = false;
 
            if (trackDetails.cParas == 0) 
            {
                return new ReadOnlyCollection(new List(0)); 
            }

            PTS.FSPARADESCRIPTION[] arrayParaDesc;
            PtsHelper.ParaListFromTrack(StructuralCache.PtsContext, pfstrack, ref trackDetails, out arrayParaDesc); 

            List paragraphResults = new List(arrayParaDesc.Length); 
            for (int i = 0; i < arrayParaDesc.Length; i++) 
            {
                BaseParaClient paraClient = StructuralCache.PtsContext.HandleToObject(arrayParaDesc[i].pfsparaclient) as BaseParaClient; 
                PTS.ValidateHandle(paraClient);
                ParagraphResult paragraphResult = paraClient.CreateParagraphResult();
                if (paragraphResult.HasTextContent)
                { 
                    hasTextContent = true;
                } 
                paragraphResults.Add(paragraphResult); 
            }
            return new ReadOnlyCollection(paragraphResults); 
        }

        //--------------------------------------------------------------------
        // Notification about new line being formatted. 
        //-------------------------------------------------------------------
        internal void OnFormatLine() 
        { 
            Invariant.Assert(!IsDisposed);
            ++_formattedLinesCount; 
        }

        //-------------------------------------------------------------------
        // Ensures visual structure for this document page is clean 
        //-------------------------------------------------------------------
        internal void EnsureValidVisuals() 
        { 
            Invariant.Assert(!IsDisposed);
            UpdateVisual(); 
        }

        //--------------------------------------------------------------------
        // Update the viewport 
        //-------------------------------------------------------------------
        internal void UpdateViewport(ref PTS.FSRECT viewport, bool drawBackground) 
        { 
            Rect contentViewport;
 
            // Transform point to PtsPage coordinate system.
            // NOTE: TransformToAncestor is safe (will never throw an exception).
            GeneralTransform transform = this.PageVisual.Child.TransformToAncestor(this.PageVisual);
            transform = transform.Inverse; 

            contentViewport = viewport.FromTextDpi(); 
            if (transform != null) 
            {
                contentViewport = transform.TransformBounds(contentViewport); 
            }

            if(!IsDisposed)
            { 
                // Draw background
                if (drawBackground) 
                { 
                    this.PageVisual.DrawBackground((Brush)_structuralCache.PropertyOwner.GetValue(FlowDocument.BackgroundProperty), contentViewport);
                } 

                using (_structuralCache.SetDocumentVisualValidationContext(this))
                {
                    PTS.FSRECT contentViewportTextDpi = new PTS.FSRECT(contentViewport); 
                    _ptsPage.UpdateViewport(ref contentViewportTextDpi);
 
                    _structuralCache.DetectInvalidOperation(); 
                }
 
                ValidateTextView();
            }
        }
 
        #endregion Internal methods
 
        //-------------------------------------------------------------------- 
        //
        //  Internal Properties 
        //
        //--------------------------------------------------------------------

        #region Internal Properties 

        //------------------------------------------------------------------- 
        // Is being used in a plain text box? 
        //--------------------------------------------------------------------
        internal bool UseSizingWorkaroundForTextBox 
        {
            get { return _ptsPage.UseSizingWorkaroundForTextBox; }
            set { _ptsPage.UseSizingWorkaroundForTextBox = value; }
        } 

 
        //------------------------------------------------------------------- 
        // Margin of the page.
        //------------------------------------------------------------------- 
        internal Thickness Margin { get { return _pageMargin; } }

        //-------------------------------------------------------------------
        // Is this page already disposed? 
        //--------------------------------------------------------------------
        internal bool IsDisposed { get { return (_disposed != 0) || _structuralCache.PtsContext.Disposed; } } 
 
        //-------------------------------------------------------------------
        // Size of content on page. 
        //--------------------------------------------------------------------
        internal Size ContentSize
        {
            get 
            {
                Size size = _ptsPage.ContentSize; 
                size.Width += _pageMargin.Left + _pageMargin.Right; 
                size.Height += _pageMargin.Top + _pageMargin.Bottom;
                return size; 
            }
        }

        //-------------------------------------------------------------------- 
        // Is it finite page or bottomless?
        //------------------------------------------------------------------- 
        internal bool FinitePage { get { return _ptsPage.FinitePage; } } 

        //-------------------------------------------------------------------- 
        // Page context
        //-------------------------------------------------------------------
        internal PageContext PageContext { get { return _ptsPage.PageContext; } }
 
        //-------------------------------------------------------------------
        // Is during incremental update mode? 
        //------------------------------------------------------------------- 
        internal bool IncrementalUpdate { get { return _ptsPage.IncrementalUpdate; } }
 
        //--------------------------------------------------------------------
        // StructuralCache associated with this page.
        //-------------------------------------------------------------------
        internal StructuralCache StructuralCache { get { return _structuralCache; } } 

        //-------------------------------------------------------------------- 
        // Number of lines formatted during page formatting. 
        //--------------------------------------------------------------------
        internal int FormattedLinesCount { get { return _formattedLinesCount; } } 

        //-------------------------------------------------------------------
        // Is layout data is in a valid state.
        //-------------------------------------------------------------------- 
        internal bool IsLayoutDataValid
        { 
            get 
            {
                bool layoutDataValid = false; 
                if (!IsDisposed)
                {
                    // In case of any content/properties changes FlowDocument does BreakRecordTable
                    // management and disposes any affected pages. So it is unnecessary to check 
                    // for DtrList of ForceReformat here, because _disposed flag reflects this fact
                    // in more granular way. 
                    layoutDataValid = _structuralCache.FormattingOwner.IsLayoutDataValid; 
                }
                return layoutDataValid; 
            }
        }

        //------------------------------------------------------------------- 
        // Save the maximum dcpDepend of the page, for invalidations
        // of later pages. 
        // 
        // DCPDepend - number of characters past end of page that were
        // considered for formatting of this page 
        //-------------------------------------------------------------------
        internal TextPointer DependentMax
        {
            get 
            {
                return _DependentMax; 
            } 
            set
            { 
                if ((_DependentMax == null) || ((value != null) && (value.CompareTo(_DependentMax) > 0)))
                {
                    _DependentMax = value;
                } 
            }
        } 
 
        //-------------------------------------------------------------------
        // Viewport 
        //--------------------------------------------------------------------
        internal Rect Viewport
        {
            get 
            {
                return new Rect(this.Size); 
            } 
        }
 
        #endregion Internal Properties

        //-------------------------------------------------------------------
        // 
        //  Private Methods
        // 
        //-------------------------------------------------------------------- 

        #region Private Methods 

        /// 
        /// Destroy all unmanaged resources.
        ///  
        /// Whether dispose is caused by explicit call to Dispose.
        ///  
        /// Finalizer needs to follow rules below: 
        ///     a) Your Finalize method must tolerate partially constructed instances.
        ///     b) Your Finalize method must consider the consequence of failure. 
        ///     c) Your object is callable after Finalization.
        ///     d) Your object is callable during Finalization.
        ///     e) Your Finalizer could be called multiple times.
        ///     f) Your Finalizer runs in a delicate security context. 
        /// See: http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx
        ///  
        private void Dispose(bool disposing) 
        {
            // Do actual dispose only once. 
            if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0)
            {
                if (disposing)
                { 
                    // Clear content of the root visual
                    if (this.PageVisual != null) 
                    { 
                        // Disconnect all embedded visuals (UIElements) to make sure that
                        // they are not part of visual tree when page is destroyed. 
                        // This is necessary for building proper event route, because
                        // BuildRoute prefers visual tree.
                        DestroyVisualLinks(this.PageVisual);
 
                        // Clear its drawing context and children collection.
                        this.PageVisual.Children.Clear(); 
                        this.PageVisual.ClearDrawingContext(); 
                    }
 
                    // Dispose PTS page
                    if (_ptsPage != null)
                    {
                        _ptsPage.Dispose(); 
                    }
                } 
                try 
                {
                    if (disposing) 
                    {
                        // Notify interested parties about disposal of the page.
                        OnPageDestroyed(EventArgs.Empty);
                    } 

                } 
                finally 
                {
                    _ptsPage = null; 
                    _structuralCache = null;
                    _textView = null;
                    _DependentMax = null;
                } 
            }
        } 
 
        //--------------------------------------------------------------------
        // Update visual representation of the page. 
        //-------------------------------------------------------------------
        private void UpdateVisual()
        {
            if (this.PageVisual == null) 
            {
                SetVisual(new PageVisual(this)); 
            } 
            if (_visualNeedsUpdate)
            { 
                // Draw background
                this.PageVisual.DrawBackground((Brush)_structuralCache.PropertyOwner.GetValue(FlowDocument.BackgroundProperty), new Rect(_partitionSize));

                // Connect visual created by PTS page. 
                ContainerVisual pageVisual = null;
                using (_structuralCache.SetDocumentVisualValidationContext(this)) 
                { 
                    pageVisual = _ptsPage.GetPageVisual(); // This method will update the visual tree if necessary.
                    _structuralCache.DetectInvalidOperation(); 
                }
                this.PageVisual.Child = pageVisual; // No-op if already connected.

                // DocumentPage.Visual for printing scenarions needs to be always returned 
                // in LeftToRight FlowDirection. Hence, if the document is RightToLeft,
                // mirroring transform need to be applied to the content of DocumentPage.Visual. 
                FlowDirection flowdirection = (FlowDirection)_structuralCache.PropertyOwner.GetValue(FlowDocument.FlowDirectionProperty); 
                PtsHelper.UpdateMirroringTransform(FlowDirection.LeftToRight, flowdirection, pageVisual, Size.Width);
 
                // Clear update info for PTS page.
                using (_structuralCache.SetDocumentVisualValidationContext(this))
                {
                    _ptsPage.ClearUpdateInfo(); 
                    _structuralCache.DetectInvalidOperation();
                } 
                _visualNeedsUpdate = false; 
            }
        } 

        //--------------------------------------------------------------------
        // Prepares for format page process.
        //------------------------------------------------------------------- 
        private void OnBeforeFormatPage()
        { 
            if (_visualNeedsUpdate) 
            {
                // Clear update info for PTS page. 
                _ptsPage.ClearUpdateInfo();
            }
        }
 
        //-------------------------------------------------------------------
        // Completes format page process. 
        //------------------------------------------------------------------- 
        private void OnAfterFormatPage()
        { 
            if (_textView != null)
            {
                _textView.Invalidate();
            } 
            _visualNeedsUpdate = true;
        } 
 
        //--------------------------------------------------------------------
        // IContentHost Helpers 
        //-------------------------------------------------------------------

        /// 
        /// Searches for an element in the _structuralCache.TextContainer. If the element is found, returns the 
        /// position at which it is found. Otherwise returns null.
        ///  
        ///  
        /// Element to be found.
        ///  
        /// 
        /// bool value indicating whether the search should only be limited to the text view of the page,
        /// in which case we search only text segments in the text view
        ///  
        private TextPointer FindElementPosition(IInputElement e, bool isLimitedToTextView)
        { 
            // Parameter validation 
            Debug.Assert(e != null);
 
            // Validate that this function is only called when a TextContainer exists as complex content
            Debug.Assert(_structuralCache.TextContainer is TextContainer);

            TextPointer elementPosition = null; 

            // If e is a TextElement we can optimize by checking its TextContainer 
            if (e is TextElement) 
            {
                if ((e as TextElement).TextContainer == _structuralCache.TextContainer) 
                {
                    // Element found
                    elementPosition = new TextPointer((e as TextElement).ElementStart);
                } 
                // else: elementPosition stays null
            } 
            else 
            {
                // Else: search for e in the complex content 
                if (!(_structuralCache.TextContainer.Start is TextPointer) ||
                    !(_structuralCache.TextContainer.End is TextPointer))
                {
                    // Invalid TextContainer, don't search 
                    return null;
                } 
 
                TextPointer searchPosition = new TextPointer(_structuralCache.TextContainer.Start as TextPointer);
                while (elementPosition == null && ((ITextPointer)searchPosition).CompareTo(_structuralCache.TextContainer.End) < 0) 
                {
                    // Search each position in _structuralCache.TextContainer for the element
                    switch (searchPosition.GetPointerContext(LogicalDirection.Forward))
                    { 
                        case TextPointerContext.EmbeddedElement:
                            DependencyObject embeddedObject = searchPosition.GetAdjacentElement(LogicalDirection.Forward); 
                            if (embeddedObject is ContentElement || embeddedObject is UIElement) 
                            {
                                if (embeddedObject == e as ContentElement || embeddedObject == e as UIElement) 
                                {
                                    // Element found. Stop searching
                                    elementPosition = new TextPointer(searchPosition);
                                    break; 
                                }
                            } 
                            break; 
                        default:
                            break; 
                    }
                    searchPosition.MoveToNextContextPosition(LogicalDirection.Forward);
                }
            } 

            // If the element was found, check if we are limited to text view 
            if (elementPosition != null) 
            {
                if (isLimitedToTextView) 
                {
                    // At this point, we should create TextView if it doesn't exist
                    _textView = GetTextView();
                    Invariant.Assert(_textView != null); 
                    // Check all segements in text view for position
                    for (int segmentIndex = 0; segmentIndex < ((ITextView)_textView).TextSegments.Count; segmentIndex++) 
                    { 
                        if (((ITextPointer)elementPosition).CompareTo(((ITextView)_textView).TextSegments[segmentIndex].Start) >= 0 &&
                            ((ITextPointer)elementPosition).CompareTo(((ITextView)_textView).TextSegments[segmentIndex].End) < 0) 
                        {
                            // Element lies within a segment. Return position
                            return elementPosition;
                        } 
                    }
                    // Element not found in all segments of TextView. Set position to null 
                    elementPosition = null; 
                }
            } 
            return elementPosition;
        }

        //-------------------------------------------------------------------- 
        // Disconnect all embedded visuals (UIElements) to make sure that
        // they are not part of visual tree when page is destroyed. 
        // This is necessary for building proper event route, because 
        // BuildRoute prefers visual tree.
        //-------------------------------------------------------------------- 
        private void DestroyVisualLinks(ContainerVisual visual)
        {
            VisualCollection vc = visual.Children;
            if (vc != null) 
            {
                for (int index = 0; index < vc.Count; index++) 
                { 
                    if (vc[index] is UIElementIsland)
                    { 
                        vc.RemoveAt(index);
                    }
                    else
                    { 
                        Invariant.Assert(vc[index] is ContainerVisual, "The children should always derive from ContainerVisual");
                        DestroyVisualLinks((ContainerVisual)(vc[index])); 
                    } 
                }
            } 
        }

        /// 
        /// Raise TextView.Updated event. 
        /// 
        private void ValidateTextView() 
        { 
            if (_textView != null)
            { 
                _textView.OnUpdated();
            }
        }
 
        /// 
        /// Gets TextView for this page. 
        ///  
        private TextDocumentView GetTextView()
        { 
            TextDocumentView textView = (TextDocumentView)((IServiceProvider)this).GetService(typeof(ITextView));
            Invariant.Assert(textView != null);
            return textView;
        } 

        #endregion Private Methods 
 
        //-------------------------------------------------------------------
        // 
        //  Private Properties
        //
        //--------------------------------------------------------------------
 
        #region Private Properties
 
        //------------------------------------------------------------------- 
        // Visual representing content of the page.
        //------------------------------------------------------------------- 
        private PageVisual PageVisual
        {
            get { return (base.Visual as PageVisual); }
        } 

        #endregion Private Properties 
 
        //-------------------------------------------------------------------
        // 
        //  Private Fields
        //
        //--------------------------------------------------------------------
 
        #region Private Fields
 
        //------------------------------------------------------------------- 
        // Associated PTS page.
        //-------------------------------------------------------------------- 
        private PtsPage _ptsPage;

        //--------------------------------------------------------------------
        // Structural cache. 
        //-------------------------------------------------------------------
        private StructuralCache _structuralCache; 
 
        //--------------------------------------------------------------------
        // Number of lines formatted during page formatting. 
        // NOTE: This field is used only internally for layout DRTs.
        //-------------------------------------------------------------------
        private int _formattedLinesCount;
 
        //-------------------------------------------------------------------
        // TextView associated with the document page. 
        //------------------------------------------------------------------- 
        private TextDocumentView _textView;
 
        //--------------------------------------------------------------------
        // Size of partition for the page.
        //-------------------------------------------------------------------
        private Size _partitionSize; 

        //-------------------------------------------------------------------- 
        // Margin of the page. 
        //--------------------------------------------------------------------
        private Thickness _pageMargin; 

        //-------------------------------------------------------------------
        // Is it already disposed?
        //-------------------------------------------------------------------- 
        private int _disposed;
 
        //------------------------------------------------------------------- 
        // Max of dcpDepend for page
        //------------------------------------------------------------------- 
        private TextPointer _DependentMax;

        //-------------------------------------------------------------------
        // Need to update visual? 
        //--------------------------------------------------------------------
        private bool _visualNeedsUpdate; 
 
        //-------------------------------------------------------------------
        // Width of page during last format 
        //--------------------------------------------------------------------
        private double _lastFormatWidth;

        #endregion Private Fields 

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

        #region IServiceProvider Members
 
        //--------------------------------------------------------------------
        // Gets the service object of the specified type. FlowDocumentPage 
        // currently supports only TextView 
        //
        //      serviceType - an object that specifies the type of service 
        //                    object to get
        //
        // Returns: A service object of type serviceType. A null reference
        //          if there is no service object of type serviceType. 
        //-------------------------------------------------------------------
        object IServiceProvider.GetService(Type serviceType) 
        { 
            if (serviceType == null)
            { 
                throw new ArgumentNullException("serviceType");
            }

            if (serviceType == typeof(ITextView)) 
            {
                if (_textView == null) 
                { 
                    _textView = new TextDocumentView(this, _structuralCache.TextContainer);
                } 

                return _textView;
            }
 
            return null;
        } 
 
        #endregion IServiceProvider Members
 
        //-------------------------------------------------------------------
        //
        //  IContentHost Members
        // 
        //-------------------------------------------------------------------
 
        #region IContentHost Members 

        ///  
        /// Hit tests to the correct ContentElement
        /// within the ContentHost that the mouse
        /// is over
        ///  
        /// 
        /// Mouse coordinates relative to 
        /// the ContentHost 
        /// 
        IInputElement IContentHost.InputHitTest(Point point) 
        {
            return this.InputHitTestCore(point);
        }
 
        /// 
        /// Returns rectangles for element. First finds element by navigating in FlowDocumentPage. 
        /// If element is not found or if call to get rectangles from FlowDocumentPage returns null 
        /// we return an empty collection. If the layout is not valid we return null.
        ///  
        /// 
        /// Content element for which rectangles are required
        /// 
        ReadOnlyCollection IContentHost.GetRectangles(ContentElement child) 
        {
            // Restrict search to only the text segments in the page's text view. This is not needed for 
            // HitTest because it takes only a point 
            return this.GetRectanglesCore(child, true);
        } 

        /// 
        /// Returns elements hosted by the content host as an enumerator class
        ///  
        IEnumerator IContentHost.HostedElements
        { 
            get 
            {
                return this.HostedElementsCore as IEnumerator; 
            }
        }

        ///  
        /// Called when a UIElement-derived class which is hosted by a IContentHost changes it�s DesiredSize
        ///  
        ///  
        /// Child element whose DesiredSize has changed
        ///  
        void IContentHost.OnChildDesiredSizeChanged(UIElement child)
        {
            this.OnChildDesiredSizeChangedCore(child);
        } 

        #endregion IContentHost Members 
    } 
}

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