Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / System / Windows / Controls / Primitives / DocumentViewerBase.cs / 1 / DocumentViewerBase.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: DocumentViewerBase.cs // // Description: DocumentViewerBase is a minimal base class, providing only // the functionality common across document viewing scenarios. // The base class provides no user interface, very few properties, // and minimal policy. Functionality included in the base class: // BringIntoView support & Print API services // and Annotation support. // //--------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // avoid generating warnings about unknown // message numbers and unknown pragmas for PRESharp contol using System.Collections; // IEnumerator using System.Collections.Generic; // Listusing System.Collections.ObjectModel; // ReadOnlyCollection using System.Security.Permissions; // SecurityPermission using System.Windows.Annotations; // AnnotationService using System.Windows.Automation; // AutomationPattern using System.Windows.Automation.Peers; // AutomationPeer using System.Windows.Documents; // IDocumentPaginatorSource, ... using System.Windows.Documents.Serialization; // WritingCompletedEventArgs using System.Windows.Input; // UICommand using System.Windows.Media; // Visual using System.Windows.Markup; // IAddChild, XamlSerializerUtil, ContentPropertyAttribute using System.Windows.Threading; // DispatcherPriority using System.Printing; // PrintQueue using System.Windows.Xps; // XpsDocumentWriter using MS.Internal; // Invariant, DoubleUtil using MS.Internal.KnownBoxes; // BooleanBoxes using MS.Internal.Annotations; // TextAnchor using MS.Internal.Annotations.Anchoring;// DataIdProcessor, FixedPageProcessor, ... using MS.Internal.Automation; // TextAdaptor using MS.Internal.Documents; // MultiPageTextView using MS.Internal.Controls; // EmptyEnumerator using MS.Internal.Commands; // CommandHelpers namespace System.Windows.Controls.Primitives { /// /// DocumentViewerBase is a minimal base class, providing only the functionality /// common across document viewing scenarios. The base class provides no user /// interface, very few properties, and minimal policy. Functionality included in /// the base class: BringIntoView support and Print API services /// and Annotation support. /// [ContentProperty("Document")] public abstract class DocumentViewerBase : Control, IAddChild, IServiceProvider { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Initializes class-wide settings. /// static DocumentViewerBase() { // Create our CommandBindings CreateCommandBindings(); // Register event handlers EventManager.RegisterClassHandler(typeof(DocumentViewerBase), RequestBringIntoViewEvent, new RequestBringIntoViewEventHandler(HandleRequestBringIntoView)); // Default value for AutoWordSelection is false. We want true. TextBoxBase.AutoWordSelectionProperty.OverrideMetadata(typeof(DocumentViewerBase), new FrameworkPropertyMetadata(true)); } ////// Instantiates a new instance of a DocumentViewerBase. /// protected DocumentViewerBase() : base() { _pageViews = new ReadOnlyCollection(new List ()); // By default text selection is enabled. SetFlags(true, Flags.IsSelectionEnabled); } #endregion Constructors //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods /// /// Called when the Template's tree has been generated /// ///Whether Visuals were added to the tree. public override void OnApplyTemplate() { base.OnApplyTemplate(); // Update collection of DocumentPageViews if the Visual tree has // been updated through control template UpdatePageViews(); } ////// Moves the master page to the previous page. /// public void PreviousPage() { OnPreviousPageCommand(); } ////// Moves the master page to the next page. /// public void NextPage() { OnNextPageCommand(); } ////// Moves the master page to the first page. /// public void FirstPage() { OnFirstPageCommand(); } ////// Moves the master page to the last page. /// public void LastPage() { OnLastPageCommand(); } ////// Moves the master page to the specified page. /// /// public void GoToPage(int pageNumber) { OnGoToPageCommand(pageNumber); } ////// Invokes the Print Dialog. This is analogous to the ApplicationCommands.Print. /// public void Print() { OnPrintCommand(); } ////// Cancels current printing job. This is analogous to the ApplicationCommands.CancelPrint. /// public void CancelPrint() { OnCancelPrintCommand(); } ////// Whether the master page can be moved to the specified page. /// /// Page number. public virtual bool CanGoToPage(int pageNumber) { // Can navigate to a page, if: // a) the number is in valid range between 1 and PageCount, or // b) Paginator is set and pageNumber is PageCount+1. return (pageNumber > 0 && pageNumber <= this.PageCount) || ((_document != null) && (pageNumber - 1 == this.PageCount) && !_document.DocumentPaginator.IsPageCountValid); } #endregion Public Methods //-------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------- #region Public Properties ////// The IDocumentPaginatorSource to be paginated and viewed. /// public IDocumentPaginatorSource Document { get { return _document; } set { SetValue(DocumentProperty, value); } } ////// The number of pages currently available for viewing. This value /// is updated as content is paginated, and will change dramatically /// when the content is resized, or edited. /// public int PageCount { get { return (int) GetValue(PageCountProperty); } } ////// The one-based page number of the page being displayed on the master /// page DocumentPageView. If there is no content, this value will be 0. /// public virtual int MasterPageNumber { get { return (int) GetValue(MasterPageNumberProperty); } } ////// Whether the viewer can move the master page to the previous page. /// public virtual bool CanGoToPreviousPage { get { return (bool) GetValue(CanGoToPreviousPageProperty); } } ////// Whether the viewer can advance the master page to the next page. /// public virtual bool CanGoToNextPage { get { return (bool) GetValue(CanGoToNextPageProperty); } } ////// A read-only collection of the DocumentPageView objects contained /// within the viewer. These objects are manipulated by the viewer /// in order to display content. /// [CLSCompliant(false)] public ReadOnlyCollectionPageViews { get { return _pageViews; } } #region Public Dynamic Properties /// \ /// public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register( "Document", typeof(IDocumentPaginatorSource), typeof(DocumentViewerBase), new FrameworkPropertyMetadata( null, new PropertyChangedCallback(DocumentChanged))); ////// /// protected static readonly DependencyPropertyKey PageCountPropertyKey = DependencyProperty.RegisterReadOnly( "PageCount", typeof(int), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(0)); ////// /// public static readonly DependencyProperty PageCountProperty = PageCountPropertyKey.DependencyProperty; ////// /// protected static readonly DependencyPropertyKey MasterPageNumberPropertyKey = DependencyProperty.RegisterReadOnly( "MasterPageNumber", typeof(int), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(0)); ////// /// public static readonly DependencyProperty MasterPageNumberProperty = MasterPageNumberPropertyKey.DependencyProperty; ////// /// protected static readonly DependencyPropertyKey CanGoToPreviousPagePropertyKey = DependencyProperty.RegisterReadOnly( "CanGoToPreviousPage", typeof(bool), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// /// public static readonly DependencyProperty CanGoToPreviousPageProperty = CanGoToPreviousPagePropertyKey.DependencyProperty; ////// /// protected static readonly DependencyPropertyKey CanGoToNextPagePropertyKey = DependencyProperty.RegisterReadOnly( "CanGoToNextPage", typeof(bool), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// /// public static readonly DependencyProperty CanGoToNextPageProperty = CanGoToNextPagePropertyKey.DependencyProperty; ////// /// Attached property used to signify which of the child DocumentPageView objects /// is the master page. If more than one DocumentPageView has this value set, /// then the first tagged DocumentPageView in the tree (depth-first) is designated /// the master. If none of the children have this property set, then the depth-first /// PageView is designated the master. /// public static readonly DependencyProperty IsMasterPageProperty = DependencyProperty.RegisterAttached( "IsMasterPage", typeof(bool), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// DependencyProperty getter for /// The element from which to read the attached property. public static bool GetIsMasterPage(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(IsMasterPageProperty); } ///property. /// /// DependencyProperty setter for /// The element to which to write the attached property. /// The property value to set public static void SetIsMasterPage(DependencyObject element, bool value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsMasterPageProperty, value); } #endregion Public Dynamic Properties #endregion Public Properties //------------------------------------------------------------------- // // Public Events // //-------------------------------------------------------------------- #region Public Events ///property. /// /// Fired when collection of DocumentPageViews is changed. /// public event EventHandler PageViewsChanged; #endregion Public Events //------------------------------------------------------------------- // // Protected Methods // //------------------------------------------------------------------- #region Protected Methods ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new DocumentViewerBaseAutomationPeer(this); } ///) /// /// Invalidates the PageViews collection, triggering a call to GetPageViews. /// protected void InvalidatePageViews() { // Update collection of DocumentPageViews if the collection has // been explicitly invalidated. UpdatePageViews(); InvalidateMeasure(); } ////// Returns the master DocumentPageView. /// protected DocumentPageView GetMasterPageView() { int index; DocumentPageView masterPageView = null; // Search for the first element with IsMasterPage property set. for (index = 0; index < _pageViews.Count; index++) { if (GetIsMasterPage(_pageViews[index])) { masterPageView = _pageViews[index]; break; } } // If none of the DocumentPageViews have this property set, // then use the first one in the collection. if (masterPageView == null) { masterPageView = _pageViews.Count > 0 ? _pageViews[0] : null; } return masterPageView; } ////// Creates a collection of DocumentPageView objects used to display Document. /// /// True if the collection is different than the public PageViews collection. ///Collection of DocumentPageView objects used to display Document. protected virtual ReadOnlyCollectionGetPageViewsCollection(out bool changed) { List pageViewList; AdornerDecorator adornerDecorator; // By default retrieve all DocumentPageViews from style. pageViewList = new List (1/* simplest case has just one element */); FindDocumentPageViews(this, pageViewList); // By default use AdornerDecorator.Child as RenderScope for TextEditor. Retieve // AdornerDecorator from the style. adornerDecorator = FindAdornerDecorator(this); this.TextEditorRenderScope = (adornerDecorator != null) ? (adornerDecorator.Child as FrameworkElement) : null; // Since existing DocumentPageViews are being replaced, need to disconnect // them from the Document. for (int index = 0; index < _pageViews.Count; index++) { _pageViews[index].DocumentPaginator = null; } changed = true; return new ReadOnlyCollection (pageViewList); } /// /// Called when the PageViews collection is modified; this occurs when GetPageViews /// returns True or if the control's template is modified. /// protected virtual void OnPageViewsChanged() { // Raise notification about change to DocumentPageView collection. if (this.PageViewsChanged != null) { this.PageViewsChanged(this, EventArgs.Empty); } // Change of DocumentPageView collection may cause invalidation of content // represented by DocumentPageViews. OnMasterPageNumberChanged(); } ////// Called when the MasterPageNumber property is changed, this occurs when the /// developer manually sets the property, or when the FirstPage, NextPage, /// etc commands are executed. /// protected virtual void OnMasterPageNumberChanged() { // Invalidation of MasterPage invalidates following properties: // - MasterPageNumber // - CanGoToPreviousPage // - CanGoToNextPage UpdateReadOnlyProperties(true, true); } ////// Called when a BringIntoView event is bubbled up from the Document. /// Base implementation will move the master page to the page /// on which the element occurs. /// /// The object to make visible. /// The rectangular region in the object's coordinate space which should be made visible. /// protected virtual void OnBringIntoView(DependencyObject element, Rect rect, int pageNumber) { if (element == null) { throw new ArgumentNullException("element"); } OnGoToPageCommand(pageNumber); } ////// Handler for the PreviousPage command. /// protected virtual void OnPreviousPageCommand() { // If can go to the previous page, shift all pages // by decrementing page number by 1. if (this.CanGoToPreviousPage) { ShiftPagesByOffset(-1); } } ////// Handler for the NextPage command. /// protected virtual void OnNextPageCommand() { // If can go to the next page, shift all pages // by incrementing page number by 1. if (this.CanGoToNextPage) { ShiftPagesByOffset(1); } } ////// Handler for the FirstPage command. /// protected virtual void OnFirstPageCommand() { // Navigate the master page to the first one and shift // all remaining pages by delta. ShiftPagesByOffset(1 - this.MasterPageNumber); } ////// Handler for the LastPage command. /// protected virtual void OnLastPageCommand() { // Navigate the master page to the last one and shift // all remaining pages by delta. ShiftPagesByOffset(this.PageCount - this.MasterPageNumber); } ////// Handler for the GoToPage command. /// /// protected virtual void OnGoToPageCommand(int pageNumber) { // Check if can go to the specified page. // Navigate the master page to specified page number and shift // all remaining pages by delta. if (CanGoToPage(pageNumber)) { ShiftPagesByOffset(pageNumber - this.MasterPageNumber); } } ////// Handler for the Print command. /// protected virtual void OnPrintCommand() { #if !DONOTREFPRINTINGASMMETA XpsDocumentWriter docWriter; PrintDocumentImageableArea ia = null; // Only one printing job is allowed. if (_documentWriter != null) { return; } if (_document != null) { // Show print dialog. docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia); if (docWriter != null && ia != null) { // Register for WritingCompleted event. _documentWriter = docWriter; _documentWriter.WritingCompleted += new WritingCompletedEventHandler(HandlePrintCompleted); _documentWriter.WritingCancelled += new WritingCancelledEventHandler(HandlePrintCancelled); // Since _documentWriter value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); // Write to the PrintQueue if( _document is FixedDocumentSequence ) { docWriter.WriteAsync(_document as FixedDocumentSequence); } else if( _document is FixedDocument ) { docWriter.WriteAsync(_document as FixedDocument); } else { docWriter.WriteAsync(_document.DocumentPaginator); } } } #endif // DONOTREFPRINTINGASMMETA } ////// Handler for the CancelPrint command. /// protected virtual void OnCancelPrintCommand() { #if !DONOTREFPRINTINGASMMETA if (_documentWriter != null) { _documentWriter.CancelAsync(); } #endif // DONOTREFPRINTINGASMMETA } ////// Called when the Document property is changed. /// protected virtual void OnDocumentChanged() { int index; // Document has been changed. Update existing DocumentPageViews to point them to the new Document. for (index = 0; index < _pageViews.Count; index++) { _pageViews[index].DocumentPaginator = (_document != null) ? _document.DocumentPaginator : null; } // Document invalidation invalidates following properties: // - PageCount // - MasterPageNumber // - CanGoToPreviousPage // - CanGoToNextPage UpdateReadOnlyProperties(true, true); // Attach TextEditor, if content supports it. This method will also // detach TextEditor from old content. AttachTextEditor(); } #endregion Protected Methods //------------------------------------------------------------------- // // Protected Properties // //-------------------------------------------------------------------- #region Protected Properties ////// Returns enumerator to logical children. /// protected internal override IEnumerator LogicalChildren { get { if (this.HasLogicalChildren && _document != null) { return new SingleChildEnumerator(_document); } return EmptyEnumerator.Instance; } } #endregion Protected Properties //------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------- #region Internal Methods ////// Determines whether DocumentPageView is a master page. /// /// Instance of DocumentPageView. ///Whether given instance of DocumentPageView is a master page. internal bool IsMasterPageView(DocumentPageView pageView) { Invariant.Assert(pageView != null); return (pageView == GetMasterPageView()); } ////// Invoked when the "Find" button in the Find Toolbar is clicked. /// This method invokes the actual Find process. /// internal ITextRange Find(FindToolBar findToolBar) { ITextView masterPageTextView = null; DocumentPageView masterPage = GetMasterPageView(); if (masterPage != null && masterPage is IServiceProvider) { masterPageTextView = ((IServiceProvider)masterPage).GetService(typeof(ITextView)) as ITextView; } return DocumentViewerHelper.Find(findToolBar, _textEditor, _textView, masterPageTextView); } #endregion Internal Methods //-------------------------------------------------------------------- // // Internal Properties // //------------------------------------------------------------------- #region Internal Properties ////// Whether text selection is enabled or disabled. /// internal bool IsSelectionEnabled { get { return CheckFlags(Flags.IsSelectionEnabled); } set { SetFlags(value, Flags.IsSelectionEnabled); AttachTextEditor(); } } ////// TextEditor instance. /// internal TextEditor TextEditor { get { return _textEditor; } } ////// Allows overriding the RenderScope used by the TextEditor. /// internal FrameworkElement TextEditorRenderScope { get { return _textEditorRenderScope; } set { _textEditorRenderScope = value; AttachTextEditor(); } } #endregion Internal Properties //-------------------------------------------------------------------- // // Private Methods // //------------------------------------------------------------------- #region Private Methods ////// Retrieves an ITextPointer from the MasterPage. /// If startOfPage is true, then we will retreive the first /// TextPointer in the first TextSegment in the DocumentPageTextView. /// Else, we will retreive the last TextPointer in the last TextSegment /// of the MasterPage DocumentPageTextView. /// /// ///private ITextPointer GetMasterPageTextPointer(bool startOfPage) { ITextPointer masterPointer = null; ITextView textView = null; DocumentPageView masterPage = GetMasterPageView(); if (masterPage != null && masterPage is IServiceProvider) { textView = ((IServiceProvider)masterPage).GetService(typeof(ITextView)) as ITextView; if (textView != null && textView.IsValid) { // Find the very first/(last) text pointer in this textView. foreach (TextSegment textSegment in textView.TextSegments) { if (textSegment.IsNull) { continue; } if (masterPointer == null) { // Set initial masterPointer value. masterPointer = startOfPage ? textSegment.Start : textSegment.End; } else { if (startOfPage) { if (textSegment.Start.CompareTo(masterPointer) < 0) { // Start is before the current masterPointer masterPointer = textSegment.Start; } } else { // end is after than the current masterPointer if (textSegment.End.CompareTo(masterPointer) > 0) { masterPointer = textSegment.End; } } } } } } return masterPointer; } /// /// Update collection of DocumentPageViews in responce to Visual tree changes. /// ///Whether collection of DocumentPageViews has been updated. private void UpdatePageViews() { int index; bool changed; ReadOnlyCollectionpageViews; // Get collection of new DocumentPageViews. pageViews = GetPageViewsCollection(out changed); // If DocumentPageViews collection has not been changed, there is nothing to update. if (changed) { // Verify collection of DocumentPageViews. It needs to meet following conditions: // a) at least one DocumentPageView, // b) only one page has IsMasterPage property set to true, // c) unique PageNumbers for each active DocumentPageView, VerifyDocumentPageViews(pageViews); // New collection of DocumentPageViews is replacing the old one. Point new // DocumentPageViews to the Document. _pageViews = pageViews; for (index = 0; index < _pageViews.Count; index++) { _pageViews[index].DocumentPaginator = (_document != null) ? _document.DocumentPaginator : null; } // Collection of DocumentPageView has been changed. Need to update // TextView, if one already exists. if (_textView != null) { _textView.OnPagesUpdated(); } // DocumentPageViews collection has been changed. Notify all listeners // and/or derived classes about this fact. OnPageViewsChanged(); } } /// /// Verify collection of DocumentPageViews. It needs to meet following conditions: /// a) collection is not null, /// b) only one page has IsMasterPage property set to true, /// c) unique PageNumbers for each active DocumentPageView, /// /// Collection of DocumentPageViews to validate. private void VerifyDocumentPageViews(ReadOnlyCollectionpageViews) { int index; bool hasMasterPage = false; // At least one DocumentPageView is required. if (pageViews == null) { throw new ArgumentException(SR.Get(SRID.DocumentViewerPageViewsCollectionEmpty)); } // Expecting only one DocumentPageView with IsMasterPage property set to true. for (index = 0; index < pageViews.Count; index++) { if (GetIsMasterPage(pageViews[index])) { if (hasMasterPage) { throw new ArgumentException(SR.Get(SRID.DocumentViewerOneMasterPage)); } hasMasterPage = true; } } // Unique PageNumbers for each active DocumentPageView. } /// /// Does deep Visual tree walk to retrieve all DocumentPageViews. /// It stops recursing down into visual tree in following situations: /// a) Visual is UIElement and it is not part of Contol Template, /// b) Visual is DocumentPageView. /// /// FrameworkElement that is part of Control Template. /// Collection of DocumentPageViews; found elements are appended here. ///Whether collection of DocumentPageViews has been updated. private void FindDocumentPageViews(Visual root, ListpageViews) { Invariant.Assert(root != null); Invariant.Assert(pageViews != null); FrameworkElement fe; // Do deep tree walk to retrieve all DocumentPageViews. // It stops recursing down into visual tree in following situations: // a) Visual is UIElement and it is not part of Contol Template, // b) Visual is DocumentPageView. // Add to collection any DocumentPageViews found in the Control Template. int count = root.InternalVisualChildrenCount; for(int i = 0; i < count; i++) { Visual child = root.InternalGetVisualChild(i); fe = child as FrameworkElement; if (fe != null) { if (fe.TemplatedParent != null) { if (fe is DocumentPageView) { pageViews.Add(fe as DocumentPageView); } else { FindDocumentPageViews(fe, pageViews); } } } else { FindDocumentPageViews(child, pageViews); } } } /// /// Does deep Visual tree walk to retrieve an AdornerDecorator. Because /// AdornerDecorator is supposed to cover all DocumentPageViews, it stops /// recursing down into visual tree in following situations: /// a) Visual is UIElement and it is not part of Contol Template, /// b) Visual is DocumentPageView. /// c) Visual is AdornerDecorator. /// /// FrameworkElement that is part of Control Template. ///AdornerDecorator, if found. private AdornerDecorator FindAdornerDecorator(Visual root) { Invariant.Assert(root != null); FrameworkElement fe; AdornerDecorator adornerDecorator = null; // Do deep Visual tree walk to retrieve an AdornerDecorator. Because // AdornerDecorator is supposed to cover all DocumentPageViews, it stops // recursing down into visual tree in following situations: // a) Visual is UIElement and it is not part of Contol Template, // b) Visual is DocumentPageView. // c) Visual is AdornerDecorator. int count = root.InternalVisualChildrenCount; for(int i = 0; i < count; i++) { Visual child = root.InternalGetVisualChild(i); fe = child as FrameworkElement; if (fe != null) { if (fe.TemplatedParent != null) { if (fe is AdornerDecorator) { adornerDecorator = (AdornerDecorator)fe; } else if (!(fe is DocumentPageView)) { adornerDecorator = FindAdornerDecorator(fe); } // else stop on DocumentPageView } } else { adornerDecorator = FindAdornerDecorator(child); } if (adornerDecorator != null) { break; } } return adornerDecorator; } ////// Attach TextEditor to Document, if supports text. /// private void AttachTextEditor() { AnnotationService service = AnnotationService.GetService(this); ITextContainer textContainer; // This method is called when Document is changing, so need // to clear old TextEditor data. if (_textEditor != null) { _textEditor.OnDetach(); _textEditor = null; if (_textView.TextContainer.TextView == _textView) { _textView.TextContainer.TextView = null; } _textView = null; } if (service != null) { // Must be enabled - otherwise it won't be on the tree service.Disable(); } // If new Document supports TextEditor, create one. // If the Document is already attached to TextEditor (TextSelection != null), // do not create TextEditor for this instance of the viewer. (This situation may happen // when the same instance of Document is attached to more than one viewer). textContainer = this.TextContainer; if (textContainer != null && this.TextEditorRenderScope != null && textContainer.TextSelection == null) { _textView = new MultiPageTextView(this, this.TextEditorRenderScope, textContainer); _textEditor = new TextEditor(textContainer, this, false); _textEditor.IsReadOnly = !IsEditingEnabled; _textEditor.TextView = _textView; textContainer.TextView = _textView; } // Re-enable the service in order to register on the new TextView if (service != null) { service.Enable(service.Store); } } ////// Called when WritingCompleted event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCompleted(object sender, WritingCompletedEventArgs e) { CleanUpPrintOperation(); } ////// Called when WritingCancelled event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCancelled(object sender, WritingCancelledEventArgs e) { CleanUpPrintOperation(); } ////// Handler for PaginationCompleted event raised by the Document. /// private void HandlePaginationCompleted(object sender, EventArgs e) { // PaginationCompleted may invalidate following properties: // - PageCount // - CanGoToNextPage UpdateReadOnlyProperties(true, false); } ////// Handler for PaginationProgress event raised by the Document. /// private void HandlePaginationProgress(object sender, EventArgs e) { // PaginationProgress may invalidate following properties: // - PageCount // - CanGoToNextPage UpdateReadOnlyProperties(true, false); } ////// Handler for GetPageNumberCompleted event raised by the Document. /// private void HandleGetPageNumberCompleted(object sender, GetPageNumberCompletedEventArgs e) { BringIntoViewState bringIntoViewState; // At this point the Document's page count might have changed, // so update properties accordingly. UpdateReadOnlyProperties(true, false); if (_document != null && sender == _document.DocumentPaginator && e != null) { if (!e.Cancelled && (e.Error == null)) { bringIntoViewState = e.UserState as BringIntoViewState; if (bringIntoViewState != null && bringIntoViewState.Source == this) { OnBringIntoView(bringIntoViewState.TargetObject, bringIntoViewState.TargetRect, e.PageNumber + 1); } } } } ////// Makes sure the target is visible in the client area. May cause navigation /// to a different page. /// /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private void HandleRequestBringIntoView(RequestBringIntoViewEventArgs args) { DependencyObject child; DependencyObject parent; ContentPosition contentPosition; BringIntoViewState bringIntoViewState; DynamicDocumentPaginator documentPaginator; Rect targetRect = Rect.Empty; if (args != null && args.TargetObject != null && _document is DependencyObject) { // If the passed in object is a logical child of DocumentViewer's Document, // attempt to make it visible now. // Special case: TargetObject is the document itself. Then scroll to the top (page 1). // This supports navigating from baseURI#anchor to just baseURI. parent = _document as DependencyObject; if (args.TargetObject == _document) { OnGoToPageCommand(1); args.Handled = true; // Mark the event as handled. } else { // Verify if TargetObject is in fact a child of Document. child = args.TargetObject; while (child != null && child != parent) { // Skip elements in the control's template (if such exists) and // walk up logical tree to find if the focused element is within // the document. FrameworkElement fe = child as FrameworkElement; if (fe != null && fe.TemplatedParent != null) { child = fe.TemplatedParent; } else { child = LogicalTreeHelper.GetParent(child); } } if (child != null) { // Special case UIElements already connected to visual tree. if (args.TargetObject is UIElement) { UIElement targetObject = (UIElement)args.TargetObject; if (VisualTreeHelper.IsAncestorOf(this, targetObject)) { targetRect = args.TargetRect; if (targetRect.IsEmpty) { targetRect = new Rect(targetObject.RenderSize); } GeneralTransform transform = targetObject.TransformToAncestor(this); targetRect = transform.TransformBounds(targetRect); targetRect.IntersectsWith(new Rect(this.RenderSize)); } } // If target is not already visible, bring appropriate page into view. if (targetRect.IsEmpty) { // Get content position for given target. documentPaginator = _document.DocumentPaginator as DynamicDocumentPaginator; if (documentPaginator != null) { contentPosition = documentPaginator.GetObjectPosition(args.TargetObject); if (contentPosition != null && contentPosition != ContentPosition.Missing) { // Asynchronously retrieve PageNumber for given ContentPosition. bringIntoViewState = new BringIntoViewState(this, contentPosition, args.TargetObject, args.TargetRect); documentPaginator.GetPageNumberAsync(contentPosition, bringIntoViewState); } } } args.Handled = true; // Mark the event as handled. } } if (args.Handled) { // Create new BringIntoView request for this element, so // if there is an ancestor handling BringIntoView, it can // react appropriately and bring this element into view. if (targetRect.IsEmpty) { BringIntoView(); } else { BringIntoView(targetRect); } } } } ////// Update values for readonly properties. /// /// Whether PageCount has been changed. /// Whether MasterPageNumber has been changed. private void UpdateReadOnlyProperties(bool pageCountChanged, bool masterPageChanged) { if (pageCountChanged) { SetValue(PageCountPropertyKey, (_document != null) ? _document.DocumentPaginator.PageCount : 0); } bool invalidateRequery = false; if (masterPageChanged) { int masterPageNumber = 0; DocumentPageView masterPageView; if (_document != null && _pageViews.Count > 0) { masterPageView = GetMasterPageView(); if (masterPageView != null) { masterPageNumber = masterPageView.PageNumber + 1; } } SetValue(MasterPageNumberPropertyKey, masterPageNumber); SetValue(CanGoToPreviousPagePropertyKey, MasterPageNumber > 1); invalidateRequery = true; } if (pageCountChanged || masterPageChanged) { bool canGoToNextPage = false; if (_document != null) { canGoToNextPage = (MasterPageNumber < _document.DocumentPaginator.PageCount) || !_document.DocumentPaginator.IsPageCountValid; } SetValue(CanGoToNextPagePropertyKey, canGoToNextPage); invalidateRequery = true; } if(invalidateRequery) { CommandManager.InvalidateRequerySuggested(); } } ////// Shift all pages by specified offset. /// private void ShiftPagesByOffset(int offset) { int index; if (offset != 0) { for (index = 0; index < _pageViews.Count; index++) { _pageViews[index].PageNumber += offset; } // Change of PageNumber property on DocumentPageViews will cause // invalidation of content represented by DocumentPageViews. OnMasterPageNumberChanged(); } } ////// Sets or unsets one or multiple flags. /// /// Whether flag or flags are set or cleared. /// Combination of flags to change. private void SetFlags(bool value, Flags flags) { _flags = value ? (_flags | flags) : (_flags & (~flags)); } ////// Returns true if all of passed flags in the bitmask are set. /// /// Combination of flags to get the value. ///Returns true if all of passed flags in the bitmask are set. private bool CheckFlags(Flags flags) { return ((_flags & flags) == flags); } ////// The Document has changed and needs to be updated. /// private void DocumentChanged(IDocumentPaginatorSource oldDocument, IDocumentPaginatorSource newDocument) { DependencyObject doDocument; DynamicDocumentPaginator dynamicDocumentPaginator; _document = newDocument; // Cleanup state associated with the old document. if (oldDocument != null) { // If Document was added to logical tree of DocumentViewer before, remove it. if (CheckFlags(Flags.DocumentAsLogicalChild)) { RemoveLogicalChild(oldDocument); } // Unregister from PaginationProgress and PaginationCompleted events. dynamicDocumentPaginator = oldDocument.DocumentPaginator as DynamicDocumentPaginator; if (dynamicDocumentPaginator != null) { dynamicDocumentPaginator.PaginationProgress -= new PaginationProgressEventHandler(HandlePaginationProgress); dynamicDocumentPaginator.PaginationCompleted -= new EventHandler(HandlePaginationCompleted); dynamicDocumentPaginator.GetPageNumberCompleted -= new GetPageNumberCompletedEventHandler(HandleGetPageNumberCompleted); } DependencyObject depObj = oldDocument as DependencyObject; if (depObj != null) { depObj.ClearValue(PathNode.HiddenParentProperty); } } // If DocumentViewer was created through style, then do not modify // the logical tree. Instead, set "core parent" for the Document. doDocument = _document as DependencyObject; if (doDocument != null && LogicalTreeHelper.GetParent(doDocument) != null && doDocument is ContentElement) { // Set the "core parent" back to us. ContentOperations.SetParent((ContentElement)doDocument, this); SetFlags(false, Flags.DocumentAsLogicalChild); } else { SetFlags(true, Flags.DocumentAsLogicalChild); } // Initialize state associated with the new document. if (_document != null) { // If Document should be part of DocumentViewer's logical tree, add it. if (CheckFlags(Flags.DocumentAsLogicalChild)) { AddLogicalChild(_document); } // Register for PaginationProgress and PaginationCompleted events. dynamicDocumentPaginator = _document.DocumentPaginator as DynamicDocumentPaginator; if (dynamicDocumentPaginator != null) { dynamicDocumentPaginator.PaginationProgress += new PaginationProgressEventHandler(HandlePaginationProgress); dynamicDocumentPaginator.PaginationCompleted += new EventHandler(HandlePaginationCompleted); dynamicDocumentPaginator.GetPageNumberCompleted += new GetPageNumberCompletedEventHandler(HandleGetPageNumberCompleted); } // Setup DPs and processors for annotation handling. If the service isn't already // enabled the processors will be registered by the service when it is enabled. FlowDocument flowDocument; DependencyObject doc = _document as DependencyObject; if (_document is FixedDocument || _document is FixedDocumentSequence) { // Clear properties that aren't needed for FixedDocument this.ClearValue(AnnotationService.DataIdProperty); // Setup service to look for FixedPages in the content AnnotationService.SetSubTreeProcessorId(this, FixedPageProcessor.Id); // Tell the content how to get to its parent DocumentViewer doc.SetValue(PathNode.HiddenParentProperty, this); // If the service is already registered, set it up for fixed content AnnotationService service = AnnotationService.GetService(this); if (service != null) { service.LocatorManager.RegisterSelectionProcessor(new FixedTextSelectionProcessor(), typeof(TextRange)); service.LocatorManager.RegisterSelectionProcessor(new FixedTextSelectionProcessor(), typeof(TextAnchor)); } } else if ((flowDocument = _document as FlowDocument) != null) { // Tell the content how to get to its parent DocumentViewer flowDocument.SetValue(PathNode.HiddenParentProperty, this); // If the service is already registered, set it up for fixed content AnnotationService service = AnnotationService.GetService(this); if (service != null) { service.LocatorManager.RegisterSelectionProcessor(new TextSelectionProcessor(), typeof(TextRange)); service.LocatorManager.RegisterSelectionProcessor(new TextSelectionProcessor(), typeof(TextAnchor)); service.LocatorManager.RegisterSelectionProcessor(new TextViewSelectionProcessor(), typeof(DocumentViewerBase)); } // Setup service to use DataID processor AnnotationService.SetDataId(this, "FlowDocument"); } else { // Clear values that were set directly on the tree - only valid for Fixed or Flow Documents this.ClearValue(AnnotationService.SubTreeProcessorIdProperty); this.ClearValue(AnnotationService.DataIdProperty); } } // Document is also represented as Automation child. Need to invalidate peer to force update. DocumentViewerBaseAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as DocumentViewerBaseAutomationPeer; if (peer != null) { peer.InvalidatePeer(); } // Respond to Document change - update state that is affected by this change. OnDocumentChanged(); } ////// Cleans up after a print operation by unregistering for events and re-enabling buttons. /// private void CleanUpPrintOperation() { #if !DONOTREFPRINTINGASMMETA if (_documentWriter != null) { _documentWriter.WritingCompleted -= new WritingCompletedEventHandler(HandlePrintCompleted); _documentWriter.WritingCancelled -= new WritingCancelledEventHandler(HandlePrintCancelled); _documentWriter = null; // Since _documentWriter value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } #endif // DONOTREFPRINTINGASMMETA } #region Commands ////// Set up Command and RoutedCommand bindings. /// private static void CreateCommandBindings() { ExecutedRoutedEventHandler executedHandler; CanExecuteRoutedEventHandler canExecuteHandler; // Create our generic ExecutedRoutedEventHandler. executedHandler = new ExecutedRoutedEventHandler(ExecutedRoutedEventHandler); // Create our generic QueryEnabledStatusHandler canExecuteHandler = new CanExecuteRoutedEventHandler(CanExecuteRoutedEventHandler); // Command: NavigationCommands.PreviousPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.PreviousPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.NextPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.NextPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.FirstPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.FirstPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.LastPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.LastPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.GoToPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.GoToPage, executedHandler, canExecuteHandler); // no key gesture // Command: ApplicationCommands.Print CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), ApplicationCommands.Print, executedHandler, canExecuteHandler, new KeyGesture(Key.P, ModifierKeys.Control)); // Command: ApplicationCommands.CancelPrint CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), ApplicationCommands.CancelPrint, executedHandler, canExecuteHandler); // no key gesture // Register editing command handlers - After our commands to let editor handle them first TextEditor.RegisterCommandHandlers(typeof(DocumentViewerBase), /*acceptsRichContent:*/true, /*readOnly:*/!IsEditingEnabled, /*registerEventListeners*/true); } ////// Central handler for CanExecuteRouted events fired by Commands directed at DocumentViewerBase. /// /// The target of this Command, expected to be DocumentViewerBase /// The event arguments for this event. private static void CanExecuteRoutedEventHandler(object target, CanExecuteRoutedEventArgs args) { DocumentViewerBase dv = target as DocumentViewerBase; Invariant.Assert(dv != null, "Target of CanExecuteRoutedEventHandler must be DocumentViewerBase."); Invariant.Assert(args != null, "args cannot be null."); // DocumentViewerBase is capable of execution of the majority of its commands. // Special rules: // a) Print command is enabled when Document is attached and printing is not in progress. // b) CancelPrint command is enabled only during printing. if (args.Command == ApplicationCommands.Print) { args.CanExecute = (dv.Document != null) && (dv._documentWriter == null); args.Handled = true; } else if (args.Command == ApplicationCommands.CancelPrint) { args.CanExecute = (dv._documentWriter != null); } else { args.CanExecute = true; } } ////// Central handler for all ExecutedRouted events fired by Commands directed at DocumentViewerBase. /// /// The target of this Command, expected to be DocumentViewerBase. /// The event arguments associated with this event. private static void ExecutedRoutedEventHandler(object target, ExecutedRoutedEventArgs args) { DocumentViewerBase dv = target as DocumentViewerBase; Invariant.Assert(dv != null, "Target of ExecuteEvent must be DocumentViewerBase."); Invariant.Assert(args != null, "args cannot be null."); // Now we execute the method corresponding to the Command that fired this event; // each Command has its own protected virtual method that performs the operation // corresponding to the Command. if (args.Command == NavigationCommands.PreviousPage) { dv.OnPreviousPageCommand(); } else if (args.Command == NavigationCommands.NextPage) { dv.OnNextPageCommand(); } else if (args.Command == NavigationCommands.FirstPage) { dv.OnFirstPageCommand(); } else if (args.Command == NavigationCommands.LastPage) { dv.OnLastPageCommand(); } else if (args.Command == NavigationCommands.GoToPage) { // Ignore GoToPageCommand, if: // a) there is no value for the page number. // b) the value cannot be converted to Int32. if (args.Parameter != null) { int pageNumber = -1; try { pageNumber = Convert.ToInt32(args.Parameter, System.Globalization.CultureInfo.CurrentCulture); } #pragma warning disable 56502 // Allow empty catch statements. catch (InvalidCastException) { } catch (OverflowException) { } catch (FormatException) { } #pragma warning restore 56502 if (pageNumber >= 0) { dv.OnGoToPageCommand(pageNumber); } } } else if (args.Command == ApplicationCommands.Print) { dv.OnPrintCommand(); } else if (args.Command == ApplicationCommands.CancelPrint) { dv.OnCancelPrintCommand(); } else { Invariant.Assert(false, "Command not handled in ExecutedRoutedEventHandler."); } } #endregion Commands #region Static Methods ////// Called from the event handler to make sure the target is visible in the client /// area. May cause navigation to a different page. /// /// The instance handling the event. /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private static void HandleRequestBringIntoView(object sender, RequestBringIntoViewEventArgs args) { if (sender != null && sender is DocumentViewerBase) { ((DocumentViewerBase)sender).HandleRequestBringIntoView(args); } } ////// The Document has changed and needs to be updated. /// private static void DocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is DocumentViewerBase); ((DocumentViewerBase) d).DocumentChanged((IDocumentPaginatorSource) e.OldValue, (IDocumentPaginatorSource) e.NewValue); // Since Document state is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } #endregion Static Methods #endregion Private Methods //------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------- #region Private Properties ////// ITextContainer associated with Document. /// private ITextContainer TextContainer { get { ITextContainer textContainer = null; if (_document != null) { if (_document is IServiceProvider && CheckFlags(Flags.IsSelectionEnabled)) { textContainer = ((IServiceProvider)_document).GetService(typeof(ITextContainer)) as ITextContainer; } } return textContainer; } } #endregion Private Properties //-------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------- #region Private Fields private ReadOnlyCollection_pageViews; // Collection of DocumentPageViews presenting paginated Document. private FrameworkElement _textEditorRenderScope; // RenderScope associated with the TextEditor. private MultiPageTextView _textView; // ITextView associated with DocumentViewer. private TextEditor _textEditor; // TextEditor associated with DocumentViewer. private IDocumentPaginatorSource _document; // IDocumentPaginatorSource representing Document. private Flags _flags; // Flags reflecting various aspects of object's state. #if !DONOTREFPRINTINGASMMETA private XpsDocumentWriter _documentWriter; // DocumentWriter used for printing. #endif // DONOTREFPRINTINGASMMETA private static bool IsEditingEnabled = false; // A flag enabling text editing within a document viewer // accessible only through reflection. #endregion Private Fields //-------------------------------------------------------------------- // // Private Types // //-------------------------------------------------------------------- #region Private Types /// /// Flags reflecting various aspects of viewer's state. /// [System.Flags] private enum Flags { // free bit = 0x10, IsSelectionEnabled = 0x20, // Is text selection enabled. DocumentAsLogicalChild = 0x40, // Is Document part of logical tree. } ////// State of BringIntoView operation. /// private class BringIntoViewState { internal BringIntoViewState(DocumentViewerBase source, ContentPosition contentPosition, DependencyObject targetObject, Rect targetRect) { this.Source = source; this.ContentPosition = contentPosition; this.TargetObject = targetObject; this.TargetRect = targetRect; } internal DocumentViewerBase Source; internal ContentPosition ContentPosition; internal DependencyObject TargetObject; internal Rect TargetRect; } #endregion Private Types //------------------------------------------------------------------- // // IAddChild // //-------------------------------------------------------------------- #region IAddChild ////// Called to add the object as a Child. /// /// Object to add as a child. ///DocumentViewerBase only supports a single child of type IDocumentPaginatorSource. void IAddChild.AddChild(Object value) { if (value == null) { throw new ArgumentNullException("value"); } // Check if Content has already been set. if (this.Document != null) { throw new InvalidOperationException(SR.Get(SRID.DocumentViewerCanHaveOnlyOneChild)); } // Only IDocumentPaginatorSource is a valid content. IDocumentPaginatorSource document = value as IDocumentPaginatorSource; if (document == null) { throw new ArgumentException(SR.Get(SRID.DocumentViewerChildMustImplementIDocumentPaginatorSource), "value"); } this.Document = document; } ////// Called when text appears under the tag in markup /// /// Text to add to the Object. ///DocumentViewer does not support Text children. void IAddChild.AddText(string text) { XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this); } #endregion IAddChild //------------------------------------------------------------------- // // IServiceProvider // //------------------------------------------------------------------- #region IServiceProvider ////// Returns service objects associated with this control. /// /// Specifies the type of service object to get. object IServiceProvider.GetService(Type serviceType) { object service = null; if (serviceType == null) { throw new ArgumentNullException("serviceType"); } // Following services are available: // (1) TextView - wrapper for TextViews exposed by PageViews. // (2) TextContainer - the service object is retrieved from Document. if (serviceType == typeof(ITextView)) { service = _textView; } else if (serviceType == typeof(TextContainer) || serviceType == typeof(ITextContainer)) { service = this.TextContainer; } return service; } #endregion IServiceProvider } } #pragma warning enable 1634, 1691 // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: DocumentViewerBase.cs // // Description: DocumentViewerBase is a minimal base class, providing only // the functionality common across document viewing scenarios. // The base class provides no user interface, very few properties, // and minimal policy. Functionality included in the base class: // BringIntoView support & Print API services // and Annotation support. // //--------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // avoid generating warnings about unknown // message numbers and unknown pragmas for PRESharp contol using System.Collections; // IEnumerator using System.Collections.Generic; // Listusing System.Collections.ObjectModel; // ReadOnlyCollection using System.Security.Permissions; // SecurityPermission using System.Windows.Annotations; // AnnotationService using System.Windows.Automation; // AutomationPattern using System.Windows.Automation.Peers; // AutomationPeer using System.Windows.Documents; // IDocumentPaginatorSource, ... using System.Windows.Documents.Serialization; // WritingCompletedEventArgs using System.Windows.Input; // UICommand using System.Windows.Media; // Visual using System.Windows.Markup; // IAddChild, XamlSerializerUtil, ContentPropertyAttribute using System.Windows.Threading; // DispatcherPriority using System.Printing; // PrintQueue using System.Windows.Xps; // XpsDocumentWriter using MS.Internal; // Invariant, DoubleUtil using MS.Internal.KnownBoxes; // BooleanBoxes using MS.Internal.Annotations; // TextAnchor using MS.Internal.Annotations.Anchoring;// DataIdProcessor, FixedPageProcessor, ... using MS.Internal.Automation; // TextAdaptor using MS.Internal.Documents; // MultiPageTextView using MS.Internal.Controls; // EmptyEnumerator using MS.Internal.Commands; // CommandHelpers namespace System.Windows.Controls.Primitives { /// /// DocumentViewerBase is a minimal base class, providing only the functionality /// common across document viewing scenarios. The base class provides no user /// interface, very few properties, and minimal policy. Functionality included in /// the base class: BringIntoView support and Print API services /// and Annotation support. /// [ContentProperty("Document")] public abstract class DocumentViewerBase : Control, IAddChild, IServiceProvider { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Initializes class-wide settings. /// static DocumentViewerBase() { // Create our CommandBindings CreateCommandBindings(); // Register event handlers EventManager.RegisterClassHandler(typeof(DocumentViewerBase), RequestBringIntoViewEvent, new RequestBringIntoViewEventHandler(HandleRequestBringIntoView)); // Default value for AutoWordSelection is false. We want true. TextBoxBase.AutoWordSelectionProperty.OverrideMetadata(typeof(DocumentViewerBase), new FrameworkPropertyMetadata(true)); } ////// Instantiates a new instance of a DocumentViewerBase. /// protected DocumentViewerBase() : base() { _pageViews = new ReadOnlyCollection(new List ()); // By default text selection is enabled. SetFlags(true, Flags.IsSelectionEnabled); } #endregion Constructors //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods /// /// Called when the Template's tree has been generated /// ///Whether Visuals were added to the tree. public override void OnApplyTemplate() { base.OnApplyTemplate(); // Update collection of DocumentPageViews if the Visual tree has // been updated through control template UpdatePageViews(); } ////// Moves the master page to the previous page. /// public void PreviousPage() { OnPreviousPageCommand(); } ////// Moves the master page to the next page. /// public void NextPage() { OnNextPageCommand(); } ////// Moves the master page to the first page. /// public void FirstPage() { OnFirstPageCommand(); } ////// Moves the master page to the last page. /// public void LastPage() { OnLastPageCommand(); } ////// Moves the master page to the specified page. /// /// public void GoToPage(int pageNumber) { OnGoToPageCommand(pageNumber); } ////// Invokes the Print Dialog. This is analogous to the ApplicationCommands.Print. /// public void Print() { OnPrintCommand(); } ////// Cancels current printing job. This is analogous to the ApplicationCommands.CancelPrint. /// public void CancelPrint() { OnCancelPrintCommand(); } ////// Whether the master page can be moved to the specified page. /// /// Page number. public virtual bool CanGoToPage(int pageNumber) { // Can navigate to a page, if: // a) the number is in valid range between 1 and PageCount, or // b) Paginator is set and pageNumber is PageCount+1. return (pageNumber > 0 && pageNumber <= this.PageCount) || ((_document != null) && (pageNumber - 1 == this.PageCount) && !_document.DocumentPaginator.IsPageCountValid); } #endregion Public Methods //-------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------- #region Public Properties ////// The IDocumentPaginatorSource to be paginated and viewed. /// public IDocumentPaginatorSource Document { get { return _document; } set { SetValue(DocumentProperty, value); } } ////// The number of pages currently available for viewing. This value /// is updated as content is paginated, and will change dramatically /// when the content is resized, or edited. /// public int PageCount { get { return (int) GetValue(PageCountProperty); } } ////// The one-based page number of the page being displayed on the master /// page DocumentPageView. If there is no content, this value will be 0. /// public virtual int MasterPageNumber { get { return (int) GetValue(MasterPageNumberProperty); } } ////// Whether the viewer can move the master page to the previous page. /// public virtual bool CanGoToPreviousPage { get { return (bool) GetValue(CanGoToPreviousPageProperty); } } ////// Whether the viewer can advance the master page to the next page. /// public virtual bool CanGoToNextPage { get { return (bool) GetValue(CanGoToNextPageProperty); } } ////// A read-only collection of the DocumentPageView objects contained /// within the viewer. These objects are manipulated by the viewer /// in order to display content. /// [CLSCompliant(false)] public ReadOnlyCollectionPageViews { get { return _pageViews; } } #region Public Dynamic Properties /// \ /// public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register( "Document", typeof(IDocumentPaginatorSource), typeof(DocumentViewerBase), new FrameworkPropertyMetadata( null, new PropertyChangedCallback(DocumentChanged))); ////// /// protected static readonly DependencyPropertyKey PageCountPropertyKey = DependencyProperty.RegisterReadOnly( "PageCount", typeof(int), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(0)); ////// /// public static readonly DependencyProperty PageCountProperty = PageCountPropertyKey.DependencyProperty; ////// /// protected static readonly DependencyPropertyKey MasterPageNumberPropertyKey = DependencyProperty.RegisterReadOnly( "MasterPageNumber", typeof(int), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(0)); ////// /// public static readonly DependencyProperty MasterPageNumberProperty = MasterPageNumberPropertyKey.DependencyProperty; ////// /// protected static readonly DependencyPropertyKey CanGoToPreviousPagePropertyKey = DependencyProperty.RegisterReadOnly( "CanGoToPreviousPage", typeof(bool), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// /// public static readonly DependencyProperty CanGoToPreviousPageProperty = CanGoToPreviousPagePropertyKey.DependencyProperty; ////// /// protected static readonly DependencyPropertyKey CanGoToNextPagePropertyKey = DependencyProperty.RegisterReadOnly( "CanGoToNextPage", typeof(bool), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// /// public static readonly DependencyProperty CanGoToNextPageProperty = CanGoToNextPagePropertyKey.DependencyProperty; ////// /// Attached property used to signify which of the child DocumentPageView objects /// is the master page. If more than one DocumentPageView has this value set, /// then the first tagged DocumentPageView in the tree (depth-first) is designated /// the master. If none of the children have this property set, then the depth-first /// PageView is designated the master. /// public static readonly DependencyProperty IsMasterPageProperty = DependencyProperty.RegisterAttached( "IsMasterPage", typeof(bool), typeof(DocumentViewerBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// DependencyProperty getter for /// The element from which to read the attached property. public static bool GetIsMasterPage(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(IsMasterPageProperty); } ///property. /// /// DependencyProperty setter for /// The element to which to write the attached property. /// The property value to set public static void SetIsMasterPage(DependencyObject element, bool value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsMasterPageProperty, value); } #endregion Public Dynamic Properties #endregion Public Properties //------------------------------------------------------------------- // // Public Events // //-------------------------------------------------------------------- #region Public Events ///property. /// /// Fired when collection of DocumentPageViews is changed. /// public event EventHandler PageViewsChanged; #endregion Public Events //------------------------------------------------------------------- // // Protected Methods // //------------------------------------------------------------------- #region Protected Methods ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new DocumentViewerBaseAutomationPeer(this); } ///) /// /// Invalidates the PageViews collection, triggering a call to GetPageViews. /// protected void InvalidatePageViews() { // Update collection of DocumentPageViews if the collection has // been explicitly invalidated. UpdatePageViews(); InvalidateMeasure(); } ////// Returns the master DocumentPageView. /// protected DocumentPageView GetMasterPageView() { int index; DocumentPageView masterPageView = null; // Search for the first element with IsMasterPage property set. for (index = 0; index < _pageViews.Count; index++) { if (GetIsMasterPage(_pageViews[index])) { masterPageView = _pageViews[index]; break; } } // If none of the DocumentPageViews have this property set, // then use the first one in the collection. if (masterPageView == null) { masterPageView = _pageViews.Count > 0 ? _pageViews[0] : null; } return masterPageView; } ////// Creates a collection of DocumentPageView objects used to display Document. /// /// True if the collection is different than the public PageViews collection. ///Collection of DocumentPageView objects used to display Document. protected virtual ReadOnlyCollectionGetPageViewsCollection(out bool changed) { List pageViewList; AdornerDecorator adornerDecorator; // By default retrieve all DocumentPageViews from style. pageViewList = new List (1/* simplest case has just one element */); FindDocumentPageViews(this, pageViewList); // By default use AdornerDecorator.Child as RenderScope for TextEditor. Retieve // AdornerDecorator from the style. adornerDecorator = FindAdornerDecorator(this); this.TextEditorRenderScope = (adornerDecorator != null) ? (adornerDecorator.Child as FrameworkElement) : null; // Since existing DocumentPageViews are being replaced, need to disconnect // them from the Document. for (int index = 0; index < _pageViews.Count; index++) { _pageViews[index].DocumentPaginator = null; } changed = true; return new ReadOnlyCollection (pageViewList); } /// /// Called when the PageViews collection is modified; this occurs when GetPageViews /// returns True or if the control's template is modified. /// protected virtual void OnPageViewsChanged() { // Raise notification about change to DocumentPageView collection. if (this.PageViewsChanged != null) { this.PageViewsChanged(this, EventArgs.Empty); } // Change of DocumentPageView collection may cause invalidation of content // represented by DocumentPageViews. OnMasterPageNumberChanged(); } ////// Called when the MasterPageNumber property is changed, this occurs when the /// developer manually sets the property, or when the FirstPage, NextPage, /// etc commands are executed. /// protected virtual void OnMasterPageNumberChanged() { // Invalidation of MasterPage invalidates following properties: // - MasterPageNumber // - CanGoToPreviousPage // - CanGoToNextPage UpdateReadOnlyProperties(true, true); } ////// Called when a BringIntoView event is bubbled up from the Document. /// Base implementation will move the master page to the page /// on which the element occurs. /// /// The object to make visible. /// The rectangular region in the object's coordinate space which should be made visible. /// protected virtual void OnBringIntoView(DependencyObject element, Rect rect, int pageNumber) { if (element == null) { throw new ArgumentNullException("element"); } OnGoToPageCommand(pageNumber); } ////// Handler for the PreviousPage command. /// protected virtual void OnPreviousPageCommand() { // If can go to the previous page, shift all pages // by decrementing page number by 1. if (this.CanGoToPreviousPage) { ShiftPagesByOffset(-1); } } ////// Handler for the NextPage command. /// protected virtual void OnNextPageCommand() { // If can go to the next page, shift all pages // by incrementing page number by 1. if (this.CanGoToNextPage) { ShiftPagesByOffset(1); } } ////// Handler for the FirstPage command. /// protected virtual void OnFirstPageCommand() { // Navigate the master page to the first one and shift // all remaining pages by delta. ShiftPagesByOffset(1 - this.MasterPageNumber); } ////// Handler for the LastPage command. /// protected virtual void OnLastPageCommand() { // Navigate the master page to the last one and shift // all remaining pages by delta. ShiftPagesByOffset(this.PageCount - this.MasterPageNumber); } ////// Handler for the GoToPage command. /// /// protected virtual void OnGoToPageCommand(int pageNumber) { // Check if can go to the specified page. // Navigate the master page to specified page number and shift // all remaining pages by delta. if (CanGoToPage(pageNumber)) { ShiftPagesByOffset(pageNumber - this.MasterPageNumber); } } ////// Handler for the Print command. /// protected virtual void OnPrintCommand() { #if !DONOTREFPRINTINGASMMETA XpsDocumentWriter docWriter; PrintDocumentImageableArea ia = null; // Only one printing job is allowed. if (_documentWriter != null) { return; } if (_document != null) { // Show print dialog. docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia); if (docWriter != null && ia != null) { // Register for WritingCompleted event. _documentWriter = docWriter; _documentWriter.WritingCompleted += new WritingCompletedEventHandler(HandlePrintCompleted); _documentWriter.WritingCancelled += new WritingCancelledEventHandler(HandlePrintCancelled); // Since _documentWriter value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); // Write to the PrintQueue if( _document is FixedDocumentSequence ) { docWriter.WriteAsync(_document as FixedDocumentSequence); } else if( _document is FixedDocument ) { docWriter.WriteAsync(_document as FixedDocument); } else { docWriter.WriteAsync(_document.DocumentPaginator); } } } #endif // DONOTREFPRINTINGASMMETA } ////// Handler for the CancelPrint command. /// protected virtual void OnCancelPrintCommand() { #if !DONOTREFPRINTINGASMMETA if (_documentWriter != null) { _documentWriter.CancelAsync(); } #endif // DONOTREFPRINTINGASMMETA } ////// Called when the Document property is changed. /// protected virtual void OnDocumentChanged() { int index; // Document has been changed. Update existing DocumentPageViews to point them to the new Document. for (index = 0; index < _pageViews.Count; index++) { _pageViews[index].DocumentPaginator = (_document != null) ? _document.DocumentPaginator : null; } // Document invalidation invalidates following properties: // - PageCount // - MasterPageNumber // - CanGoToPreviousPage // - CanGoToNextPage UpdateReadOnlyProperties(true, true); // Attach TextEditor, if content supports it. This method will also // detach TextEditor from old content. AttachTextEditor(); } #endregion Protected Methods //------------------------------------------------------------------- // // Protected Properties // //-------------------------------------------------------------------- #region Protected Properties ////// Returns enumerator to logical children. /// protected internal override IEnumerator LogicalChildren { get { if (this.HasLogicalChildren && _document != null) { return new SingleChildEnumerator(_document); } return EmptyEnumerator.Instance; } } #endregion Protected Properties //------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------- #region Internal Methods ////// Determines whether DocumentPageView is a master page. /// /// Instance of DocumentPageView. ///Whether given instance of DocumentPageView is a master page. internal bool IsMasterPageView(DocumentPageView pageView) { Invariant.Assert(pageView != null); return (pageView == GetMasterPageView()); } ////// Invoked when the "Find" button in the Find Toolbar is clicked. /// This method invokes the actual Find process. /// internal ITextRange Find(FindToolBar findToolBar) { ITextView masterPageTextView = null; DocumentPageView masterPage = GetMasterPageView(); if (masterPage != null && masterPage is IServiceProvider) { masterPageTextView = ((IServiceProvider)masterPage).GetService(typeof(ITextView)) as ITextView; } return DocumentViewerHelper.Find(findToolBar, _textEditor, _textView, masterPageTextView); } #endregion Internal Methods //-------------------------------------------------------------------- // // Internal Properties // //------------------------------------------------------------------- #region Internal Properties ////// Whether text selection is enabled or disabled. /// internal bool IsSelectionEnabled { get { return CheckFlags(Flags.IsSelectionEnabled); } set { SetFlags(value, Flags.IsSelectionEnabled); AttachTextEditor(); } } ////// TextEditor instance. /// internal TextEditor TextEditor { get { return _textEditor; } } ////// Allows overriding the RenderScope used by the TextEditor. /// internal FrameworkElement TextEditorRenderScope { get { return _textEditorRenderScope; } set { _textEditorRenderScope = value; AttachTextEditor(); } } #endregion Internal Properties //-------------------------------------------------------------------- // // Private Methods // //------------------------------------------------------------------- #region Private Methods ////// Retrieves an ITextPointer from the MasterPage. /// If startOfPage is true, then we will retreive the first /// TextPointer in the first TextSegment in the DocumentPageTextView. /// Else, we will retreive the last TextPointer in the last TextSegment /// of the MasterPage DocumentPageTextView. /// /// ///private ITextPointer GetMasterPageTextPointer(bool startOfPage) { ITextPointer masterPointer = null; ITextView textView = null; DocumentPageView masterPage = GetMasterPageView(); if (masterPage != null && masterPage is IServiceProvider) { textView = ((IServiceProvider)masterPage).GetService(typeof(ITextView)) as ITextView; if (textView != null && textView.IsValid) { // Find the very first/(last) text pointer in this textView. foreach (TextSegment textSegment in textView.TextSegments) { if (textSegment.IsNull) { continue; } if (masterPointer == null) { // Set initial masterPointer value. masterPointer = startOfPage ? textSegment.Start : textSegment.End; } else { if (startOfPage) { if (textSegment.Start.CompareTo(masterPointer) < 0) { // Start is before the current masterPointer masterPointer = textSegment.Start; } } else { // end is after than the current masterPointer if (textSegment.End.CompareTo(masterPointer) > 0) { masterPointer = textSegment.End; } } } } } } return masterPointer; } /// /// Update collection of DocumentPageViews in responce to Visual tree changes. /// ///Whether collection of DocumentPageViews has been updated. private void UpdatePageViews() { int index; bool changed; ReadOnlyCollectionpageViews; // Get collection of new DocumentPageViews. pageViews = GetPageViewsCollection(out changed); // If DocumentPageViews collection has not been changed, there is nothing to update. if (changed) { // Verify collection of DocumentPageViews. It needs to meet following conditions: // a) at least one DocumentPageView, // b) only one page has IsMasterPage property set to true, // c) unique PageNumbers for each active DocumentPageView, VerifyDocumentPageViews(pageViews); // New collection of DocumentPageViews is replacing the old one. Point new // DocumentPageViews to the Document. _pageViews = pageViews; for (index = 0; index < _pageViews.Count; index++) { _pageViews[index].DocumentPaginator = (_document != null) ? _document.DocumentPaginator : null; } // Collection of DocumentPageView has been changed. Need to update // TextView, if one already exists. if (_textView != null) { _textView.OnPagesUpdated(); } // DocumentPageViews collection has been changed. Notify all listeners // and/or derived classes about this fact. OnPageViewsChanged(); } } /// /// Verify collection of DocumentPageViews. It needs to meet following conditions: /// a) collection is not null, /// b) only one page has IsMasterPage property set to true, /// c) unique PageNumbers for each active DocumentPageView, /// /// Collection of DocumentPageViews to validate. private void VerifyDocumentPageViews(ReadOnlyCollectionpageViews) { int index; bool hasMasterPage = false; // At least one DocumentPageView is required. if (pageViews == null) { throw new ArgumentException(SR.Get(SRID.DocumentViewerPageViewsCollectionEmpty)); } // Expecting only one DocumentPageView with IsMasterPage property set to true. for (index = 0; index < pageViews.Count; index++) { if (GetIsMasterPage(pageViews[index])) { if (hasMasterPage) { throw new ArgumentException(SR.Get(SRID.DocumentViewerOneMasterPage)); } hasMasterPage = true; } } // Unique PageNumbers for each active DocumentPageView. } /// /// Does deep Visual tree walk to retrieve all DocumentPageViews. /// It stops recursing down into visual tree in following situations: /// a) Visual is UIElement and it is not part of Contol Template, /// b) Visual is DocumentPageView. /// /// FrameworkElement that is part of Control Template. /// Collection of DocumentPageViews; found elements are appended here. ///Whether collection of DocumentPageViews has been updated. private void FindDocumentPageViews(Visual root, ListpageViews) { Invariant.Assert(root != null); Invariant.Assert(pageViews != null); FrameworkElement fe; // Do deep tree walk to retrieve all DocumentPageViews. // It stops recursing down into visual tree in following situations: // a) Visual is UIElement and it is not part of Contol Template, // b) Visual is DocumentPageView. // Add to collection any DocumentPageViews found in the Control Template. int count = root.InternalVisualChildrenCount; for(int i = 0; i < count; i++) { Visual child = root.InternalGetVisualChild(i); fe = child as FrameworkElement; if (fe != null) { if (fe.TemplatedParent != null) { if (fe is DocumentPageView) { pageViews.Add(fe as DocumentPageView); } else { FindDocumentPageViews(fe, pageViews); } } } else { FindDocumentPageViews(child, pageViews); } } } /// /// Does deep Visual tree walk to retrieve an AdornerDecorator. Because /// AdornerDecorator is supposed to cover all DocumentPageViews, it stops /// recursing down into visual tree in following situations: /// a) Visual is UIElement and it is not part of Contol Template, /// b) Visual is DocumentPageView. /// c) Visual is AdornerDecorator. /// /// FrameworkElement that is part of Control Template. ///AdornerDecorator, if found. private AdornerDecorator FindAdornerDecorator(Visual root) { Invariant.Assert(root != null); FrameworkElement fe; AdornerDecorator adornerDecorator = null; // Do deep Visual tree walk to retrieve an AdornerDecorator. Because // AdornerDecorator is supposed to cover all DocumentPageViews, it stops // recursing down into visual tree in following situations: // a) Visual is UIElement and it is not part of Contol Template, // b) Visual is DocumentPageView. // c) Visual is AdornerDecorator. int count = root.InternalVisualChildrenCount; for(int i = 0; i < count; i++) { Visual child = root.InternalGetVisualChild(i); fe = child as FrameworkElement; if (fe != null) { if (fe.TemplatedParent != null) { if (fe is AdornerDecorator) { adornerDecorator = (AdornerDecorator)fe; } else if (!(fe is DocumentPageView)) { adornerDecorator = FindAdornerDecorator(fe); } // else stop on DocumentPageView } } else { adornerDecorator = FindAdornerDecorator(child); } if (adornerDecorator != null) { break; } } return adornerDecorator; } ////// Attach TextEditor to Document, if supports text. /// private void AttachTextEditor() { AnnotationService service = AnnotationService.GetService(this); ITextContainer textContainer; // This method is called when Document is changing, so need // to clear old TextEditor data. if (_textEditor != null) { _textEditor.OnDetach(); _textEditor = null; if (_textView.TextContainer.TextView == _textView) { _textView.TextContainer.TextView = null; } _textView = null; } if (service != null) { // Must be enabled - otherwise it won't be on the tree service.Disable(); } // If new Document supports TextEditor, create one. // If the Document is already attached to TextEditor (TextSelection != null), // do not create TextEditor for this instance of the viewer. (This situation may happen // when the same instance of Document is attached to more than one viewer). textContainer = this.TextContainer; if (textContainer != null && this.TextEditorRenderScope != null && textContainer.TextSelection == null) { _textView = new MultiPageTextView(this, this.TextEditorRenderScope, textContainer); _textEditor = new TextEditor(textContainer, this, false); _textEditor.IsReadOnly = !IsEditingEnabled; _textEditor.TextView = _textView; textContainer.TextView = _textView; } // Re-enable the service in order to register on the new TextView if (service != null) { service.Enable(service.Store); } } ////// Called when WritingCompleted event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCompleted(object sender, WritingCompletedEventArgs e) { CleanUpPrintOperation(); } ////// Called when WritingCancelled event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCancelled(object sender, WritingCancelledEventArgs e) { CleanUpPrintOperation(); } ////// Handler for PaginationCompleted event raised by the Document. /// private void HandlePaginationCompleted(object sender, EventArgs e) { // PaginationCompleted may invalidate following properties: // - PageCount // - CanGoToNextPage UpdateReadOnlyProperties(true, false); } ////// Handler for PaginationProgress event raised by the Document. /// private void HandlePaginationProgress(object sender, EventArgs e) { // PaginationProgress may invalidate following properties: // - PageCount // - CanGoToNextPage UpdateReadOnlyProperties(true, false); } ////// Handler for GetPageNumberCompleted event raised by the Document. /// private void HandleGetPageNumberCompleted(object sender, GetPageNumberCompletedEventArgs e) { BringIntoViewState bringIntoViewState; // At this point the Document's page count might have changed, // so update properties accordingly. UpdateReadOnlyProperties(true, false); if (_document != null && sender == _document.DocumentPaginator && e != null) { if (!e.Cancelled && (e.Error == null)) { bringIntoViewState = e.UserState as BringIntoViewState; if (bringIntoViewState != null && bringIntoViewState.Source == this) { OnBringIntoView(bringIntoViewState.TargetObject, bringIntoViewState.TargetRect, e.PageNumber + 1); } } } } ////// Makes sure the target is visible in the client area. May cause navigation /// to a different page. /// /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private void HandleRequestBringIntoView(RequestBringIntoViewEventArgs args) { DependencyObject child; DependencyObject parent; ContentPosition contentPosition; BringIntoViewState bringIntoViewState; DynamicDocumentPaginator documentPaginator; Rect targetRect = Rect.Empty; if (args != null && args.TargetObject != null && _document is DependencyObject) { // If the passed in object is a logical child of DocumentViewer's Document, // attempt to make it visible now. // Special case: TargetObject is the document itself. Then scroll to the top (page 1). // This supports navigating from baseURI#anchor to just baseURI. parent = _document as DependencyObject; if (args.TargetObject == _document) { OnGoToPageCommand(1); args.Handled = true; // Mark the event as handled. } else { // Verify if TargetObject is in fact a child of Document. child = args.TargetObject; while (child != null && child != parent) { // Skip elements in the control's template (if such exists) and // walk up logical tree to find if the focused element is within // the document. FrameworkElement fe = child as FrameworkElement; if (fe != null && fe.TemplatedParent != null) { child = fe.TemplatedParent; } else { child = LogicalTreeHelper.GetParent(child); } } if (child != null) { // Special case UIElements already connected to visual tree. if (args.TargetObject is UIElement) { UIElement targetObject = (UIElement)args.TargetObject; if (VisualTreeHelper.IsAncestorOf(this, targetObject)) { targetRect = args.TargetRect; if (targetRect.IsEmpty) { targetRect = new Rect(targetObject.RenderSize); } GeneralTransform transform = targetObject.TransformToAncestor(this); targetRect = transform.TransformBounds(targetRect); targetRect.IntersectsWith(new Rect(this.RenderSize)); } } // If target is not already visible, bring appropriate page into view. if (targetRect.IsEmpty) { // Get content position for given target. documentPaginator = _document.DocumentPaginator as DynamicDocumentPaginator; if (documentPaginator != null) { contentPosition = documentPaginator.GetObjectPosition(args.TargetObject); if (contentPosition != null && contentPosition != ContentPosition.Missing) { // Asynchronously retrieve PageNumber for given ContentPosition. bringIntoViewState = new BringIntoViewState(this, contentPosition, args.TargetObject, args.TargetRect); documentPaginator.GetPageNumberAsync(contentPosition, bringIntoViewState); } } } args.Handled = true; // Mark the event as handled. } } if (args.Handled) { // Create new BringIntoView request for this element, so // if there is an ancestor handling BringIntoView, it can // react appropriately and bring this element into view. if (targetRect.IsEmpty) { BringIntoView(); } else { BringIntoView(targetRect); } } } } ////// Update values for readonly properties. /// /// Whether PageCount has been changed. /// Whether MasterPageNumber has been changed. private void UpdateReadOnlyProperties(bool pageCountChanged, bool masterPageChanged) { if (pageCountChanged) { SetValue(PageCountPropertyKey, (_document != null) ? _document.DocumentPaginator.PageCount : 0); } bool invalidateRequery = false; if (masterPageChanged) { int masterPageNumber = 0; DocumentPageView masterPageView; if (_document != null && _pageViews.Count > 0) { masterPageView = GetMasterPageView(); if (masterPageView != null) { masterPageNumber = masterPageView.PageNumber + 1; } } SetValue(MasterPageNumberPropertyKey, masterPageNumber); SetValue(CanGoToPreviousPagePropertyKey, MasterPageNumber > 1); invalidateRequery = true; } if (pageCountChanged || masterPageChanged) { bool canGoToNextPage = false; if (_document != null) { canGoToNextPage = (MasterPageNumber < _document.DocumentPaginator.PageCount) || !_document.DocumentPaginator.IsPageCountValid; } SetValue(CanGoToNextPagePropertyKey, canGoToNextPage); invalidateRequery = true; } if(invalidateRequery) { CommandManager.InvalidateRequerySuggested(); } } ////// Shift all pages by specified offset. /// private void ShiftPagesByOffset(int offset) { int index; if (offset != 0) { for (index = 0; index < _pageViews.Count; index++) { _pageViews[index].PageNumber += offset; } // Change of PageNumber property on DocumentPageViews will cause // invalidation of content represented by DocumentPageViews. OnMasterPageNumberChanged(); } } ////// Sets or unsets one or multiple flags. /// /// Whether flag or flags are set or cleared. /// Combination of flags to change. private void SetFlags(bool value, Flags flags) { _flags = value ? (_flags | flags) : (_flags & (~flags)); } ////// Returns true if all of passed flags in the bitmask are set. /// /// Combination of flags to get the value. ///Returns true if all of passed flags in the bitmask are set. private bool CheckFlags(Flags flags) { return ((_flags & flags) == flags); } ////// The Document has changed and needs to be updated. /// private void DocumentChanged(IDocumentPaginatorSource oldDocument, IDocumentPaginatorSource newDocument) { DependencyObject doDocument; DynamicDocumentPaginator dynamicDocumentPaginator; _document = newDocument; // Cleanup state associated with the old document. if (oldDocument != null) { // If Document was added to logical tree of DocumentViewer before, remove it. if (CheckFlags(Flags.DocumentAsLogicalChild)) { RemoveLogicalChild(oldDocument); } // Unregister from PaginationProgress and PaginationCompleted events. dynamicDocumentPaginator = oldDocument.DocumentPaginator as DynamicDocumentPaginator; if (dynamicDocumentPaginator != null) { dynamicDocumentPaginator.PaginationProgress -= new PaginationProgressEventHandler(HandlePaginationProgress); dynamicDocumentPaginator.PaginationCompleted -= new EventHandler(HandlePaginationCompleted); dynamicDocumentPaginator.GetPageNumberCompleted -= new GetPageNumberCompletedEventHandler(HandleGetPageNumberCompleted); } DependencyObject depObj = oldDocument as DependencyObject; if (depObj != null) { depObj.ClearValue(PathNode.HiddenParentProperty); } } // If DocumentViewer was created through style, then do not modify // the logical tree. Instead, set "core parent" for the Document. doDocument = _document as DependencyObject; if (doDocument != null && LogicalTreeHelper.GetParent(doDocument) != null && doDocument is ContentElement) { // Set the "core parent" back to us. ContentOperations.SetParent((ContentElement)doDocument, this); SetFlags(false, Flags.DocumentAsLogicalChild); } else { SetFlags(true, Flags.DocumentAsLogicalChild); } // Initialize state associated with the new document. if (_document != null) { // If Document should be part of DocumentViewer's logical tree, add it. if (CheckFlags(Flags.DocumentAsLogicalChild)) { AddLogicalChild(_document); } // Register for PaginationProgress and PaginationCompleted events. dynamicDocumentPaginator = _document.DocumentPaginator as DynamicDocumentPaginator; if (dynamicDocumentPaginator != null) { dynamicDocumentPaginator.PaginationProgress += new PaginationProgressEventHandler(HandlePaginationProgress); dynamicDocumentPaginator.PaginationCompleted += new EventHandler(HandlePaginationCompleted); dynamicDocumentPaginator.GetPageNumberCompleted += new GetPageNumberCompletedEventHandler(HandleGetPageNumberCompleted); } // Setup DPs and processors for annotation handling. If the service isn't already // enabled the processors will be registered by the service when it is enabled. FlowDocument flowDocument; DependencyObject doc = _document as DependencyObject; if (_document is FixedDocument || _document is FixedDocumentSequence) { // Clear properties that aren't needed for FixedDocument this.ClearValue(AnnotationService.DataIdProperty); // Setup service to look for FixedPages in the content AnnotationService.SetSubTreeProcessorId(this, FixedPageProcessor.Id); // Tell the content how to get to its parent DocumentViewer doc.SetValue(PathNode.HiddenParentProperty, this); // If the service is already registered, set it up for fixed content AnnotationService service = AnnotationService.GetService(this); if (service != null) { service.LocatorManager.RegisterSelectionProcessor(new FixedTextSelectionProcessor(), typeof(TextRange)); service.LocatorManager.RegisterSelectionProcessor(new FixedTextSelectionProcessor(), typeof(TextAnchor)); } } else if ((flowDocument = _document as FlowDocument) != null) { // Tell the content how to get to its parent DocumentViewer flowDocument.SetValue(PathNode.HiddenParentProperty, this); // If the service is already registered, set it up for fixed content AnnotationService service = AnnotationService.GetService(this); if (service != null) { service.LocatorManager.RegisterSelectionProcessor(new TextSelectionProcessor(), typeof(TextRange)); service.LocatorManager.RegisterSelectionProcessor(new TextSelectionProcessor(), typeof(TextAnchor)); service.LocatorManager.RegisterSelectionProcessor(new TextViewSelectionProcessor(), typeof(DocumentViewerBase)); } // Setup service to use DataID processor AnnotationService.SetDataId(this, "FlowDocument"); } else { // Clear values that were set directly on the tree - only valid for Fixed or Flow Documents this.ClearValue(AnnotationService.SubTreeProcessorIdProperty); this.ClearValue(AnnotationService.DataIdProperty); } } // Document is also represented as Automation child. Need to invalidate peer to force update. DocumentViewerBaseAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as DocumentViewerBaseAutomationPeer; if (peer != null) { peer.InvalidatePeer(); } // Respond to Document change - update state that is affected by this change. OnDocumentChanged(); } ////// Cleans up after a print operation by unregistering for events and re-enabling buttons. /// private void CleanUpPrintOperation() { #if !DONOTREFPRINTINGASMMETA if (_documentWriter != null) { _documentWriter.WritingCompleted -= new WritingCompletedEventHandler(HandlePrintCompleted); _documentWriter.WritingCancelled -= new WritingCancelledEventHandler(HandlePrintCancelled); _documentWriter = null; // Since _documentWriter value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } #endif // DONOTREFPRINTINGASMMETA } #region Commands ////// Set up Command and RoutedCommand bindings. /// private static void CreateCommandBindings() { ExecutedRoutedEventHandler executedHandler; CanExecuteRoutedEventHandler canExecuteHandler; // Create our generic ExecutedRoutedEventHandler. executedHandler = new ExecutedRoutedEventHandler(ExecutedRoutedEventHandler); // Create our generic QueryEnabledStatusHandler canExecuteHandler = new CanExecuteRoutedEventHandler(CanExecuteRoutedEventHandler); // Command: NavigationCommands.PreviousPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.PreviousPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.NextPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.NextPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.FirstPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.FirstPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.LastPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.LastPage, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.GoToPage CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), NavigationCommands.GoToPage, executedHandler, canExecuteHandler); // no key gesture // Command: ApplicationCommands.Print CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), ApplicationCommands.Print, executedHandler, canExecuteHandler, new KeyGesture(Key.P, ModifierKeys.Control)); // Command: ApplicationCommands.CancelPrint CommandHelpers.RegisterCommandHandler(typeof(DocumentViewerBase), ApplicationCommands.CancelPrint, executedHandler, canExecuteHandler); // no key gesture // Register editing command handlers - After our commands to let editor handle them first TextEditor.RegisterCommandHandlers(typeof(DocumentViewerBase), /*acceptsRichContent:*/true, /*readOnly:*/!IsEditingEnabled, /*registerEventListeners*/true); } ////// Central handler for CanExecuteRouted events fired by Commands directed at DocumentViewerBase. /// /// The target of this Command, expected to be DocumentViewerBase /// The event arguments for this event. private static void CanExecuteRoutedEventHandler(object target, CanExecuteRoutedEventArgs args) { DocumentViewerBase dv = target as DocumentViewerBase; Invariant.Assert(dv != null, "Target of CanExecuteRoutedEventHandler must be DocumentViewerBase."); Invariant.Assert(args != null, "args cannot be null."); // DocumentViewerBase is capable of execution of the majority of its commands. // Special rules: // a) Print command is enabled when Document is attached and printing is not in progress. // b) CancelPrint command is enabled only during printing. if (args.Command == ApplicationCommands.Print) { args.CanExecute = (dv.Document != null) && (dv._documentWriter == null); args.Handled = true; } else if (args.Command == ApplicationCommands.CancelPrint) { args.CanExecute = (dv._documentWriter != null); } else { args.CanExecute = true; } } ////// Central handler for all ExecutedRouted events fired by Commands directed at DocumentViewerBase. /// /// The target of this Command, expected to be DocumentViewerBase. /// The event arguments associated with this event. private static void ExecutedRoutedEventHandler(object target, ExecutedRoutedEventArgs args) { DocumentViewerBase dv = target as DocumentViewerBase; Invariant.Assert(dv != null, "Target of ExecuteEvent must be DocumentViewerBase."); Invariant.Assert(args != null, "args cannot be null."); // Now we execute the method corresponding to the Command that fired this event; // each Command has its own protected virtual method that performs the operation // corresponding to the Command. if (args.Command == NavigationCommands.PreviousPage) { dv.OnPreviousPageCommand(); } else if (args.Command == NavigationCommands.NextPage) { dv.OnNextPageCommand(); } else if (args.Command == NavigationCommands.FirstPage) { dv.OnFirstPageCommand(); } else if (args.Command == NavigationCommands.LastPage) { dv.OnLastPageCommand(); } else if (args.Command == NavigationCommands.GoToPage) { // Ignore GoToPageCommand, if: // a) there is no value for the page number. // b) the value cannot be converted to Int32. if (args.Parameter != null) { int pageNumber = -1; try { pageNumber = Convert.ToInt32(args.Parameter, System.Globalization.CultureInfo.CurrentCulture); } #pragma warning disable 56502 // Allow empty catch statements. catch (InvalidCastException) { } catch (OverflowException) { } catch (FormatException) { } #pragma warning restore 56502 if (pageNumber >= 0) { dv.OnGoToPageCommand(pageNumber); } } } else if (args.Command == ApplicationCommands.Print) { dv.OnPrintCommand(); } else if (args.Command == ApplicationCommands.CancelPrint) { dv.OnCancelPrintCommand(); } else { Invariant.Assert(false, "Command not handled in ExecutedRoutedEventHandler."); } } #endregion Commands #region Static Methods ////// Called from the event handler to make sure the target is visible in the client /// area. May cause navigation to a different page. /// /// The instance handling the event. /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private static void HandleRequestBringIntoView(object sender, RequestBringIntoViewEventArgs args) { if (sender != null && sender is DocumentViewerBase) { ((DocumentViewerBase)sender).HandleRequestBringIntoView(args); } } ////// The Document has changed and needs to be updated. /// private static void DocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is DocumentViewerBase); ((DocumentViewerBase) d).DocumentChanged((IDocumentPaginatorSource) e.OldValue, (IDocumentPaginatorSource) e.NewValue); // Since Document state is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } #endregion Static Methods #endregion Private Methods //------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------- #region Private Properties ////// ITextContainer associated with Document. /// private ITextContainer TextContainer { get { ITextContainer textContainer = null; if (_document != null) { if (_document is IServiceProvider && CheckFlags(Flags.IsSelectionEnabled)) { textContainer = ((IServiceProvider)_document).GetService(typeof(ITextContainer)) as ITextContainer; } } return textContainer; } } #endregion Private Properties //-------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------- #region Private Fields private ReadOnlyCollection_pageViews; // Collection of DocumentPageViews presenting paginated Document. private FrameworkElement _textEditorRenderScope; // RenderScope associated with the TextEditor. private MultiPageTextView _textView; // ITextView associated with DocumentViewer. private TextEditor _textEditor; // TextEditor associated with DocumentViewer. private IDocumentPaginatorSource _document; // IDocumentPaginatorSource representing Document. private Flags _flags; // Flags reflecting various aspects of object's state. #if !DONOTREFPRINTINGASMMETA private XpsDocumentWriter _documentWriter; // DocumentWriter used for printing. #endif // DONOTREFPRINTINGASMMETA private static bool IsEditingEnabled = false; // A flag enabling text editing within a document viewer // accessible only through reflection. #endregion Private Fields //-------------------------------------------------------------------- // // Private Types // //-------------------------------------------------------------------- #region Private Types /// /// Flags reflecting various aspects of viewer's state. /// [System.Flags] private enum Flags { // free bit = 0x10, IsSelectionEnabled = 0x20, // Is text selection enabled. DocumentAsLogicalChild = 0x40, // Is Document part of logical tree. } ////// State of BringIntoView operation. /// private class BringIntoViewState { internal BringIntoViewState(DocumentViewerBase source, ContentPosition contentPosition, DependencyObject targetObject, Rect targetRect) { this.Source = source; this.ContentPosition = contentPosition; this.TargetObject = targetObject; this.TargetRect = targetRect; } internal DocumentViewerBase Source; internal ContentPosition ContentPosition; internal DependencyObject TargetObject; internal Rect TargetRect; } #endregion Private Types //------------------------------------------------------------------- // // IAddChild // //-------------------------------------------------------------------- #region IAddChild ////// Called to add the object as a Child. /// /// Object to add as a child. ///DocumentViewerBase only supports a single child of type IDocumentPaginatorSource. void IAddChild.AddChild(Object value) { if (value == null) { throw new ArgumentNullException("value"); } // Check if Content has already been set. if (this.Document != null) { throw new InvalidOperationException(SR.Get(SRID.DocumentViewerCanHaveOnlyOneChild)); } // Only IDocumentPaginatorSource is a valid content. IDocumentPaginatorSource document = value as IDocumentPaginatorSource; if (document == null) { throw new ArgumentException(SR.Get(SRID.DocumentViewerChildMustImplementIDocumentPaginatorSource), "value"); } this.Document = document; } ////// Called when text appears under the tag in markup /// /// Text to add to the Object. ///DocumentViewer does not support Text children. void IAddChild.AddText(string text) { XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this); } #endregion IAddChild //------------------------------------------------------------------- // // IServiceProvider // //------------------------------------------------------------------- #region IServiceProvider ////// Returns service objects associated with this control. /// /// Specifies the type of service object to get. object IServiceProvider.GetService(Type serviceType) { object service = null; if (serviceType == null) { throw new ArgumentNullException("serviceType"); } // Following services are available: // (1) TextView - wrapper for TextViews exposed by PageViews. // (2) TextContainer - the service object is retrieved from Document. if (serviceType == typeof(ITextView)) { service = _textView; } else if (serviceType == typeof(TextContainer) || serviceType == typeof(ITextContainer)) { service = this.TextContainer; } return service; } #endregion IServiceProvider } } #pragma warning enable 1634, 1691 // 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
- PerformanceCounterManager.cs
- UsernameTokenFactoryCredential.cs
- XmlDataDocument.cs
- Tokenizer.cs
- TaskFileService.cs
- RSAPKCS1SignatureFormatter.cs
- EncryptedReference.cs
- Pkcs7Recipient.cs
- FormsAuthentication.cs
- StatusBar.cs
- RadioButton.cs
- _LocalDataStore.cs
- XmlReflectionMember.cs
- WebPart.cs
- PieceDirectory.cs
- BrowserCapabilitiesCodeGenerator.cs
- ISAPIApplicationHost.cs
- RegexInterpreter.cs
- TrustSection.cs
- odbcmetadatafactory.cs
- MethodBuilderInstantiation.cs
- UncommonField.cs
- SerializationInfoEnumerator.cs
- XmlSerializerNamespaces.cs
- TimeSpanConverter.cs
- FrugalList.cs
- WebResourceUtil.cs
- RenderingEventArgs.cs
- ResourceReader.cs
- DocumentManager.cs
- DocumentViewer.cs
- ImageAutomationPeer.cs
- XsltException.cs
- streamingZipPartStream.cs
- WindowsMenu.cs
- SecurityProtocol.cs
- Font.cs
- AmbientLight.cs
- HelpEvent.cs
- InputEventArgs.cs
- IRCollection.cs
- VectorValueSerializer.cs
- MultiAsyncResult.cs
- TableCell.cs
- NamespaceDecl.cs
- XmlValidatingReader.cs
- RawUIStateInputReport.cs
- DbDeleteCommandTree.cs
- AudioFileOut.cs
- RadioButtonFlatAdapter.cs
- x509utils.cs
- BindingMemberInfo.cs
- LogicalTreeHelper.cs
- RootProfilePropertySettingsCollection.cs
- SByteConverter.cs
- OleDbConnectionFactory.cs
- ThrowHelper.cs
- MultiPageTextView.cs
- ProfileInfo.cs
- SQLBytesStorage.cs
- RangeContentEnumerator.cs
- InstanceHandle.cs
- WpfGeneratedKnownProperties.cs
- httpapplicationstate.cs
- VisualTreeHelper.cs
- DependentTransaction.cs
- RangeExpression.cs
- LOSFormatter.cs
- safex509handles.cs
- SoapReflectionImporter.cs
- ToolBarPanel.cs
- InfiniteTimeSpanConverter.cs
- QueryContext.cs
- MinimizableAttributeTypeConverter.cs
- Pair.cs
- UseLicense.cs
- CodeDomDesignerLoader.cs
- ResizeGrip.cs
- TextShapeableCharacters.cs
- StringDictionary.cs
- securestring.cs
- WebPartHelpVerb.cs
- InputLangChangeRequestEvent.cs
- MetadataCache.cs
- ResourceManagerWrapper.cs
- DataObjectPastingEventArgs.cs
- Validator.cs
- ViewSimplifier.cs
- EncoderExceptionFallback.cs
- SecurityTokenException.cs
- CountAggregationOperator.cs
- LinearGradientBrush.cs
- FilterEventArgs.cs
- DataBindingList.cs
- XmlSchemaInfo.cs
- DataGridViewControlCollection.cs
- AppLevelCompilationSectionCache.cs
- VSDExceptions.cs
- HTMLTagNameToTypeMapper.cs
- ComContractElementCollection.cs