|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Description:
// Implements the FixedDocumentSequence element
//
using MS.Internal.Documents;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Automation.Peers; // AutomationPeer
using System.Windows.Threading; // Dispatcher
using System.Windows.Media; // Visual
using System.Windows.Markup; // IAddChild, ContentProperty
using System.Windows.Navigation;
#pragma warning disable 1634, 1691 // suppressing PreSharp warnings
namespace System.Windows.Documents
{
/// <summary>
/// FixedDocumentSequence is a IDocumentPaginatorSource that composites other IDocumentPaginatorSource
/// via DocumentReference
/// </summary>
[ContentProperty("References")]
public class FixedDocumentSequence : FrameworkContentElement, IDocumentPaginatorSource, IAddChildInternal, IServiceProvider, IFixedNavigate, IUriContext
{
//--------------------------------------------------------------------
//
// Connstructors
//
//---------------------------------------------------------------------
#region Constructors
static FixedDocumentSequence()
{
FocusableProperty.OverrideMetadata(typeof(FixedDocumentSequence), new FrameworkPropertyMetadata(true));
}
/// <summary>
/// Default FixedDocumentSequence constructor
/// </summary>
public FixedDocumentSequence() : base()
{
_Init();
}
#endregion Constructors
//--------------------------------------------------------------------
//
// Public Methods
//
//---------------------------------------------------------------------
#region IServiceProvider Members
/// <summary>
/// <see cref="IServiceProvider.GetService" />
/// </summary>
object IServiceProvider.GetService(Type serviceType)
{
ArgumentNullException.ThrowIfNull(serviceType);
if (serviceType == typeof(ITextContainer))
{
return this.TextContainer;
}
if (serviceType == typeof(RubberbandSelector))
{
// create this on demand, but not through the property, only through the
// service, so it is only created when it's actually used
if (_rubberbandSelector == null)
{
_rubberbandSelector = new RubberbandSelector();
}
return _rubberbandSelector;
}
return null;
}
#endregion IServiceProvider Members
#region IAddChild
///<summary>
/// Called to Add the object as a Child.
///</summary>
///<param name="value">
/// Object to add as a child
///</param>
void IAddChild.AddChild(Object value)
{
ArgumentNullException.ThrowIfNull(value);
// Dispatcher.VerifyAccess();
DocumentReference docRef = value as DocumentReference;
if (docRef == null)
{
throw new ArgumentException(SR.Format(SR.UnexpectedParameterType, value.GetType(), typeof(DocumentReference)), "value");
}
if (docRef.IsInitialized)
{
_references.Add(docRef);
}
else
{
DocumentsTrace.FixedDocumentSequence.Content.Trace($"Doc {_references.Count} Deferred");
if (_partialRef == null)
{
_partialRef = docRef;
_partialRef.Initialized += new EventHandler(_OnDocumentReferenceInitialized);
}
else
{
throw new InvalidOperationException(SR.PrevoiusUninitializedDocumentReferenceOutstanding);
}
}
}
///<summary>
/// Called when text appears under the tag in markup
///</summary>
///<param name="text">
/// Text to Add to the Object
///</param>
void IAddChild.AddText(string text)
{
XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this);
}
#endregion
#region IFixedNavigate
void IFixedNavigate.NavigateAsync(string elementID)
{
if (IsPageCountValid == true)
{
FixedHyperLink.NavigateToElement(this, elementID);
}
else
{
_navigateAfterPagination = true;
_navigateFragment = elementID;
}
}
UIElement IFixedNavigate.FindElementByID(string elementID, out FixedPage rootFixedPage)
{
UIElement uiElementRet = null;
rootFixedPage = null;
DynamicDocumentPaginator childPaginator;
FixedDocument childFixedDoc;
if (Char.IsDigit(elementID[0]))
{
//We convert to a page number here.
int pageNumber = Convert.ToInt32(elementID, CultureInfo.InvariantCulture) - 1;
int childPageNumber;
if (TranslatePageNumber(pageNumber, out childPaginator, out childPageNumber))
{
childFixedDoc = childPaginator.Source as FixedDocument;
if (childFixedDoc != null)
{
uiElementRet = childFixedDoc.GetFixedPage(childPageNumber);
}
}
}
else
{
foreach (DocumentReference docRef in References)
{
childPaginator = GetPaginator(docRef);
childFixedDoc = childPaginator.Source as FixedDocument;
if (childFixedDoc != null)
{
uiElementRet = ((IFixedNavigate)childFixedDoc).FindElementByID(elementID, out rootFixedPage);
if (uiElementRet != null)
{
break;
}
}
}
}
return uiElementRet;
}
#endregion
#region LogicalTree
/// <summary>
/// Returns enumerator to logical children
/// </summary>
protected internal override IEnumerator LogicalChildren
{
get
{
// Dispatcher.VerifyAccess();
DocumentReference[] docArray = new DocumentReference[_references.Count];
this._references.CopyTo(docArray, 0);
return docArray.GetEnumerator();
}
}
#endregion LogicalTree
#region IDocumentPaginatorSource Members
/// <summary>
/// An object which paginates content.
/// </summary>
public DocumentPaginator DocumentPaginator
{
get { return _paginator; }
}
#endregion IDocumentPaginatorSource Members
#region DocumentPaginator
/// <summary>
/// <see cref="System.Windows.Documents.DocumentPaginator.GetPage"/>
/// </summary>
internal DocumentPage GetPage(int pageNumber)
{
DocumentsTrace.FixedFormat.IDF.Trace($"IDP.GetPage({pageNumber})");
// Make sure that the call is in the right context.
// Dispatcher.VerifyAccess();
// Page number cannot be negative.
if (pageNumber < 0)
{
throw new ArgumentOutOfRangeException("pageNumber", SR.IDPNegativePageNumber);
}
DocumentPage innerDP = null;
DynamicDocumentPaginator innerPaginator;
int innerPageNumber;
// Find the appropriate inner IDF and BR
if (TranslatePageNumber(pageNumber, out innerPaginator, out innerPageNumber))
{
innerDP = innerPaginator.GetPage(innerPageNumber);
Debug.Assert(innerDP != null);
// Now warp inner DP and return it
return new FixedDocumentSequenceDocumentPage(this, innerPaginator, innerDP);
}
return DocumentPage.Missing;
}
/// <summary>
/// Returns a FixedDocumentSequenceDocumentPage for the specified document and page number.
/// </summary>
internal DocumentPage GetPage(FixedDocument document, int fixedDocPageNumber)
{
if (fixedDocPageNumber < 0)
{
throw new ArgumentOutOfRangeException("fixedDocPageNumber", SR.IDPNegativePageNumber);
}
ArgumentNullException.ThrowIfNull(document);
DocumentPage innerDP = document.GetPage(fixedDocPageNumber);
Debug.Assert(innerDP != null);
// Now wrap inner DP and return it
return new FixedDocumentSequenceDocumentPage(this, document.DocumentPaginator as DynamicDocumentPaginator, innerDP);
}
/// <summary>
/// <see cref="System.Windows.Documents.DocumentPaginator.GetPageAsync(int,object)"/>
/// </summary>
internal void GetPageAsync(int pageNumber, object userState)
{
DocumentsTrace.FixedFormat.IDF.Trace($"IDP.GetPageAsync({pageNumber}, {userState})");
// Make sure that the call is in the right context.
// Dispatcher.VerifyAccess();
// Page number cannot be negative.
if (pageNumber < 0)
{
throw new ArgumentOutOfRangeException("pageNumber", SR.IDPNegativePageNumber);
}
ArgumentNullException.ThrowIfNull(userState);
// Add to outstanding AsyncOp list
GetPageAsyncRequest asyncRequest = new GetPageAsyncRequest(new RequestedPage(pageNumber/*childPaginator, childPageNumber*/), userState);
_asyncOps[userState] = asyncRequest;
DispatcherOperationCallback queueTask = new DispatcherOperationCallback(_GetPageAsyncDelegate);
Dispatcher.BeginInvoke(DispatcherPriority.Background, queueTask, asyncRequest);
}
/// <summary>
/// <see cref="DynamicDocumentPaginator.GetPageNumber"/>
/// </summary>
internal int GetPageNumber(ContentPosition contentPosition)
{
ArgumentNullException.ThrowIfNull(contentPosition);
// ContentPosition may be only created by DynamicDocumentPaginator.GetObjectPosition or
// DynamicDocumentPaginator.GetPagePosition.
// Because of that we are expecting one of 2 types here.
DynamicDocumentPaginator childPaginator = null;
ContentPosition childContentPosition = null;
if (contentPosition is DocumentSequenceTextPointer)
{
DocumentSequenceTextPointer dsTextPointer = (DocumentSequenceTextPointer)contentPosition;
#pragma warning suppress 6506 // dsTextPointer is obviously not null
childPaginator = GetPaginator(dsTextPointer.ChildBlock.DocRef);
childContentPosition = dsTextPointer.ChildPointer as ContentPosition;
}
if (childContentPosition == null)
{
throw new ArgumentException(SR.IDPInvalidContentPosition);
}
int childPageNumber = childPaginator.GetPageNumber(childContentPosition);
int pageNumber;
_SynthesizeGlobalPageNumber(childPaginator, childPageNumber, out pageNumber);
return pageNumber;
}
/// <summary>
/// <see cref="System.Windows.Documents.DocumentPaginator.CancelAsync"/>
/// </summary>
internal void CancelAsync(object userState)
{
DocumentsTrace.FixedFormat.IDF.Trace($"IDP.GetPageAsyncCancel([{userState}])");
ArgumentNullException.ThrowIfNull(userState);
if (_asyncOps.ContainsKey(userState))
{
GetPageAsyncRequest asyncRequest = _asyncOps[userState];
if (asyncRequest != null)
{
asyncRequest.Cancelled = true;
if (asyncRequest.Page.ChildPaginator != null)
{
asyncRequest.Page.ChildPaginator.CancelAsync(asyncRequest);
}
}
}
}
/// <summary>
/// <see cref="DynamicDocumentPaginator.GetObjectPosition"/>
/// </summary>
internal ContentPosition GetObjectPosition(Object o)
{
ArgumentNullException.ThrowIfNull(o);
foreach (DocumentReference docRef in References)
{
DynamicDocumentPaginator childPaginator = GetPaginator(docRef);
if (childPaginator != null)
{
ContentPosition cp = childPaginator.GetObjectPosition(o);
if (cp != ContentPosition.Missing && (cp is ITextPointer))
{
ChildDocumentBlock childBlock = new ChildDocumentBlock(this.TextContainer, docRef);
return new DocumentSequenceTextPointer(childBlock, (ITextPointer)cp);
}
}
}
return ContentPosition.Missing;
}
/// <summary>
/// <see cref="DynamicDocumentPaginator.GetPagePosition"/>
/// </summary>
internal ContentPosition GetPagePosition(DocumentPage page)
{
FixedDocumentSequenceDocumentPage docPage = page as FixedDocumentSequenceDocumentPage;
if (docPage == null)
{
return ContentPosition.Missing;
}
return docPage.ContentPosition;
}
/// <summary>
/// <see cref="System.Windows.Documents.DocumentPaginator.IsPageCountValid"/>
/// </summary>
internal bool IsPageCountValid
{
get
{
bool documentSequencePageCountFinal = true;
if (IsInitialized)
{
foreach (DocumentReference docRef in References)
{
DynamicDocumentPaginator paginator = GetPaginator(docRef);
if (paginator == null || !paginator.IsPageCountValid)
{
documentSequencePageCountFinal = false;
break;
}
}
}
else
{
documentSequencePageCountFinal = false;
}
return documentSequencePageCountFinal;
}
}
/// <summary>
/// <see cref="System.Windows.Documents.DocumentPaginator.PageCount"/>
/// </summary>
internal int PageCount
{
get
{
//
// This can be optimized if the page count for each IDP won't change
// && the list of IDP won't change. When each doc.IsPageCountValid is
// true, then page count won't change.
//
int count = 0;
foreach (DocumentReference docRef in References)
{
DynamicDocumentPaginator paginator = GetPaginator(docRef);
if (paginator != null)
{
count += paginator.PageCount;
if (!paginator.IsPageCountValid)
{
break;
}
}
}
return count;
}
}
/// <summary>
/// <see cref="System.Windows.Documents.DocumentPaginator.PageSize"/>
/// </summary>
internal Size PageSize
{
get { return _pageSize; }
set { _pageSize = value; }
}
#endregion DocumentPaginator
#region IUriContext
/// <summary>
/// <see cref="IUriContext.BaseUri" />
/// </summary>
Uri IUriContext.BaseUri
{
get { return (Uri) GetValue(BaseUriHelper.BaseUriProperty); }
set { SetValue(BaseUriHelper.BaseUriProperty, value); }
}
#endregion
//-------------------------------------------------------------- ------
//
// Public Properties
//
//---------------------------------------------------------------------
#region Public Properties
/// <summary>
/// Get a collection of DocumentReference that this FixedDocumentSequence contains.
/// </summary>
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
[CLSCompliant(false)]
public DocumentReferenceCollection References
{
get
{
// Dispatcher.VerifyAccess();
return _references;
}
}
/// <summary>
///
/// </summary>
public static readonly DependencyProperty PrintTicketProperty
= DependencyProperty.RegisterAttached("PrintTicket", typeof(object), typeof(FixedDocumentSequence),
new FrameworkPropertyMetadata((object)null));
/// <summary>
/// Get/Set PrintTicket Property
/// </summary>
public object PrintTicket
{
get
{
object printTicket = GetValue(PrintTicketProperty);
return printTicket;
}
set
{
SetValue(PrintTicketProperty,value);
}
}
#endregion Public Properties
//--------------------------------------------------------------------
//
// Public Events
//
//---------------------------------------------------------------------
//--------------------------------------------------------------------
//
// Protected Methods
//
//---------------------------------------------------------------------
#region Protected Methods
/// <summary>
/// Creates AutomationPeer (<see cref="ContentElement.OnCreateAutomationPeer"/>)
/// </summary>
protected override AutomationPeer OnCreateAutomationPeer()
{
return new DocumentAutomationPeer(this);
}
#endregion Protected Methods
//--------------------------------------------------------------------
//
// Internal Methods
//
//---------------------------------------------------------------------
#region Internal Method
internal DynamicDocumentPaginator GetPaginator(DocumentReference docRef)
{
// #966803: Source change won't be a support scenario.
Debug.Assert(docRef != null);
DynamicDocumentPaginator paginator = null;
IDocumentPaginatorSource document = docRef.CurrentlyLoadedDoc;
if (document != null)
{
paginator = document.DocumentPaginator as DynamicDocumentPaginator;
Debug.Assert(paginator != null);
}
else
{
document = docRef.GetDocument(false /*forceReload*/);
if (document != null)
{
paginator = document.DocumentPaginator as DynamicDocumentPaginator;
Debug.Assert(paginator != null);
// hook up event handlers
paginator.PaginationCompleted += new EventHandler(_OnChildPaginationCompleted);
paginator.PaginationProgress += new PaginationProgressEventHandler(_OnChildPaginationProgress);
paginator.PagesChanged += new PagesChangedEventHandler(_OnChildPagesChanged);
}
}
return paginator;
}
//----------------------------------------------------------------------
// IDP Helper
//----------------------------------------------------------------------
// Take a global page number and translate it into a child paginator with a child page number.
// A document will be look at if the previous document has finished pagination.
internal bool TranslatePageNumber(int pageNumber, out DynamicDocumentPaginator childPaginator, out int childPageNumber)
{
childPaginator = null;
childPageNumber = 0;
foreach (DocumentReference docRef in References)
{
childPaginator = GetPaginator(docRef);
if (childPaginator != null)
{
childPageNumber = pageNumber;
if (childPaginator.PageCount > childPageNumber)
{
// The page falls inside this paginator.
return true;
}
else
{
if (!childPaginator.IsPageCountValid)
{
// Don't bother look further if this doc has not finished
// pagination
break;
}
pageNumber -= childPaginator.PageCount;
}
}
}
return false;
}
#endregion Internal Method
//--------------------------------------------------------------------
//
// Internal Properties
//
//---------------------------------------------------------------------
#region Internal Properties
internal DocumentSequenceTextContainer TextContainer
{
get
{
if (_textContainer == null)
{
_textContainer = new DocumentSequenceTextContainer(this);
}
return _textContainer;
}
}
#endregion Internal Properties
//--------------------------------------------------------------------
//
// Private Methods
//
//---------------------------------------------------------------------
#region Private Methods
private void _Init()
{
_paginator = new FixedDocumentSequencePaginator(this);
_references = new DocumentReferenceCollection();
_references.CollectionChanged += new NotifyCollectionChangedEventHandler(_OnCollectionChanged);
_asyncOps = new Dictionary<Object,GetPageAsyncRequest>();
_pendingPages = new List<RequestedPage>();
_pageSize = new Size(8.5d * 96d, 11.0d * 96d);
this.Initialized += new EventHandler(OnInitialized);
}
private void OnInitialized(object sender, EventArgs e)
{
bool documentSequencePageCountFinal = true;
foreach (DocumentReference docRef in References)
{
DynamicDocumentPaginator paginator = GetPaginator(docRef);
if (paginator == null || !paginator.IsPageCountValid)
{
documentSequencePageCountFinal = false;
break;
}
}
if (documentSequencePageCountFinal == true)
{
_paginator.NotifyPaginationCompleted(EventArgs.Empty);
}
if (PageCount > 0)
{
DocumentPage docPage = GetPage(0);
if (docPage != null)
{
FixedPage page = docPage.Visual as FixedPage;
if (page != null)
{
this.Language = page.Language;
}
}
}
}
//----------------------------------------------------------------------
// Content Model Helper
//----------------------------------------------------------------------
private void _OnDocumentReferenceInitialized(object sender, EventArgs e)
{
DocumentReference docRef = (DocumentReference)sender;
if (docRef == _partialRef)
{
DocumentsTrace.FixedDocumentSequence.Content.Trace($"Loaded DocumentReference {_references.Count}");
_partialRef.Initialized -= new EventHandler(_OnDocumentReferenceInitialized);
_partialRef = null;
_references.Add(docRef);
}
}
private void _OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (args.Action == NotifyCollectionChangedAction.Add)
{
if (args.NewItems.Count != 1)
{
throw new NotSupportedException(SR.RangeActionsNotSupported);
}
else
{
// get the affected item
object item = args.NewItems[0];
DocumentsTrace.FixedDocumentSequence.Content.Trace($"_OnCollectionChange: Add {item.GetHashCode()}");
AddLogicalChild(item);
int pageCount = this.PageCount;
DynamicDocumentPaginator paginator = GetPaginator((DocumentReference)item);
if (paginator == null)
{
throw new ApplicationException(SR.DocumentReferenceHasInvalidDocument);
}
int addedPages = paginator.PageCount;
int firstPage = pageCount - addedPages;
if (addedPages > 0)
{
DocumentsTrace.FixedDocumentSequence.Content.Trace($"_OnCollectionChange: Add with IDP {paginator.GetHashCode()}");
_paginator.NotifyPaginationProgress(new PaginationProgressEventArgs(firstPage, addedPages));
_paginator.NotifyPagesChanged(new PagesChangedEventArgs(firstPage, addedPages));
}
}
}
else
{
throw new NotSupportedException(SR.Format(SR.UnexpectedCollectionChangeAction, args.Action));
}
}
// Take a child paginator and a page nubmer, find which global page number it corresponds to
private bool _SynthesizeGlobalPageNumber(DynamicDocumentPaginator childPaginator, int childPageNumber, out int pageNumber)
{
pageNumber = 0;
foreach (DocumentReference docRef in References)
{
DynamicDocumentPaginator innerPaginator = GetPaginator(docRef);
if (innerPaginator != null)
{
if (innerPaginator == childPaginator)
{
pageNumber += childPageNumber;
return true;
}
pageNumber += innerPaginator.PageCount;
}
}
return false;
}
#if PAYLOADCODECOVERAGE
// Get total page count before a given child document index.
private int _GetPageCountBefore(int childIndex)
{
int count = 0;
for(int i = 0; i < childIndex && i < References.Count; i++)
{
DocumentPaginator childDoc = GetPaginator(References[i]);
if (childDoc != null)
{
count += childDoc.PageCount;
if (!childDoc.IsPageCountValid)
break;
}
}
return count;
}
#endif
//----------------------------------------------------------------------
// Child IDP Events
//----------------------------------------------------------------------
private void _OnChildPaginationCompleted(object sender, EventArgs args)
{
DocumentsTrace.FixedDocumentSequence.IDF.Trace("_OnChildPaginationCompleted");
if (IsPageCountValid)
{
_paginator.NotifyPaginationCompleted(EventArgs.Empty);
if (_navigateAfterPagination)
{
FixedHyperLink.NavigateToElement(this, _navigateFragment);
_navigateAfterPagination = false;
}
}
}
private void _OnChildPaginationProgress(object sender, PaginationProgressEventArgs args)
{
DocumentsTrace.FixedDocumentSequence.IDF.Trace("_OnChildPaginationProgress");
int pageNumber;
if (_SynthesizeGlobalPageNumber((DynamicDocumentPaginator)sender, args.Start, out pageNumber))
{
_paginator.NotifyPaginationProgress(new PaginationProgressEventArgs(pageNumber, args.Count));
}
}
private void _OnChildPagesChanged(object sender, PagesChangedEventArgs args)
{
DocumentsTrace.FixedDocumentSequence.IDF.Trace("_OnChildPagesChanged");
int pageNumber;
if (_SynthesizeGlobalPageNumber((DynamicDocumentPaginator)sender, args.Start, out pageNumber))
{
_paginator.NotifyPagesChanged(new PagesChangedEventArgs(pageNumber, args.Count));
}
else
{
_paginator.NotifyPagesChanged(new PagesChangedEventArgs(PageCount, int.MaxValue));
}
}
//----------------------------------------------------------------------
// IDP Async
//----------------------------------------------------------------------
// An async request from the client code
// It is translated into an async request into child paginator
private object _GetPageAsyncDelegate(object arg)
{
DocumentsTrace.FixedDocumentSequence.IDF.Trace("_GetPageAsyncDelegate");
GetPageAsyncRequest asyncRequest = (GetPageAsyncRequest)arg;
int pageNumber = asyncRequest.Page.PageNumber;
if (asyncRequest.Cancelled
|| !TranslatePageNumber(pageNumber, out asyncRequest.Page.ChildPaginator, out asyncRequest.Page.ChildPageNumber)
|| asyncRequest.Cancelled // Check again for cancellation, as previous line may have loaded FixedDocument and taken a while
)
{
_NotifyGetPageAsyncCompleted(DocumentPage.Missing, pageNumber, null, true, asyncRequest.UserState);
_asyncOps.Remove(asyncRequest.UserState);
return null;
}
if (!_pendingPages.Contains(asyncRequest.Page))
{
// Initiate an async request to child paginator only if this page has not been requested.
_pendingPages.Add(asyncRequest.Page);
asyncRequest.Page.ChildPaginator.GetPageCompleted += new GetPageCompletedEventHandler(_OnGetPageCompleted);
// use this asyncRequest as UserState
asyncRequest.Page.ChildPaginator.GetPageAsync(asyncRequest.Page.ChildPageNumber, asyncRequest);
}
return null;
}
// Callback from inner IDP.GetPageAsync
private void _OnGetPageCompleted(object sender, GetPageCompletedEventArgs args)
{
DocumentsTrace.FixedDocumentSequence.IDF.Trace("_OnGetPageCompleted");
// this job is complete
GetPageAsyncRequest completedRequest = (GetPageAsyncRequest)args.UserState;
_pendingPages.Remove(completedRequest.Page);
// wrap the returned result into FixedDocumentSequenceDocumentPage
DocumentPage sdp = DocumentPage.Missing;
int pageNumber = completedRequest.Page.PageNumber;
if (!args.Cancelled && (args.Error == null) && (args.DocumentPage != DocumentPage.Missing))
{
sdp = new FixedDocumentSequenceDocumentPage(this, (DynamicDocumentPaginator)sender, args.DocumentPage);
_SynthesizeGlobalPageNumber((DynamicDocumentPaginator)sender, args.PageNumber, out pageNumber);
}
if (!args.Cancelled)
{
// Notify all outstanding request for this particular page
ArrayList notificationList = new ArrayList();
IEnumerator<KeyValuePair<Object, GetPageAsyncRequest>> ienum = _asyncOps.GetEnumerator();
try
{
while (ienum.MoveNext())
{
GetPageAsyncRequest asyncRequest = ienum.Current.Value;
// Process any outstanding request for this PageContent
if (completedRequest.Page.Equals(asyncRequest.Page))
{
notificationList.Add(ienum.Current.Key);
// this could throw depending on event handlers that are added
_NotifyGetPageAsyncCompleted(sdp, pageNumber, args.Error, asyncRequest.Cancelled, asyncRequest.UserState);
}
}
}
finally
{
// Remove completed requests from current async ops list
foreach (Object userState in notificationList)
{
_asyncOps.Remove(userState);
}
}
}
}
// Notify the caller of IDFAsync.MeasurePageAsync
private void _NotifyGetPageAsyncCompleted(DocumentPage page, int pageNumber, Exception error, bool cancelled, object userState)
{
DocumentsTrace.FixedDocumentSequence.IDF.Trace("_NotifyGetPageAsyncCompleted");
_paginator.NotifyGetPageCompleted(new GetPageCompletedEventArgs(page, pageNumber, error, cancelled, userState));
}
#endregion Private Methods
//--------------------------------------------------------------------
//
// Private Properties
//
//---------------------------------------------------------------------
//--------------------------------------------------------------------
//
// Private Fields
//
//---------------------------------------------------------------------
#region Private Fields
private DocumentReferenceCollection _references;
private DocumentReference _partialRef; // uninitialized doc that is currently parsed.
// IDP
private FixedDocumentSequencePaginator _paginator;
private IDictionary<Object, GetPageAsyncRequest> _asyncOps; // pending request from client code
private IList<RequestedPage> _pendingPages; // pending request to child page
private Size _pageSize;
private bool _navigateAfterPagination = false;
private string _navigateFragment;
// Text OM
private DocumentSequenceTextContainer _textContainer;
// Rubber band selection
private RubberbandSelector _rubberbandSelector;
#endregion Private Fields
//--------------------------------------------------------------------
//
// Nested Class
//
//---------------------------------------------------------------------
#region Nested Class
// Represent a page that is being requested.
private struct RequestedPage
{
internal DynamicDocumentPaginator ChildPaginator;
internal int ChildPageNumber;
internal int PageNumber;
internal RequestedPage(int pageNumber)
{
PageNumber = pageNumber;
ChildPageNumber = 0;
ChildPaginator = null;
}
public override int GetHashCode()
{
return PageNumber;
}
// override Equals semantic
public override bool Equals(object obj)
{
if (!(obj is RequestedPage))
{
return false;
}
return this.Equals((RequestedPage)obj);
}
// Strongly typed version of Equals
public bool Equals(RequestedPage obj)
{
return this.PageNumber == obj.PageNumber;
}
public static bool operator ==(RequestedPage obj1, RequestedPage obj2)
{
return obj1.Equals(obj2);
}
public static bool operator !=(RequestedPage obj1, RequestedPage obj2)
{
return !(obj1.Equals(obj2));
}
}
// Represents an outstanding async request
private class GetPageAsyncRequest
{
internal GetPageAsyncRequest(RequestedPage page, object userState)
{
Page = page;
UserState = userState;
Cancelled = false;
}
internal RequestedPage Page;
internal object UserState;
internal bool Cancelled;
}
#endregion Nested Class
}
//=====================================================================
//
// FixedDocumentSequence's implemenation of DocumentPage
//
internal sealed class FixedDocumentSequenceDocumentPage : DocumentPage, IServiceProvider
{
//--------------------------------------------------------------------
//
// Ctors
//
//---------------------------------------------------------------------
#region Ctors
internal FixedDocumentSequenceDocumentPage(FixedDocumentSequence documentSequence, DynamicDocumentPaginator documentPaginator, DocumentPage documentPage)
: base((documentPage is FixedDocumentPage) ? ((FixedDocumentPage)documentPage).FixedPage : documentPage.Visual, documentPage.Size, documentPage.BleedBox, documentPage.ContentBox)
{
Debug.Assert(documentSequence != null);
Debug.Assert(documentPaginator != null);
Debug.Assert(documentPage != null);
_fixedDocumentSequence = documentSequence;
_documentPaginator = documentPaginator;
_documentPage = documentPage;
}
#endregion Ctors;
//--------------------------------------------------------------------
//
// Public Methods
//
//---------------------------------------------------------------------
#region IServiceProvider
/// <summary>
/// Returns service objects associated with this control.
/// </summary>
/// <param name="serviceType">
/// Specifies the type of service object to get.
/// </param>
object IServiceProvider.GetService(Type serviceType)
{
ArgumentNullException.ThrowIfNull(serviceType);
if (serviceType == typeof(ITextView))
{
if (_textView == null)
{
_textView = new DocumentSequenceTextView(this);
}
return _textView;
}
return null;
}
#endregion IServiceProvider
#region Public Methods
#if DEBUG
/// <summary>
/// <see cref="System.Object.ToString" />
/// </summary>
public override string ToString()
{
return string.Create(CultureInfo.InvariantCulture, $"SDP:D{_DocumentIndex}");
}
#endif
#endregion Public Methods
//--------------------------------------------------------------------
//
// Internal Methods
//
//---------------------------------------------------------------------
//--------------------------------------------------------------------
//
// Internal Properties
//
//---------------------------------------------------------------------
#region Internal Properties
public override Visual Visual
{
get
{
if (!_layedOut)
{
_layedOut = true;
UIElement e;
if ((e = ((object)base.Visual) as UIElement) != null)
{
e.Measure(base.Size);
e.Arrange(new Rect(base.Size));
}
}
return base.Visual;
}
}
internal ContentPosition ContentPosition
{
get
{
ITextPointer childPosition = _documentPaginator.GetPagePosition(_documentPage) as ITextPointer;
if (childPosition != null)
{
ChildDocumentBlock childBlock = new ChildDocumentBlock(_fixedDocumentSequence.TextContainer,
ChildDocumentReference);
return new DocumentSequenceTextPointer(childBlock, childPosition);
}
return null;
}
}
internal DocumentReference ChildDocumentReference
{
get
{
foreach (DocumentReference docRef in _fixedDocumentSequence.References)
{
if (docRef.CurrentlyLoadedDoc == _documentPaginator.Source)
{
return docRef;
}
}
Debug.Assert(false);
return null;
}
}
internal DocumentPage ChildDocumentPage
{
get { return _documentPage; }
}
internal FixedDocumentSequence FixedDocumentSequence
{
get { return this._fixedDocumentSequence; }
}
#endregion Internal Properties
//--------------------------------------------------------------------
//
// Internal Event
//
//---------------------------------------------------------------------
//--------------------------------------------------------------------
//
// Private Properties
//
//---------------------------------------------------------------------
#if DEBUG
private int _DocumentIndex
{
get
{
int docIndex = 0;
foreach(DocumentReference docRef in _fixedDocumentSequence.References)
{
docIndex++;
if (docRef.CurrentlyLoadedDoc == _documentPaginator.Source)
{
break;
}
}
return docIndex;
}
}
#endif
//--------------------------------------------------------------------
//
// Private Fields
//
//---------------------------------------------------------------------
#region Private Fields
private readonly FixedDocumentSequence _fixedDocumentSequence;
private readonly DynamicDocumentPaginator _documentPaginator;
private readonly DocumentPage _documentPage;
private bool _layedOut;
// Text OM
private DocumentSequenceTextView _textView;
#endregion Private Fields
}
}
|