/// <summary> /// Invokes GetPageNumberAsync on the passed in ContentPosition. /// The handler for GetPageNumberAsync will bring that ContentPosition into view. /// </summary> /// <param name="data">The MakeVisibleData to be made visible</param> private void MakeContentPositionVisibleAsync(MakeVisibleData data) { //If the ContentPosition is valid, we can make it visible now. if (data.ContentPosition != null && data.ContentPosition != ContentPosition.Missing) { Content.GetPageNumberAsync(data.ContentPosition, data); } }
/// <summary> /// Delegate method used to bring a specified page into view. /// </summary> /// <param name="data"></param> /// <param name="pageNumber"></param> private void BringPageIntoViewDelegate(MakeVisibleData data, int pageNumber) { //Make the page visible if necessary: // - If our layout is not yet valid // - If the visual being made visible is a FixedPage and // the bring-into-view rect is the entire page // (in which case we always want to move it as close to the top of the // viewport as possible even if it is already partially visible) // - If the page isn't currently visible. if (!_rowCache.HasValidLayout || (data.Visual is FixedPage && data.Visual.VisualContentBounds == data.Rect) || pageNumber < _firstVisiblePageNumber || pageNumber > _lastVisiblePageNumber) { MakePageVisible(pageNumber); } //The page's contents have already been loaded, we can bring the object into view immediately. if (IsPageLoaded(pageNumber)) { MakeVisibleImpl(data); } else { //Now we have to wait for the page to be loaded so that we can //ensure that the object itself is visible. //As pages are loaded, this page will be checked for, and the dispatcher below //executed as appropriate. _makeVisiblePageNeeded = pageNumber; _makeVisibleDispatcher = Dispatcher.BeginInvoke(DispatcherPriority.Inactive, (DispatcherOperationCallback)delegate(object arg) { MakeVisibleImpl((MakeVisibleData)arg); return null; }, data); } }
/// <summary> /// Implementation of MakeVisible logic, the final step in a MakeVisible operation. /// </summary> /// <param name="data"></param> private void MakeVisibleImpl(MakeVisibleData data) { if (data.Visual != null) { //Ensure that the passed-in visual is a descendant of DocumentGrid. if (((Visual)this).IsAncestorOf(data.Visual)) { //Now we can determine where this visual is relative to the upper left //corner of the DocumentGrid and thus make it visible. GeneralTransform transform = data.Visual.TransformToAncestor(this); Rect boundingRect = (data.Rect != Rect.Empty) ? data.Rect : data.Visual.VisualContentBounds; Rect offsetRect = transform.TransformBounds(boundingRect); MakeRectVisible(offsetRect, false /* alwaysCenter */); } } else if (data.ContentPosition != null) { ITextPointer tp = data.ContentPosition as ITextPointer; //If we have a valid TextView and the TextPointer is in that TextView //we can make the TextPointer's Rect visible... if (TextViewContains(tp)) { MakeRectVisible(TextView.GetRectangleFromTextPosition(tp), false /* alwaysCenter */); } } else { Invariant.Assert(false, "Invalid object brought into view."); } }
/// <summary> /// Makes the specified object on the specified page visible, which may be an /// asynchronous operation if the page is not already in view. /// </summary> /// <param name="data">Data corresponding to the object to be made visible.</param> /// <param name="pageNumber">The page number the object is on.</param> private void MakeVisibleAsync(MakeVisibleData data, int pageNumber) { //This page may not be currently visible. //First we need to make the page visible, if necessary. //This will be done at background priority to allow currently-loading pages time to //finish. If we do not do this, then in the corner case of: // 1) Document has just been loaded (for example, just after a hyperlink navigation to the doc) // 2) Document has non 8.5x11-sized pages at the beginning of the document // 3) Navigation is to a page past the initially visible pages. //In this case, the order of operations is something like this: // 1) Initially visible pages start loading // 2) MakeVisible is invoked, and MakePageVisible is called, which uses cached // page info to decide where to scroll to. (8.5x11 is assumed until a page is loaded) // 3) Document is scrolled to position computed in #2 // 4) Pages loading in #1 finish loading, GetPageCompleted is called, PageCache is updated, // and the document layout changes. This will shift the page we actually want to see up or down, // potentially by a substantial amount. // 5) User is now looking at the wrong page, and if the page that the hyperlink target is on isn't // visible at this point then the MakeVisible operation fails in MakeVisibleImpl since the target // isn't in the visual tree. // Everyone got that? So, doing the initial "MakePageVisible" operation at Background priority // allows step #4 above to finish _before_ we do steps 2 and 3, so the right page will be visible // in 5 and the MakeVisible operation will succeed. Dispatcher.BeginInvoke(DispatcherPriority.Background, new BringPageIntoViewCallback( BringPageIntoViewDelegate ), data, pageNumber); }