Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / PtsHost / FlowDocumentPage.cs / 1305600 / 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 ReadOnlyCollectionGetRectanglesCore(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 IEnumeratorHostedElementsCore { 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 ReadOnlyCollectionGetColumnResults(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 ReadOnlyCollectionGetParagraphResultsFromColumn(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/[....]/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 /// ReadOnlyCollectionIContentHost.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 /// IEnumeratorIContentHost.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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Base64Encoder.cs
- WindowsProgressbar.cs
- HashHelper.cs
- InstanceStoreQueryResult.cs
- Stacktrace.cs
- SignatureDescription.cs
- Decoder.cs
- TreeView.cs
- SHA256Cng.cs
- ScriptDescriptor.cs
- NotifyCollectionChangedEventArgs.cs
- UriTemplateClientFormatter.cs
- IntSecurity.cs
- CacheChildrenQuery.cs
- SqlStatistics.cs
- ListControl.cs
- BinaryObjectInfo.cs
- ListParaClient.cs
- ReturnEventArgs.cs
- ControlEvent.cs
- ProjectionPruner.cs
- DebugView.cs
- ImportedNamespaceContextItem.cs
- DataGridParentRows.cs
- PublisherMembershipCondition.cs
- CodeFieldReferenceExpression.cs
- ListBoxItem.cs
- HttpGetServerProtocol.cs
- PostBackOptions.cs
- PagedDataSource.cs
- InstanceHandleReference.cs
- HTTP_SERVICE_CONFIG_URLACL_KEY.cs
- IndependentAnimationStorage.cs
- MetafileHeader.cs
- TypeTypeConverter.cs
- HwndHostAutomationPeer.cs
- RuntimeCompatibilityAttribute.cs
- WriteableBitmap.cs
- DispatcherEventArgs.cs
- HtmlTableRow.cs
- SetterBaseCollection.cs
- MissingMethodException.cs
- SocketElement.cs
- _NestedSingleAsyncResult.cs
- LoginNameDesigner.cs
- TokenBasedSet.cs
- DecimalAnimationBase.cs
- ThrowHelper.cs
- _CommandStream.cs
- SoundPlayer.cs
- TextMetrics.cs
- ContentDisposition.cs
- QueryInterceptorAttribute.cs
- DbCommandTree.cs
- HMACSHA1.cs
- AsyncDataRequest.cs
- KeyInterop.cs
- SafeHandles.cs
- FormatVersion.cs
- HeaderUtility.cs
- CodeCatchClauseCollection.cs
- Content.cs
- CheckBoxDesigner.cs
- TimeoutTimer.cs
- WizardPanel.cs
- DataGridDefaultColumnWidthTypeConverter.cs
- XmlSchemaAll.cs
- CalendarBlackoutDatesCollection.cs
- TrackingQuery.cs
- TextRangeEditLists.cs
- XPathAncestorIterator.cs
- XmlWriterSettings.cs
- WebProxyScriptElement.cs
- OutKeywords.cs
- ToolStripDropDownItem.cs
- SafeRightsManagementSessionHandle.cs
- ISFTagAndGuidCache.cs
- SqlCacheDependencyDatabase.cs
- ResourceManager.cs
- RelationshipEndCollection.cs
- BasicBrowserDialog.cs
- DataGridViewComboBoxEditingControl.cs
- XmlHierarchyData.cs
- HelloOperation11AsyncResult.cs
- OutputCacheSection.cs
- ThreadLocal.cs
- RepeaterItemCollection.cs
- ExtensionFile.cs
- SafeHandles.cs
- SchemaImporterExtensionElementCollection.cs
- _AutoWebProxyScriptHelper.cs
- NativeStructs.cs
- FixedSOMImage.cs
- Helpers.cs
- PrincipalPermission.cs
- ColumnCollection.cs
- CompressEmulationStream.cs
- DetailsView.cs
- FormClosedEvent.cs
- BitmapCache.cs