Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Controls / FlowDocumentScrollViewer.cs / 1 / FlowDocumentScrollViewer.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; // Object using System.Collections; // IEnumerator using System.Collections.ObjectModel; // ReadOnlyCollectionusing System.Printing; // WritingCompletedEventArgs using System.Security; // SecurityCritical using System.Windows.Annotations; // AnnotationService using System.Windows.Automation.Peers; // AutomationPeer using System.Windows.Data; // BindingOperations using System.Windows.Controls.Primitives; // IScrollInfo using System.Windows.Documents; // FlowDocument using System.Windows.Documents.Serialization; // WritingCompletedEventArgs using System.Windows.Input; // KeyEventArgs using System.Windows.Media; // ScaleTransform, VisualTreeHelper using System.Windows.Markup; // IAddChild using System.Windows.Threading; // Dispatcher using System.Windows.Xps; // XpsDocumentWriter using MS.Internal; // Invariant, DoubleUtil using MS.Internal.Annotations.Anchoring; using MS.Internal.Commands; // CommandHelpers using MS.Internal.Controls; // EmptyEnumerator using MS.Internal.Documents; // FindToolBar using MS.Internal.KnownBoxes; // BooleanBoxes using MS.Internal.AppModel; // IJournalState namespace System.Windows.Controls { /// /// The FlowDocumentScrollViewer displays a FlowDocument within a bottomless scrolling view; /// the content of the FlowDocument is displayed in a single column. /// This bottomless scrolling view is similar to the text display provided by web browsers /// and most applications today. /// [TemplatePart(Name = "PART_ContentHost", Type = typeof(ScrollViewer))] [TemplatePart(Name = "PART_FindToolBarHost", Type = typeof(Decorator))] [TemplatePart(Name = "PART_ToolBarHost", Type = typeof(Decorator))] [ContentProperty("Document")] public class FlowDocumentScrollViewer : Control, IAddChild, IServiceProvider, IJournalState { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Static Constructor /// static FlowDocumentScrollViewer() { DefaultStyleKeyProperty.OverrideMetadata( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(new ComponentResourceKey(typeof(PresentationUIStyleResources), "PUIFlowDocumentScrollViewer"))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(FlowDocumentScrollViewer)); CreateCommandBindings(); EventManager.RegisterClassHandler(typeof(FlowDocumentScrollViewer), RequestBringIntoViewEvent, new RequestBringIntoViewEventHandler(HandleRequestBringIntoView)); EventManager.RegisterClassHandler(typeof(FlowDocumentScrollViewer), Keyboard.KeyDownEvent, new KeyEventHandler(KeyDownHandler), true); } ////// Default Constructor /// public FlowDocumentScrollViewer() : base() { // Set data ID which will be used to identify annotations on content in this viewer AnnotationService.SetDataId(this, "FlowDocument"); } #endregion //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods ////// Called when the template tree has been created. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); // Initialize FindTooBar host. // If old FindToolBar is enabled, disable it first to ensure appropriate cleanup. if (FindToolBar != null) { ToggleFindToolBar(false); } _findToolBarHost = GetTemplateChild(_findToolBarHostTemplateName) as Decorator; // Initialize TooBar host. _toolBarHost = GetTemplateChild(_toolBarHostTemplateName) as Decorator; if (_toolBarHost != null) { _toolBarHost.Visibility = IsToolBarVisible ? Visibility.Visible : Visibility.Collapsed; } // Initialize ContentHost. // If old ContentHost is enabled, disable it first to ensure appropriate cleanup. if (_contentHost != null) { BindingOperations.ClearBinding(_contentHost, HorizontalScrollBarVisibilityProperty); BindingOperations.ClearBinding(_contentHost, VerticalScrollBarVisibilityProperty); _contentHost.ScrollChanged -= new ScrollChangedEventHandler(OnScrollChanged); RenderScope.Document = null; ClearValue(TextEditor.PageHeightProperty); _contentHost.Content = null; } _contentHost = GetTemplateChild(_contentHostTemplateName) as ScrollViewer; if (_contentHost != null) { if (_contentHost.Content != null) { throw new NotSupportedException(SR.Get(SRID.FlowDocumentScrollViewerMarkedAsContentHostMustHaveNoContent)); } _contentHost.ScrollChanged += new ScrollChangedEventHandler(OnScrollChanged); CreateTwoWayBinding(_contentHost, HorizontalScrollBarVisibilityProperty, "HorizontalScrollBarVisibility"); CreateTwoWayBinding(_contentHost, VerticalScrollBarVisibilityProperty, "VerticalScrollBarVisibility"); // Need to make ScrollViewer non-focusable, otherwise it will eat keyboard navigation from editor. _contentHost.Focusable = false; // Initialize the content of the ScrollViewer. _contentHost.Content = new FlowDocumentView(); RenderScope.Document = Document; } // Initialize TextEditor. AttachTextEditor(); // Apply the current zoom to the content host. ApplyZoom(); } ////// Invokes the Find Toolbar. This is analogous to the ApplicationCommands.Find. /// public void Find() { OnFindCommand(); } ////// 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(); } ////// Increases the current zoom. /// public void IncreaseZoom() { OnIncreaseZoomCommand(); } ////// Decreases the current zoom. /// public void DecreaseZoom() { OnDecreaseZoomCommand(); } #endregion Public Methods //-------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------- #region Public Properties ////// A Property representing a content of this FlowDocumentScrollViewer. /// public FlowDocument Document { get { return (FlowDocument)GetValue(DocumentProperty); } set { SetValue(DocumentProperty, value); } } ////// Text Selection (readonly) /// public TextSelection Selection { get { ITextSelection textSelection = null; FlowDocument flowDocument = Document; if (flowDocument != null) { textSelection = flowDocument.StructuralCache.TextContainer.TextSelection; } return textSelection as TextSelection; } } ////// The Zoom applied to all pages; this value is 100-based. /// public double Zoom { get { return (double)GetValue(ZoomProperty); } set { SetValue(ZoomProperty, value); } } ////// The maximum allowed value of the Zoom property. /// public double MaxZoom { get { return (double)GetValue(MaxZoomProperty); } set { SetValue(MaxZoomProperty, value); } } ////// The minimum allowed value of the Zoom property. /// public double MinZoom { get { return (double)GetValue(MinZoomProperty); } set { SetValue(MinZoomProperty, value); } } ////// The amount the Zoom property is incremented or decremented when /// the IncreaseZoom or DecreaseZoom command is executed. /// public double ZoomIncrement { get { return (double)GetValue(ZoomIncrementProperty); } set { SetValue(ZoomIncrementProperty, value); } } ////// Whether the viewer can increase the current zoom. /// public bool CanIncreaseZoom { get { return (bool)GetValue(CanIncreaseZoomProperty); } } ////// Whether the viewer can decrease the current zoom. /// public bool CanDecreaseZoom { get { return (bool)GetValue(CanDecreaseZoomProperty); } } ////// Whether text selection is enabled or disabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } ////// Whether the ToolBar is visible or not. /// public bool IsToolBarVisible { get { return (bool)GetValue(IsToolBarVisibleProperty); } set { SetValue(IsToolBarVisibleProperty, value); } } ////// Whether or not a horizontal scrollbar is shown. /// public ScrollBarVisibility HorizontalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); } set { SetValue(HorizontalScrollBarVisibilityProperty, value); } } ////// Whether or not a vertical scrollbar is shown. /// public ScrollBarVisibility VerticalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); } set { SetValue(VerticalScrollBarVisibilityProperty, value); } } #region Public Dynamic Properties ////// public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register( "Document", typeof(FlowDocument), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( null, new PropertyChangedCallback(DocumentChanged))); ////// /// public static readonly DependencyProperty ZoomProperty = FlowDocumentPageViewer.ZoomProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( 100d, new PropertyChangedCallback(ZoomChanged), new CoerceValueCallback(CoerceZoom))); ////// /// public static readonly DependencyProperty MaxZoomProperty = FlowDocumentPageViewer.MaxZoomProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( 200d, new PropertyChangedCallback(MaxZoomChanged), new CoerceValueCallback(CoerceMaxZoom))); ////// /// public static readonly DependencyProperty MinZoomProperty = FlowDocumentPageViewer.MinZoomProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( 80d, new PropertyChangedCallback(MinZoomChanged))); ////// /// public static readonly DependencyProperty ZoomIncrementProperty = FlowDocumentPageViewer.ZoomIncrementProperty.AddOwner( typeof(FlowDocumentScrollViewer)); ////// /// private static readonly DependencyPropertyKey CanIncreaseZoomPropertyKey = DependencyProperty.RegisterReadOnly( "CanIncreaseZoom", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); ////// /// public static readonly DependencyProperty CanIncreaseZoomProperty = CanIncreaseZoomPropertyKey.DependencyProperty; ////// /// private static readonly DependencyPropertyKey CanDecreaseZoomPropertyKey = DependencyProperty.RegisterReadOnly( "CanDecreaseZoom", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); ////// /// public static readonly DependencyProperty CanDecreaseZoomProperty = CanDecreaseZoomPropertyKey.DependencyProperty; ////// /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register( "IsSelectionEnabled", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( BooleanBoxes.TrueBox, new PropertyChangedCallback(IsSelectionEnabledChanged))); ////// /// public static readonly DependencyProperty IsToolBarVisibleProperty = DependencyProperty.Register( "IsToolBarVisible", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( BooleanBoxes.FalseBox, new PropertyChangedCallback(IsToolBarVisibleChanged))); ////// /// public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = ScrollViewer.HorizontalScrollBarVisibilityProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(ScrollBarVisibility.Auto)); ////// /// public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible)); #endregion Public Dynamic Properties #endregion Public Properties //------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// /// Called when print has been completed. /// protected virtual void OnPrintCompleted() { ClearPrintingState(); } ////// Handler for the Find command /// protected virtual void OnFindCommand() { if (CanShowFindToolBar) { // Toggle on the FindToolBar between visible and hidden state. ToggleFindToolBar(FindToolBar == null); } } ////// Handler for the Print command. /// protected virtual void OnPrintCommand() { #if !DONOTREFPRINTINGASMMETA XpsDocumentWriter docWriter; PrintDocumentImageableArea ia = null; FlowDocumentPaginator paginator; Thickness pagePadding; // Only one printing job is allowed. if (_printingState != null) { return; } // If the document is FlowDocument, do custom printing. Otherwise go through default path. if (Document != null) { // Show print dialog. docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia); if (docWriter != null && ia != null) { // Suspend layout on FlowDocumentView. if (RenderScope != null) { RenderScope.SuspendLayout(); } // Store the current state of the document in the PrintingState paginator = ((IDocumentPaginatorSource)Document).DocumentPaginator as FlowDocumentPaginator; _printingState = new FlowDocumentPrintingState(); _printingState.XpsDocumentWriter = docWriter; _printingState.PageSize = paginator.PageSize; _printingState.PagePadding = Document.PagePadding; _printingState.IsSelectionEnabled = IsSelectionEnabled; _printingState.ColumnWidth = Document.ColumnWidth; // Since _printingState value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); // Register for XpsDocumentWriter events. docWriter.WritingCompleted += new WritingCompletedEventHandler(HandlePrintCompleted); docWriter.WritingCancelled += new WritingCancelledEventHandler(HandlePrintCancelled); // Add PreviewCanExecute handler to have a chance to disable UI Commands during printing. if (_contentHost != null) { CommandManager.AddPreviewCanExecuteHandler(_contentHost, new CanExecuteRoutedEventHandler(PreviewCanExecuteRoutedEventHandler)); } // Disable TextSelection, if currently enabled. if (IsSelectionEnabled) { IsSelectionEnabled = false; } // Change the PageSize and PagePadding for the document to match the CanvasSize for the printer device. paginator.PageSize = new Size(ia.MediaSizeWidth, ia.MediaSizeHeight); pagePadding = Document.ComputePageMargin(); Document.PagePadding = new Thickness( Math.Max(ia.OriginWidth, pagePadding.Left), Math.Max(ia.OriginHeight, pagePadding.Top), Math.Max(ia.MediaSizeWidth - (ia.OriginWidth + ia.ExtentWidth), pagePadding.Right), Math.Max(ia.MediaSizeHeight - (ia.OriginHeight + ia.ExtentHeight), pagePadding.Bottom)); Document.ColumnWidth = double.PositiveInfinity; // Send DocumentPaginator to the printer. docWriter.WriteAsync(paginator); } else { OnPrintCompleted(); } } else { OnPrintCompleted(); } #endif // DONOTREFPRINTINGASMMETA } ////// Handler for the CancelPrint command. /// protected virtual void OnCancelPrintCommand() { #if !DONOTREFPRINTINGASMMETA if (_printingState != null) { _printingState.XpsDocumentWriter.CancelAsync(); } #endif // DONOTREFPRINTINGASMMETA } ////// Handler for the IncreaseZoom command. /// protected virtual void OnIncreaseZoomCommand() { // If can zoom in, increase zoom by the zoom increment value. if (CanIncreaseZoom) { Zoom = Math.Min(Zoom + ZoomIncrement, MaxZoom); } } ////// Handler for the DecreaseZoom command. /// protected virtual void OnDecreaseZoomCommand() { // If can zoom out, decrease zoom by the zoom increment value. if (CanDecreaseZoom) { Zoom = Math.Max(Zoom - ZoomIncrement, MinZoom); } } ////// This is the method that responds to the KeyDown event. /// /// Event arguments ////// Critical: get_SearchUp is defined in a non-APTCA assembly. /// TreatAsSafe: call to get_SearchUp does not entail any risk. /// [SecurityCritical, SecurityTreatAsSafe] protected override void OnKeyDown(KeyEventArgs e) { if (e.Handled) { return; } switch (e.Key) { // Esc -- Close FindToolBar case Key.Escape: if (FindToolBar != null) { ToggleFindToolBar(false); e.Handled = true; } break; // F3 -- Invoke Find case Key.F3: if (CanShowFindToolBar) { if (FindToolBar != null) { // If the Shift key is also pressed, then search up. FindToolBar.SearchUp = ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift); OnFindInvoked(this, EventArgs.Empty); } else { // Make the FindToolBar visible ToggleFindToolBar(true); } e.Handled = true; } break; } // If not handled, do default handling. if (!e.Handled) { base.OnKeyDown(e); } } ////// Mouse wheel rotation handler. /// /// MouseWheelEventArgs protected override void OnMouseWheel(MouseWheelEventArgs e) { if (e.Handled) { return; } if (_contentHost != null) { //Press Ctrl and scroll mouse wheel will zoom in/out the document if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) { // If can zoom in, increase zoom by the zoom increment value. if (e.Delta > 0 && CanIncreaseZoom) { Zoom = Math.Min(Zoom + ZoomIncrement, MaxZoom); } else if (e.Delta < 0 && CanDecreaseZoom) { Zoom = Math.Max(Zoom - ZoomIncrement, MinZoom); } } else { if (e.Delta < 0) { _contentHost.LineDown(); } else { _contentHost.LineUp(); } } e.Handled = true; } // If not handled, do default handling. if (!e.Handled) { base.OnMouseWheel(e); } } ////// Called when ContextMenuOpening is raised on this element. /// /// Event arguments protected override void OnContextMenuOpening(ContextMenuEventArgs e) { base.OnContextMenuOpening(e); DocumentViewerHelper.OnContextMenuOpening(Document, this, e); } ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new FlowDocumentScrollViewerAutomationPeer(this); } #endregion Protected Methods //------------------------------------------------------------------- // // Protected Properties // //------------------------------------------------------------------- #region Protected Properties ///) /// /// Returns enumerator to logical children. /// protected internal override IEnumerator LogicalChildren { get { if (HasLogicalChildren && Document != null) { return new SingleChildEnumerator(Document); } return EmptyEnumerator.Instance; } } #endregion Protected Properties //------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------- #region Internal Methods ////// Allows FrameworkElement to augment the EventRoute. /// internal override bool BuildRouteCore(EventRoute route, RoutedEventArgs args) { // If FlowDocumentScrollViewer is used as embedded viewer (in FlowDocumentReader), // it is not part of logical tree, so default logic in FE to re-add logical // tree from branched node does not work here. // But before FlowDocumentScrollViewer is added to the event route, logical // ancestors up to Document need to be added to the route. Otherwise // content will not have a chance to react to events first. // This breaks navigation cursor management logic, because TextEditor attached // to FlowDocumentScrollViewer handles those events first. DependencyObject document = this.Document as DependencyObject; if (document != null && LogicalTreeHelper.GetParent(document) != this) { DependencyObject branchNode = route.PeekBranchNode() as DependencyObject; if (branchNode != null && DocumentViewerHelper.IsLogicalDescendent(branchNode, document)) { // Add intermediate ContentElements to the route. FrameworkElement.AddIntermediateElementsToRoute( LogicalTreeHelper.GetParent(document), route, args, LogicalTreeHelper.GetParent(branchNode)); } } return base.BuildRouteCore(route, args); } ////// Bring specified content position into view. /// internal object BringContentPositionIntoView(object arg) { ITextPointer contentPosition = arg as ITextPointer; if (contentPosition != null) { ITextView textView = GetTextView(); if (textView != null && textView.IsValid && textView.RenderScope is IScrollInfo && contentPosition.TextContainer == textView.TextContainer) { if (textView.Contains(contentPosition)) { Rect rect = textView.GetRectangleFromTextPosition(contentPosition); if (rect != Rect.Empty) { IScrollInfo isi = (IScrollInfo)textView.RenderScope; isi.SetVerticalOffset(rect.Top + isi.VerticalOffset); } } else { // Wait until ContentPosition in in the view. Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(BringContentPositionIntoView), contentPosition); } } } return null; } #endregion Internal Methods //------------------------------------------------------------------- // // Internal Properties // //-------------------------------------------------------------------- #region Internal Properties ////// Access to the ScrollViewer in FlowDocumentScrollViewer style /// internal ScrollViewer ScrollViewer { get { return _contentHost; } } ////// Whether FindToolBar can be enabled. /// internal bool CanShowFindToolBar { get { return (_findToolBarHost != null && Document != null && _textEditor != null); } } ////// Whether currently printing the content. /// internal bool IsPrinting { get { return (_printingState != null); } } ////// Returns textpointer for upper left corner of content. /// internal TextPointer ContentPosition { get { TextPointer contentPosition = null; ITextView textView = GetTextView(); if (textView != null && textView.IsValid && textView.RenderScope is IScrollInfo) { contentPosition = textView.GetTextPositionFromPoint(new Point(), true) as TextPointer; } return contentPosition; } } #endregion Internal Properties //-------------------------------------------------------------------- // // Private Methods // //------------------------------------------------------------------- #region Private Methods ////// Enables/disables the FindToolBar. /// /// Whether to enable/disable FindToolBar. private void ToggleFindToolBar(bool enable) { Invariant.Assert(enable == (FindToolBar == null)); DocumentViewerHelper.ToggleFindToolBar(_findToolBarHost, new EventHandler(OnFindInvoked), enable); // FindToolBar is embedded inside the toolbar, so event if the ToolBar is not visible // it needs to be shown when showing FindToolBar. if (!IsToolBarVisible && _toolBarHost != null) { _toolBarHost.Visibility = enable ? Visibility.Visible : Visibility.Collapsed; } } ////// Apply the zoom value to the render scope. /// private void ApplyZoom() { if (RenderScope != null) { RenderScope.LayoutTransform = new ScaleTransform(Zoom / 100, Zoom / 100); } } ////// Attach TextEditor to Document, if supports text. /// private void AttachTextEditor() { ITextView textView; //get annotation service and state so we can restore it AnnotationService service = AnnotationService.GetService(this); bool serviceOldState = false; //disable the service if enabled if ((service != null) && service.IsEnabled) { serviceOldState = true; service.Disable(); } // This method is called when Document is changing or control template // is replaced, so need to drop old TextEditor data. if (_textEditor != null) { _textEditor.TextContainer.TextView = null; _textEditor.OnDetach(); _textEditor = null; } // 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). if (IsSelectionEnabled && Document != null && RenderScope != null && Document.StructuralCache.TextContainer.TextSelection == null) { textView = GetTextView(); _textEditor = new TextEditor(Document.StructuralCache.TextContainer, this, false); _textEditor.IsReadOnly = !IsEditingEnabled; _textEditor.TextView = textView; _textEditor.TextContainer.TextView = textView; } //restore AnnotationsService state if ((service != null) && serviceOldState) { service.Enable(service.Store); } // If TextEditor is not enabled, FindToolBar cannot be visible. if (_textEditor == null && FindToolBar != null) { ToggleFindToolBar(false); } } ////// Handler for ScrollViewer's OnScrollChanged event. /// private void OnScrollChanged(object sender, ScrollChangedEventArgs e) { if (e.OriginalSource == ScrollViewer) { // If ScrollViewer.ViewportHeight has been changed, TextEditor.PageHeight must be updated if (!DoubleUtil.IsZero(e.ViewportHeightChange)) { SetValue(TextEditor.PageHeightProperty, e.ViewportHeight); } } } ////// Called when WritingCompleted event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCompleted(object sender, WritingCompletedEventArgs e) { OnPrintCompleted(); } ////// Called when WritingCancelled event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCancelled(object sender, WritingCancelledEventArgs e) { ClearPrintingState(); } ////// Clear printing state. /// private void ClearPrintingState() { #if !DONOTREFPRINTINGASMMETA if (_printingState != null) { // Resume layout on FlowDocumentView. if (RenderScope != null) { RenderScope.ResumeLayout(); } // Enable TextSelection, if it was previously enabled. if (_printingState.IsSelectionEnabled) { IsSelectionEnabled = true; } // Remove PreviewCanExecute handler (added when Print command was executed). if (_contentHost != null) { CommandManager.RemovePreviewCanExecuteHandler(_contentHost, new CanExecuteRoutedEventHandler(PreviewCanExecuteRoutedEventHandler)); } // Unregister for XpsDocumentWriter events. _printingState.XpsDocumentWriter.WritingCompleted -= new WritingCompletedEventHandler(HandlePrintCompleted); _printingState.XpsDocumentWriter.WritingCancelled -= new WritingCancelledEventHandler(HandlePrintCancelled); // Restore old page metrics on FlowDocument. Document.PagePadding = _printingState.PagePadding; Document.ColumnWidth = _printingState.ColumnWidth; ((IDocumentPaginatorSource)Document).DocumentPaginator.PageSize = _printingState.PageSize; _printingState = null; // Since _documentWriter value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } #endif // DONOTREFPRINTINGASMMETA } ////// Makes sure the target is visible in the client area. /// /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private void HandleRequestBringIntoView(RequestBringIntoViewEventArgs args) { DependencyObject child; DependencyObject document; IContentHost ich; ReadOnlyCollectionrects; UIElement targetUIElement; Rect targetRect = Rect.Empty; if (args != null && args.TargetObject != null && Document != null) { document = Document; // If the passed in object is a logical child of FlowDocumentScrollViewer'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. if (args.TargetObject == document) { if (_contentHost != null) { _contentHost.ScrollToHome(); } args.Handled = true; // Mark the event as handled. } else if (args.TargetObject is UIElement) { targetUIElement = (UIElement)args.TargetObject; // Since entire content of FlowDocument is represented by // bottomless page, the target has to be connected to visual tree. // Otherwise, it is not descendant of FlowDocument. if (RenderScope != null && RenderScope.IsAncestorOf(targetUIElement)) { targetRect = args.TargetRect; if (targetRect.IsEmpty) { targetRect = new Rect(targetUIElement.RenderSize); } targetRect = MakeVisible((IScrollInfo)RenderScope, targetUIElement, targetRect); if(!targetRect.IsEmpty) { GeneralTransform t = RenderScope.TransformToAncestor(this); targetRect = t.TransformBounds(targetRect); } args.Handled = true; // Mark the event as handled. } } else if (args.TargetObject is ContentElement) { // Verify if TargetObject is in fact a child of Document. child = args.TargetObject; while (child != null && child != document) { child = LogicalTreeHelper.GetParent(child); } if (child != null) { ich = GetIContentHost(); if (ich != null) { // Get the position of the content. rects = ich.GetRectangles((ContentElement)args.TargetObject); if (rects.Count > 0) { targetRect = MakeVisible((IScrollInfo)RenderScope, (Visual)ich, rects[0]); if(!targetRect.IsEmpty) { GeneralTransform t = RenderScope.TransformToAncestor(this); targetRect = t.TransformBounds(targetRect); } } } 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); } } } } /// /// The Document has changed and needs to be updated. /// private void DocumentChanged(FlowDocument oldDocument, FlowDocument newDocument) { // Use TextSelection to determine whether the new document belongs to another // control or not. if (newDocument != null && newDocument.StructuralCache.TextContainer != null && newDocument.StructuralCache.TextContainer.TextSelection != null) { throw new ArgumentException(SR.Get(SRID.FlowDocumentScrollViewerDocumentBelongsToAnotherFlowDocumentScrollViewerAlready)); } // Cleanup state associated with the old document. if (oldDocument != null) { // If Document was added to logical tree of FlowDocumentScrollViewer before, remove it. if (_documentAsLogicalChild) { RemoveLogicalChild(oldDocument); } // Remove the document from the ContentHost. if (RenderScope != null) { RenderScope.Document = null; } oldDocument.ClearValue(PathNode.HiddenParentProperty); oldDocument.StructuralCache.ClearUpdateInfo(true); } // If FlowDocumentScrollViewer was created through style, then do not modify // the logical tree. Instead, set "core parent" for the Document. if (newDocument != null && LogicalTreeHelper.GetParent(newDocument) != null) { // Set the "core parent" back to us. ContentOperations.SetParent(newDocument, this); _documentAsLogicalChild = false; } else { _documentAsLogicalChild = true; } // Initialize state associated with the new document. if (newDocument != null) { // Set the document on the ContentHost. if (RenderScope != null) { RenderScope.Document = newDocument; } // If Document should be part of FlowDocumentScrollViewer's logical tree, add it. if (_documentAsLogicalChild) { AddLogicalChild(newDocument); } // Set the hidden parent of the document newDocument.SetValue(PathNode.HiddenParentProperty, this); newDocument.StructuralCache.ClearUpdateInfo(true); } // Attach TextEditor, if content supports it. This method will also // detach TextEditor from old content. AttachTextEditor(); // Update the toolbar with our current document state. if (!CanShowFindToolBar) { // Disable FindToolBar, if the content does not support it. if (FindToolBar != null) { ToggleFindToolBar(false); } } // Document is also represented as Automation child. Need to invalidate peer to force update. FlowDocumentScrollViewerAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as FlowDocumentScrollViewerAutomationPeer; if (peer != null) { peer.InvalidatePeer(); } } ////// Retrieves ITextView associated with the content. /// NOTE: Not retrieved from TextEditor, because it may not exist in some cases. /// private ITextView GetTextView() { ITextView textView = null; if (RenderScope is IServiceProvider) { textView = (ITextView)((IServiceProvider)RenderScope).GetService(typeof(ITextView)); } return textView; } ////// Retrieves IContentHost associated with the content. /// private IContentHost GetIContentHost() { IContentHost ich = null; if (RenderScope != null && VisualTreeHelper.GetChildrenCount(RenderScope) > 0) { ich = VisualTreeHelper.GetChild(RenderScope, 0) as IContentHost; } return ich; } ////// Create two way property binding. /// private void CreateTwoWayBinding(FrameworkElement fe, DependencyProperty dp, string propertyPath) { Binding binding = new Binding(propertyPath); binding.Mode = BindingMode.TwoWay; binding.Source = this; fe.SetBinding(dp, binding); } #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); // Create private commands to control content scrolling. It is required because TextEditor binds // those Keys to its own commands (caret navigation), which are useless in viewing scenarios. // Since following commands are private, it is OK to pass String.Empty as descriptive text for the command. _commandLineDown = new RoutedUICommand(String.Empty, "FDSV_LineDown", typeof(FlowDocumentScrollViewer)); _commandLineUp = new RoutedUICommand(String.Empty, "FDSV_LineUp", typeof(FlowDocumentScrollViewer)); _commandLineLeft = new RoutedUICommand(String.Empty, "FDSV_LineLeft", typeof(FlowDocumentScrollViewer)); _commandLineRight = new RoutedUICommand(String.Empty, "FDSV_LineRight", typeof(FlowDocumentScrollViewer)); // Register editing command handlers TextEditor.RegisterCommandHandlers(typeof(FlowDocumentScrollViewer), /*acceptsRichContent:*/true, /*readOnly:*/!IsEditingEnabled, /*registerEventListeners*/true); // Command: ApplicationCommands.Find CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), ApplicationCommands.Find, executedHandler, canExecuteHandler); // Command: ApplicationCommands.Print CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), ApplicationCommands.Print, executedHandler, canExecuteHandler); // Command: ApplicationCommands.CancelPrint CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), ApplicationCommands.CancelPrint, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.PreviousPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.PreviousPage, executedHandler, canExecuteHandler, Key.PageUp); // Command: NavigationCommands.NextPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.NextPage, executedHandler, canExecuteHandler, Key.PageDown); // Command: NavigationCommands.FirstPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.FirstPage, executedHandler, canExecuteHandler, new KeyGesture(Key.Home), new KeyGesture(Key.Home, ModifierKeys.Control)); // Command: NavigationCommands.LastPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.LastPage, executedHandler, canExecuteHandler, new KeyGesture(Key.End), new KeyGesture(Key.End, ModifierKeys.Control)); // Command: NavigationCommands.IncreaseZoom CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.IncreaseZoom, executedHandler, canExecuteHandler, new KeyGesture(Key.OemPlus, ModifierKeys.Control)); // Command: NavigationCommands.DecreaseZoom CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.DecreaseZoom, executedHandler, canExecuteHandler, new KeyGesture(Key.OemMinus, ModifierKeys.Control)); // Command: FDSV_LineDown CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineDown, executedHandler, canExecuteHandler, Key.Down); // Command: FDSV_LineUp CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineUp, executedHandler, canExecuteHandler, Key.Up); // Command: FDSV_LineLeft CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineLeft, executedHandler, canExecuteHandler, Key.Left); // Command: FDSV_LineRight CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineRight, executedHandler, canExecuteHandler, Key.Right); } ////// Central handler for CanExecuteRouted events fired by Commands directed at FlowDocumentScrollViewer. /// /// The target of this Command, expected to be FlowDocumentScrollViewer /// The event arguments for this event. private static void CanExecuteRoutedEventHandler(object target, CanExecuteRoutedEventArgs args) { FlowDocumentScrollViewer viewer = target as FlowDocumentScrollViewer; Invariant.Assert(viewer != null, "Target of QueryEnabledEvent must be FlowDocumentScrollViewer."); Invariant.Assert(args != null, "args cannot be null."); // FlowDocumentScrollViewer is capable of execution of the majority of its commands. // Special rules: // a) during printing only CancelPrint is enabled. // b) Find command is enabled only when FindToolBar is enabled. // c) Print command is enabled when Document is attached. // d) CancelPrint command is enabled only during printing. if (viewer._printingState == null) { if (args.Command == ApplicationCommands.Find) { args.CanExecute = viewer.CanShowFindToolBar; } else if (args.Command == ApplicationCommands.Print) { args.CanExecute = (viewer.Document != null); } else if (args.Command == ApplicationCommands.CancelPrint) { args.CanExecute = false; } else { args.CanExecute = true; } } else { args.CanExecute = (args.Command == ApplicationCommands.CancelPrint); } } ////// Central handler for all ExecutedRoutedEvent fired by Commands directed at FlowDocumentScrollViewer. /// /// The target of this Command, expected to be FlowDocumentScrollViewer. /// The event arguments associated with this event. private static void ExecutedRoutedEventHandler(object target, ExecutedRoutedEventArgs args) { FlowDocumentScrollViewer viewer = target as FlowDocumentScrollViewer; Invariant.Assert(viewer != null, "Target of ExecuteEvent must be FlowDocumentScrollViewer."); 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 == ApplicationCommands.Find) { viewer.OnFindCommand(); } else if (args.Command == ApplicationCommands.Print) { viewer.OnPrintCommand(); } else if (args.Command == ApplicationCommands.CancelPrint) { viewer.OnCancelPrintCommand(); } else if (args.Command == NavigationCommands.IncreaseZoom) { viewer.OnIncreaseZoomCommand(); } else if (args.Command == NavigationCommands.DecreaseZoom) { viewer.OnDecreaseZoomCommand(); } else if (args.Command == _commandLineDown) { if (viewer._contentHost != null) { viewer._contentHost.LineDown(); } } else if (args.Command == _commandLineUp) { if (viewer._contentHost != null) { viewer._contentHost.LineUp(); } } else if (args.Command == _commandLineLeft) { if (viewer._contentHost != null) { viewer._contentHost.LineLeft(); } } else if (args.Command == _commandLineRight) { if (viewer._contentHost != null) { viewer._contentHost.LineRight(); } } else if (args.Command == NavigationCommands.NextPage) { if (viewer._contentHost != null) { viewer._contentHost.PageDown(); } } else if (args.Command == NavigationCommands.PreviousPage) { if (viewer._contentHost != null) { viewer._contentHost.PageUp(); } } else if (args.Command == NavigationCommands.FirstPage) { if (viewer._contentHost != null) { viewer._contentHost.ScrollToHome(); } } else if (args.Command == NavigationCommands.LastPage) { if (viewer._contentHost != null) { viewer._contentHost.ScrollToEnd(); } } else { Invariant.Assert(false, "Command not handled in ExecutedRoutedEventHandler."); } } ////// Invoked when the "Find" button in the Find Toolbar is clicked. /// This method invokes the actual Find process. /// /// The object that sent this event /// The Click Events associated with this event private void OnFindInvoked(object sender, EventArgs e) { ITextRange findResult; FindToolBar findToolBar = FindToolBar; if (findToolBar != null && _textEditor != null) { // In order to show current text selection TextEditor requires Focus to be set on the UIScope. // If there embedded controls, it may happen that embedded control currently has focus and find // was invoked through hotkeys. To support this case we manually move focus to the appropriate element. Focus(); findResult = DocumentViewerHelper.Find(findToolBar, _textEditor, _textEditor.TextView, _textEditor.TextView); // If we found something, TextEditor will bring the selection into view. // It is possible, because RenderScope is inside ScrollViewer. // If we did not find anything, alert the user. if ((findResult == null) || findResult.IsEmpty) { DocumentViewerHelper.ShowFindUnsuccessfulMessage(findToolBar); } } } ////// Disable commands on DocumentViewerBase when this printing is in progress. /// private void PreviewCanExecuteRoutedEventHandler(object target, CanExecuteRoutedEventArgs args) { ScrollViewer sv = target as ScrollViewer; Invariant.Assert(sv != null, "Target of PreviewCanExecuteRoutedEventHandler must be ScrollViewer."); Invariant.Assert(args != null, "args cannot be null."); // Disable UI commands, if printing is in progress. if (_printingState != null) { args.CanExecute = false; args.Handled = true; } } ////// Called when a key event occurs. /// private static void KeyDownHandler(object sender, KeyEventArgs e) { DocumentViewerHelper.KeyDownHelper(e, ((FlowDocumentScrollViewer)sender)._findToolBarHost); } #endregion Commands #region Static Methods ////// Wrapper around IScrollInfo.MakeVisible /// /// The IScrollInfo to call MakeVisible on /// visual parameter for call to MakeVisible /// rectangle parameter for call to MakeVisible ///Rectangle representing visible portion of visual relative to scrollInfo's viewport private static Rect MakeVisible(IScrollInfo scrollInfo, Visual visual, Rect rectangle) { // Rect result; if(scrollInfo.GetType() == typeof(System.Windows.Controls.ScrollContentPresenter)) { result = ((ScrollContentPresenter)scrollInfo).MakeVisible(visual, rectangle, false); } else { result = scrollInfo.MakeVisible(visual, rectangle); } return result; } ////// The Document has changed and needs to be updated. /// private static void DocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); ((FlowDocumentScrollViewer)d).DocumentChanged((FlowDocument)e.OldValue, (FlowDocument)e.NewValue); // Since Document state is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } ////// The Zoom has changed and needs to be updated. /// private static void ZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; if (!DoubleUtil.AreClose((double)e.OldValue, (double)e.NewValue)) { // If zoom has been changed, CanIncrease/DecreaseZoom property need to be updated. viewer.SetValue(CanIncreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.GreaterThan(viewer.MaxZoom, viewer.Zoom))); viewer.SetValue(CanDecreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.LessThan(viewer.MinZoom, viewer.Zoom))); // Apply the new zoom value. viewer.ApplyZoom(); } } ////// Coerce Zoom with Max/MinZoom, MinZoom works as the baseline. /// private static object CoerceZoom(DependencyObject d, object value) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; double zoom = (double)value; double maxZoom = viewer.MaxZoom; if (DoubleUtil.LessThan(maxZoom, zoom)) { return maxZoom; } double minZoom = viewer.MinZoom; if (DoubleUtil.GreaterThan(minZoom, zoom)) { return minZoom; } return value; } ////// The MaxZoom has changed and needs to be updated. /// private static void MaxZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; viewer.CoerceValue(ZoomProperty); viewer.SetValue(CanIncreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.GreaterThan(viewer.MaxZoom, viewer.Zoom))); } ////// MaxZoom need to be coerced if MinZoom > MaxZoom /// private static object CoerceMaxZoom(DependencyObject d, object value) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; double min = viewer.MinZoom; return ((double)value < min) ? min : value; } ////// The MinZoom has changed and needs to be updated. /// private static void MinZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; viewer.CoerceValue(MaxZoomProperty); viewer.CoerceValue(ZoomProperty); viewer.SetValue(CanDecreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.LessThan(viewer.MinZoom, viewer.Zoom))); } ////// Validate Zoom, MaxZoom, MinZoom and ZoomIncrement value. /// /// Value to validate. ///True if the value is valid, false otherwise. private static bool ZoomValidateValue(object o) { double value = (double)o; return (!Double.IsNaN(value) && !Double.IsInfinity(value) && DoubleUtil.GreaterThan(value, 0d)); } ////// Called from the event handler to make sure the target is visible in the client area. /// /// 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 FlowDocumentScrollViewer) { ((FlowDocumentScrollViewer)sender).HandleRequestBringIntoView(args); } } ////// The IsSelectionEnabled has changed and needs to be updated. /// private static void IsSelectionEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; viewer.AttachTextEditor(); } ////// The IsToolBarVisible has changed and needs to be updated. /// private static void IsToolBarVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; if (viewer._toolBarHost != null) { viewer._toolBarHost.Visibility = (bool)e.NewValue ? Visibility.Visible : Visibility.Collapsed; } } #endregion Static Methods #endregion Private Methods //-------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------- #region Private Properties ////// Returns FindToolBar, if enabled. /// private FindToolBar FindToolBar { get { return (_findToolBarHost != null) ? _findToolBarHost.Child as FindToolBar : null; } } ////// Content of the ScrollViewer which is treated as RenderScope for the TextEditor. /// private FlowDocumentView RenderScope { get { return (_contentHost != null) ? _contentHost.Content as FlowDocumentView : null; } } #endregion Private Properties //------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------- #region Private Fields private TextEditor _textEditor; // Text editor (enables text selection) private Decorator _findToolBarHost; // Host for FindToolBar private Decorator _toolBarHost; // Host for ToolBar private ScrollViewer _contentHost; // Host for content viewer private bool _documentAsLogicalChild; // Is Document part of logical tree private FlowDocumentPrintingState _printingState; // Printing state private const string _contentHostTemplateName = "PART_ContentHost"; // Name for ContentHost private const string _findToolBarHostTemplateName = "PART_FindToolBarHost"; // Name for the Find ToolBar host private const string _toolBarHostTemplateName = "PART_ToolBarHost"; // Name for the ToolBar host private static bool IsEditingEnabled = false; // A flag enabling text editing within the viewer // accessible only through reflection. private static RoutedUICommand _commandLineDown; // Private LineDown command private static RoutedUICommand _commandLineUp; // Private LineUp command private static RoutedUICommand _commandLineLeft; // Private LineLeft command private static RoutedUICommand _commandLineRight; // Private LineRight command #endregion Private Fields //-------------------------------------------------------------------- // // IAddChild Members // //------------------------------------------------------------------- #region IAddChild Members ////// Called to add the object as a Child. /// /// Object to add as a child. ///FlowDocumentScrollViewer only supports a single child of type IDocumentPaginator. 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 ArgumentException(SR.Get(SRID.FlowDocumentScrollViewerCanHaveOnlyOneChild)); } if (!(value is FlowDocument)) { throw new ArgumentException(SR.Get(SRID.UnexpectedParameterType, value.GetType(), typeof(FlowDocument)), "value"); } Document = value as FlowDocument; } ////// Called when text appears under the tag in markup /// /// Text to add to the Object. ///FlowDocumentScrollViewer does not support Text children. void IAddChild.AddText(string text) { XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this); } #endregion IAddChild Members //-------------------------------------------------------------------- // // IServiceProvider Members // //-------------------------------------------------------------------- #region IServiceProvider Members ////// 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 // (2) TextContainer if (serviceType == typeof(ITextView)) { service = GetTextView(); } else if (serviceType == typeof(TextContainer) || serviceType == typeof(ITextContainer)) { if (Document != null) { service = ((IServiceProvider)Document).GetService(serviceType); } } return service; } #endregion IServiceProvider Members //------------------------------------------------------------------- // // IJournalState Members // //-------------------------------------------------------------------- #region IJournalState Members [Serializable] private class JournalState : CustomJournalStateInternal { public JournalState(int contentPosition, LogicalDirection contentPositionDirection, double zoom) { ContentPosition = contentPosition; ContentPositionDirection = contentPositionDirection; Zoom = zoom; } public int ContentPosition; public LogicalDirection ContentPositionDirection; public double Zoom; } ////// CustomJournalStateInternal IJournalState.GetJournalState(JournalReason journalReason) { int cp = -1; LogicalDirection cpDirection = LogicalDirection.Forward; TextPointer contentPosition = ContentPosition; if (contentPosition != null) { cp = contentPosition.Offset; cpDirection = contentPosition.LogicalDirection; } return new JournalState(cp, cpDirection, Zoom); } ////// /// void IJournalState.RestoreJournalState(CustomJournalStateInternal state) { JournalState viewerState = state as JournalState; if (state != null) { Zoom = viewerState.Zoom; if (viewerState.ContentPosition != -1) { FlowDocument document = Document; if (document != null) { TextContainer textContainer = document.StructuralCache.TextContainer; if (viewerState.ContentPosition <= textContainer.SymbolCount) { TextPointer contentPosition = textContainer.CreatePointerAtOffset(viewerState.ContentPosition, viewerState.ContentPositionDirection); // This need be called because the UI may not be ready when Contentposition is set. Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(BringContentPositionIntoView), contentPosition); } } } } } #endregion IJournalState Members //------------------------------------------------------------------- // // DTypeThemeStyleKey // //------------------------------------------------------------------- #region DTypeThemeStyleKey ////// /// Returns the DependencyObjectType for the registered ThemeStyleKey's default /// value. Controls will override this method to return approriate types /// internal override DependencyObjectType DTypeThemeStyleKey { get { return _dType; } } private static DependencyObjectType _dType; #endregion DTypeThemeStyleKey } } // 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. // //--------------------------------------------------------------------------- using System; // Object using System.Collections; // IEnumerator using System.Collections.ObjectModel; // ReadOnlyCollectionusing System.Printing; // WritingCompletedEventArgs using System.Security; // SecurityCritical using System.Windows.Annotations; // AnnotationService using System.Windows.Automation.Peers; // AutomationPeer using System.Windows.Data; // BindingOperations using System.Windows.Controls.Primitives; // IScrollInfo using System.Windows.Documents; // FlowDocument using System.Windows.Documents.Serialization; // WritingCompletedEventArgs using System.Windows.Input; // KeyEventArgs using System.Windows.Media; // ScaleTransform, VisualTreeHelper using System.Windows.Markup; // IAddChild using System.Windows.Threading; // Dispatcher using System.Windows.Xps; // XpsDocumentWriter using MS.Internal; // Invariant, DoubleUtil using MS.Internal.Annotations.Anchoring; using MS.Internal.Commands; // CommandHelpers using MS.Internal.Controls; // EmptyEnumerator using MS.Internal.Documents; // FindToolBar using MS.Internal.KnownBoxes; // BooleanBoxes using MS.Internal.AppModel; // IJournalState namespace System.Windows.Controls { /// /// The FlowDocumentScrollViewer displays a FlowDocument within a bottomless scrolling view; /// the content of the FlowDocument is displayed in a single column. /// This bottomless scrolling view is similar to the text display provided by web browsers /// and most applications today. /// [TemplatePart(Name = "PART_ContentHost", Type = typeof(ScrollViewer))] [TemplatePart(Name = "PART_FindToolBarHost", Type = typeof(Decorator))] [TemplatePart(Name = "PART_ToolBarHost", Type = typeof(Decorator))] [ContentProperty("Document")] public class FlowDocumentScrollViewer : Control, IAddChild, IServiceProvider, IJournalState { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Static Constructor /// static FlowDocumentScrollViewer() { DefaultStyleKeyProperty.OverrideMetadata( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(new ComponentResourceKey(typeof(PresentationUIStyleResources), "PUIFlowDocumentScrollViewer"))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(FlowDocumentScrollViewer)); CreateCommandBindings(); EventManager.RegisterClassHandler(typeof(FlowDocumentScrollViewer), RequestBringIntoViewEvent, new RequestBringIntoViewEventHandler(HandleRequestBringIntoView)); EventManager.RegisterClassHandler(typeof(FlowDocumentScrollViewer), Keyboard.KeyDownEvent, new KeyEventHandler(KeyDownHandler), true); } ////// Default Constructor /// public FlowDocumentScrollViewer() : base() { // Set data ID which will be used to identify annotations on content in this viewer AnnotationService.SetDataId(this, "FlowDocument"); } #endregion //-------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------- #region Public Methods ////// Called when the template tree has been created. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); // Initialize FindTooBar host. // If old FindToolBar is enabled, disable it first to ensure appropriate cleanup. if (FindToolBar != null) { ToggleFindToolBar(false); } _findToolBarHost = GetTemplateChild(_findToolBarHostTemplateName) as Decorator; // Initialize TooBar host. _toolBarHost = GetTemplateChild(_toolBarHostTemplateName) as Decorator; if (_toolBarHost != null) { _toolBarHost.Visibility = IsToolBarVisible ? Visibility.Visible : Visibility.Collapsed; } // Initialize ContentHost. // If old ContentHost is enabled, disable it first to ensure appropriate cleanup. if (_contentHost != null) { BindingOperations.ClearBinding(_contentHost, HorizontalScrollBarVisibilityProperty); BindingOperations.ClearBinding(_contentHost, VerticalScrollBarVisibilityProperty); _contentHost.ScrollChanged -= new ScrollChangedEventHandler(OnScrollChanged); RenderScope.Document = null; ClearValue(TextEditor.PageHeightProperty); _contentHost.Content = null; } _contentHost = GetTemplateChild(_contentHostTemplateName) as ScrollViewer; if (_contentHost != null) { if (_contentHost.Content != null) { throw new NotSupportedException(SR.Get(SRID.FlowDocumentScrollViewerMarkedAsContentHostMustHaveNoContent)); } _contentHost.ScrollChanged += new ScrollChangedEventHandler(OnScrollChanged); CreateTwoWayBinding(_contentHost, HorizontalScrollBarVisibilityProperty, "HorizontalScrollBarVisibility"); CreateTwoWayBinding(_contentHost, VerticalScrollBarVisibilityProperty, "VerticalScrollBarVisibility"); // Need to make ScrollViewer non-focusable, otherwise it will eat keyboard navigation from editor. _contentHost.Focusable = false; // Initialize the content of the ScrollViewer. _contentHost.Content = new FlowDocumentView(); RenderScope.Document = Document; } // Initialize TextEditor. AttachTextEditor(); // Apply the current zoom to the content host. ApplyZoom(); } ////// Invokes the Find Toolbar. This is analogous to the ApplicationCommands.Find. /// public void Find() { OnFindCommand(); } ////// 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(); } ////// Increases the current zoom. /// public void IncreaseZoom() { OnIncreaseZoomCommand(); } ////// Decreases the current zoom. /// public void DecreaseZoom() { OnDecreaseZoomCommand(); } #endregion Public Methods //-------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------- #region Public Properties ////// A Property representing a content of this FlowDocumentScrollViewer. /// public FlowDocument Document { get { return (FlowDocument)GetValue(DocumentProperty); } set { SetValue(DocumentProperty, value); } } ////// Text Selection (readonly) /// public TextSelection Selection { get { ITextSelection textSelection = null; FlowDocument flowDocument = Document; if (flowDocument != null) { textSelection = flowDocument.StructuralCache.TextContainer.TextSelection; } return textSelection as TextSelection; } } ////// The Zoom applied to all pages; this value is 100-based. /// public double Zoom { get { return (double)GetValue(ZoomProperty); } set { SetValue(ZoomProperty, value); } } ////// The maximum allowed value of the Zoom property. /// public double MaxZoom { get { return (double)GetValue(MaxZoomProperty); } set { SetValue(MaxZoomProperty, value); } } ////// The minimum allowed value of the Zoom property. /// public double MinZoom { get { return (double)GetValue(MinZoomProperty); } set { SetValue(MinZoomProperty, value); } } ////// The amount the Zoom property is incremented or decremented when /// the IncreaseZoom or DecreaseZoom command is executed. /// public double ZoomIncrement { get { return (double)GetValue(ZoomIncrementProperty); } set { SetValue(ZoomIncrementProperty, value); } } ////// Whether the viewer can increase the current zoom. /// public bool CanIncreaseZoom { get { return (bool)GetValue(CanIncreaseZoomProperty); } } ////// Whether the viewer can decrease the current zoom. /// public bool CanDecreaseZoom { get { return (bool)GetValue(CanDecreaseZoomProperty); } } ////// Whether text selection is enabled or disabled. /// public bool IsSelectionEnabled { get { return (bool)GetValue(IsSelectionEnabledProperty); } set { SetValue(IsSelectionEnabledProperty, value); } } ////// Whether the ToolBar is visible or not. /// public bool IsToolBarVisible { get { return (bool)GetValue(IsToolBarVisibleProperty); } set { SetValue(IsToolBarVisibleProperty, value); } } ////// Whether or not a horizontal scrollbar is shown. /// public ScrollBarVisibility HorizontalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); } set { SetValue(HorizontalScrollBarVisibilityProperty, value); } } ////// Whether or not a vertical scrollbar is shown. /// public ScrollBarVisibility VerticalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); } set { SetValue(VerticalScrollBarVisibilityProperty, value); } } #region Public Dynamic Properties ////// public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register( "Document", typeof(FlowDocument), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( null, new PropertyChangedCallback(DocumentChanged))); ////// /// public static readonly DependencyProperty ZoomProperty = FlowDocumentPageViewer.ZoomProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( 100d, new PropertyChangedCallback(ZoomChanged), new CoerceValueCallback(CoerceZoom))); ////// /// public static readonly DependencyProperty MaxZoomProperty = FlowDocumentPageViewer.MaxZoomProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( 200d, new PropertyChangedCallback(MaxZoomChanged), new CoerceValueCallback(CoerceMaxZoom))); ////// /// public static readonly DependencyProperty MinZoomProperty = FlowDocumentPageViewer.MinZoomProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( 80d, new PropertyChangedCallback(MinZoomChanged))); ////// /// public static readonly DependencyProperty ZoomIncrementProperty = FlowDocumentPageViewer.ZoomIncrementProperty.AddOwner( typeof(FlowDocumentScrollViewer)); ////// /// private static readonly DependencyPropertyKey CanIncreaseZoomPropertyKey = DependencyProperty.RegisterReadOnly( "CanIncreaseZoom", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); ////// /// public static readonly DependencyProperty CanIncreaseZoomProperty = CanIncreaseZoomPropertyKey.DependencyProperty; ////// /// private static readonly DependencyPropertyKey CanDecreaseZoomPropertyKey = DependencyProperty.RegisterReadOnly( "CanDecreaseZoom", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); ////// /// public static readonly DependencyProperty CanDecreaseZoomProperty = CanDecreaseZoomPropertyKey.DependencyProperty; ////// /// public static readonly DependencyProperty IsSelectionEnabledProperty = DependencyProperty.Register( "IsSelectionEnabled", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( BooleanBoxes.TrueBox, new PropertyChangedCallback(IsSelectionEnabledChanged))); ////// /// public static readonly DependencyProperty IsToolBarVisibleProperty = DependencyProperty.Register( "IsToolBarVisible", typeof(bool), typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata( BooleanBoxes.FalseBox, new PropertyChangedCallback(IsToolBarVisibleChanged))); ////// /// public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = ScrollViewer.HorizontalScrollBarVisibilityProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(ScrollBarVisibility.Auto)); ////// /// public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner( typeof(FlowDocumentScrollViewer), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible)); #endregion Public Dynamic Properties #endregion Public Properties //------------------------------------------------------------------- // // Protected Methods // //-------------------------------------------------------------------- #region Protected Methods ////// /// Called when print has been completed. /// protected virtual void OnPrintCompleted() { ClearPrintingState(); } ////// Handler for the Find command /// protected virtual void OnFindCommand() { if (CanShowFindToolBar) { // Toggle on the FindToolBar between visible and hidden state. ToggleFindToolBar(FindToolBar == null); } } ////// Handler for the Print command. /// protected virtual void OnPrintCommand() { #if !DONOTREFPRINTINGASMMETA XpsDocumentWriter docWriter; PrintDocumentImageableArea ia = null; FlowDocumentPaginator paginator; Thickness pagePadding; // Only one printing job is allowed. if (_printingState != null) { return; } // If the document is FlowDocument, do custom printing. Otherwise go through default path. if (Document != null) { // Show print dialog. docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia); if (docWriter != null && ia != null) { // Suspend layout on FlowDocumentView. if (RenderScope != null) { RenderScope.SuspendLayout(); } // Store the current state of the document in the PrintingState paginator = ((IDocumentPaginatorSource)Document).DocumentPaginator as FlowDocumentPaginator; _printingState = new FlowDocumentPrintingState(); _printingState.XpsDocumentWriter = docWriter; _printingState.PageSize = paginator.PageSize; _printingState.PagePadding = Document.PagePadding; _printingState.IsSelectionEnabled = IsSelectionEnabled; _printingState.ColumnWidth = Document.ColumnWidth; // Since _printingState value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); // Register for XpsDocumentWriter events. docWriter.WritingCompleted += new WritingCompletedEventHandler(HandlePrintCompleted); docWriter.WritingCancelled += new WritingCancelledEventHandler(HandlePrintCancelled); // Add PreviewCanExecute handler to have a chance to disable UI Commands during printing. if (_contentHost != null) { CommandManager.AddPreviewCanExecuteHandler(_contentHost, new CanExecuteRoutedEventHandler(PreviewCanExecuteRoutedEventHandler)); } // Disable TextSelection, if currently enabled. if (IsSelectionEnabled) { IsSelectionEnabled = false; } // Change the PageSize and PagePadding for the document to match the CanvasSize for the printer device. paginator.PageSize = new Size(ia.MediaSizeWidth, ia.MediaSizeHeight); pagePadding = Document.ComputePageMargin(); Document.PagePadding = new Thickness( Math.Max(ia.OriginWidth, pagePadding.Left), Math.Max(ia.OriginHeight, pagePadding.Top), Math.Max(ia.MediaSizeWidth - (ia.OriginWidth + ia.ExtentWidth), pagePadding.Right), Math.Max(ia.MediaSizeHeight - (ia.OriginHeight + ia.ExtentHeight), pagePadding.Bottom)); Document.ColumnWidth = double.PositiveInfinity; // Send DocumentPaginator to the printer. docWriter.WriteAsync(paginator); } else { OnPrintCompleted(); } } else { OnPrintCompleted(); } #endif // DONOTREFPRINTINGASMMETA } ////// Handler for the CancelPrint command. /// protected virtual void OnCancelPrintCommand() { #if !DONOTREFPRINTINGASMMETA if (_printingState != null) { _printingState.XpsDocumentWriter.CancelAsync(); } #endif // DONOTREFPRINTINGASMMETA } ////// Handler for the IncreaseZoom command. /// protected virtual void OnIncreaseZoomCommand() { // If can zoom in, increase zoom by the zoom increment value. if (CanIncreaseZoom) { Zoom = Math.Min(Zoom + ZoomIncrement, MaxZoom); } } ////// Handler for the DecreaseZoom command. /// protected virtual void OnDecreaseZoomCommand() { // If can zoom out, decrease zoom by the zoom increment value. if (CanDecreaseZoom) { Zoom = Math.Max(Zoom - ZoomIncrement, MinZoom); } } ////// This is the method that responds to the KeyDown event. /// /// Event arguments ////// Critical: get_SearchUp is defined in a non-APTCA assembly. /// TreatAsSafe: call to get_SearchUp does not entail any risk. /// [SecurityCritical, SecurityTreatAsSafe] protected override void OnKeyDown(KeyEventArgs e) { if (e.Handled) { return; } switch (e.Key) { // Esc -- Close FindToolBar case Key.Escape: if (FindToolBar != null) { ToggleFindToolBar(false); e.Handled = true; } break; // F3 -- Invoke Find case Key.F3: if (CanShowFindToolBar) { if (FindToolBar != null) { // If the Shift key is also pressed, then search up. FindToolBar.SearchUp = ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift); OnFindInvoked(this, EventArgs.Empty); } else { // Make the FindToolBar visible ToggleFindToolBar(true); } e.Handled = true; } break; } // If not handled, do default handling. if (!e.Handled) { base.OnKeyDown(e); } } ////// Mouse wheel rotation handler. /// /// MouseWheelEventArgs protected override void OnMouseWheel(MouseWheelEventArgs e) { if (e.Handled) { return; } if (_contentHost != null) { //Press Ctrl and scroll mouse wheel will zoom in/out the document if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) { // If can zoom in, increase zoom by the zoom increment value. if (e.Delta > 0 && CanIncreaseZoom) { Zoom = Math.Min(Zoom + ZoomIncrement, MaxZoom); } else if (e.Delta < 0 && CanDecreaseZoom) { Zoom = Math.Max(Zoom - ZoomIncrement, MinZoom); } } else { if (e.Delta < 0) { _contentHost.LineDown(); } else { _contentHost.LineUp(); } } e.Handled = true; } // If not handled, do default handling. if (!e.Handled) { base.OnMouseWheel(e); } } ////// Called when ContextMenuOpening is raised on this element. /// /// Event arguments protected override void OnContextMenuOpening(ContextMenuEventArgs e) { base.OnContextMenuOpening(e); DocumentViewerHelper.OnContextMenuOpening(Document, this, e); } ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new FlowDocumentScrollViewerAutomationPeer(this); } #endregion Protected Methods //------------------------------------------------------------------- // // Protected Properties // //------------------------------------------------------------------- #region Protected Properties ///) /// /// Returns enumerator to logical children. /// protected internal override IEnumerator LogicalChildren { get { if (HasLogicalChildren && Document != null) { return new SingleChildEnumerator(Document); } return EmptyEnumerator.Instance; } } #endregion Protected Properties //------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------- #region Internal Methods ////// Allows FrameworkElement to augment the EventRoute. /// internal override bool BuildRouteCore(EventRoute route, RoutedEventArgs args) { // If FlowDocumentScrollViewer is used as embedded viewer (in FlowDocumentReader), // it is not part of logical tree, so default logic in FE to re-add logical // tree from branched node does not work here. // But before FlowDocumentScrollViewer is added to the event route, logical // ancestors up to Document need to be added to the route. Otherwise // content will not have a chance to react to events first. // This breaks navigation cursor management logic, because TextEditor attached // to FlowDocumentScrollViewer handles those events first. DependencyObject document = this.Document as DependencyObject; if (document != null && LogicalTreeHelper.GetParent(document) != this) { DependencyObject branchNode = route.PeekBranchNode() as DependencyObject; if (branchNode != null && DocumentViewerHelper.IsLogicalDescendent(branchNode, document)) { // Add intermediate ContentElements to the route. FrameworkElement.AddIntermediateElementsToRoute( LogicalTreeHelper.GetParent(document), route, args, LogicalTreeHelper.GetParent(branchNode)); } } return base.BuildRouteCore(route, args); } ////// Bring specified content position into view. /// internal object BringContentPositionIntoView(object arg) { ITextPointer contentPosition = arg as ITextPointer; if (contentPosition != null) { ITextView textView = GetTextView(); if (textView != null && textView.IsValid && textView.RenderScope is IScrollInfo && contentPosition.TextContainer == textView.TextContainer) { if (textView.Contains(contentPosition)) { Rect rect = textView.GetRectangleFromTextPosition(contentPosition); if (rect != Rect.Empty) { IScrollInfo isi = (IScrollInfo)textView.RenderScope; isi.SetVerticalOffset(rect.Top + isi.VerticalOffset); } } else { // Wait until ContentPosition in in the view. Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(BringContentPositionIntoView), contentPosition); } } } return null; } #endregion Internal Methods //------------------------------------------------------------------- // // Internal Properties // //-------------------------------------------------------------------- #region Internal Properties ////// Access to the ScrollViewer in FlowDocumentScrollViewer style /// internal ScrollViewer ScrollViewer { get { return _contentHost; } } ////// Whether FindToolBar can be enabled. /// internal bool CanShowFindToolBar { get { return (_findToolBarHost != null && Document != null && _textEditor != null); } } ////// Whether currently printing the content. /// internal bool IsPrinting { get { return (_printingState != null); } } ////// Returns textpointer for upper left corner of content. /// internal TextPointer ContentPosition { get { TextPointer contentPosition = null; ITextView textView = GetTextView(); if (textView != null && textView.IsValid && textView.RenderScope is IScrollInfo) { contentPosition = textView.GetTextPositionFromPoint(new Point(), true) as TextPointer; } return contentPosition; } } #endregion Internal Properties //-------------------------------------------------------------------- // // Private Methods // //------------------------------------------------------------------- #region Private Methods ////// Enables/disables the FindToolBar. /// /// Whether to enable/disable FindToolBar. private void ToggleFindToolBar(bool enable) { Invariant.Assert(enable == (FindToolBar == null)); DocumentViewerHelper.ToggleFindToolBar(_findToolBarHost, new EventHandler(OnFindInvoked), enable); // FindToolBar is embedded inside the toolbar, so event if the ToolBar is not visible // it needs to be shown when showing FindToolBar. if (!IsToolBarVisible && _toolBarHost != null) { _toolBarHost.Visibility = enable ? Visibility.Visible : Visibility.Collapsed; } } ////// Apply the zoom value to the render scope. /// private void ApplyZoom() { if (RenderScope != null) { RenderScope.LayoutTransform = new ScaleTransform(Zoom / 100, Zoom / 100); } } ////// Attach TextEditor to Document, if supports text. /// private void AttachTextEditor() { ITextView textView; //get annotation service and state so we can restore it AnnotationService service = AnnotationService.GetService(this); bool serviceOldState = false; //disable the service if enabled if ((service != null) && service.IsEnabled) { serviceOldState = true; service.Disable(); } // This method is called when Document is changing or control template // is replaced, so need to drop old TextEditor data. if (_textEditor != null) { _textEditor.TextContainer.TextView = null; _textEditor.OnDetach(); _textEditor = null; } // 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). if (IsSelectionEnabled && Document != null && RenderScope != null && Document.StructuralCache.TextContainer.TextSelection == null) { textView = GetTextView(); _textEditor = new TextEditor(Document.StructuralCache.TextContainer, this, false); _textEditor.IsReadOnly = !IsEditingEnabled; _textEditor.TextView = textView; _textEditor.TextContainer.TextView = textView; } //restore AnnotationsService state if ((service != null) && serviceOldState) { service.Enable(service.Store); } // If TextEditor is not enabled, FindToolBar cannot be visible. if (_textEditor == null && FindToolBar != null) { ToggleFindToolBar(false); } } ////// Handler for ScrollViewer's OnScrollChanged event. /// private void OnScrollChanged(object sender, ScrollChangedEventArgs e) { if (e.OriginalSource == ScrollViewer) { // If ScrollViewer.ViewportHeight has been changed, TextEditor.PageHeight must be updated if (!DoubleUtil.IsZero(e.ViewportHeightChange)) { SetValue(TextEditor.PageHeightProperty, e.ViewportHeight); } } } ////// Called when WritingCompleted event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCompleted(object sender, WritingCompletedEventArgs e) { OnPrintCompleted(); } ////// Called when WritingCancelled event raised by a DocumentWriter (during printing). /// /// Sender of the event. /// Event arguments. private void HandlePrintCancelled(object sender, WritingCancelledEventArgs e) { ClearPrintingState(); } ////// Clear printing state. /// private void ClearPrintingState() { #if !DONOTREFPRINTINGASMMETA if (_printingState != null) { // Resume layout on FlowDocumentView. if (RenderScope != null) { RenderScope.ResumeLayout(); } // Enable TextSelection, if it was previously enabled. if (_printingState.IsSelectionEnabled) { IsSelectionEnabled = true; } // Remove PreviewCanExecute handler (added when Print command was executed). if (_contentHost != null) { CommandManager.RemovePreviewCanExecuteHandler(_contentHost, new CanExecuteRoutedEventHandler(PreviewCanExecuteRoutedEventHandler)); } // Unregister for XpsDocumentWriter events. _printingState.XpsDocumentWriter.WritingCompleted -= new WritingCompletedEventHandler(HandlePrintCompleted); _printingState.XpsDocumentWriter.WritingCancelled -= new WritingCancelledEventHandler(HandlePrintCancelled); // Restore old page metrics on FlowDocument. Document.PagePadding = _printingState.PagePadding; Document.ColumnWidth = _printingState.ColumnWidth; ((IDocumentPaginatorSource)Document).DocumentPaginator.PageSize = _printingState.PageSize; _printingState = null; // Since _documentWriter value is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } #endif // DONOTREFPRINTINGASMMETA } ////// Makes sure the target is visible in the client area. /// /// RequestBringIntoViewEventArgs indicates the element and region to scroll into view. private void HandleRequestBringIntoView(RequestBringIntoViewEventArgs args) { DependencyObject child; DependencyObject document; IContentHost ich; ReadOnlyCollectionrects; UIElement targetUIElement; Rect targetRect = Rect.Empty; if (args != null && args.TargetObject != null && Document != null) { document = Document; // If the passed in object is a logical child of FlowDocumentScrollViewer'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. if (args.TargetObject == document) { if (_contentHost != null) { _contentHost.ScrollToHome(); } args.Handled = true; // Mark the event as handled. } else if (args.TargetObject is UIElement) { targetUIElement = (UIElement)args.TargetObject; // Since entire content of FlowDocument is represented by // bottomless page, the target has to be connected to visual tree. // Otherwise, it is not descendant of FlowDocument. if (RenderScope != null && RenderScope.IsAncestorOf(targetUIElement)) { targetRect = args.TargetRect; if (targetRect.IsEmpty) { targetRect = new Rect(targetUIElement.RenderSize); } targetRect = MakeVisible((IScrollInfo)RenderScope, targetUIElement, targetRect); if(!targetRect.IsEmpty) { GeneralTransform t = RenderScope.TransformToAncestor(this); targetRect = t.TransformBounds(targetRect); } args.Handled = true; // Mark the event as handled. } } else if (args.TargetObject is ContentElement) { // Verify if TargetObject is in fact a child of Document. child = args.TargetObject; while (child != null && child != document) { child = LogicalTreeHelper.GetParent(child); } if (child != null) { ich = GetIContentHost(); if (ich != null) { // Get the position of the content. rects = ich.GetRectangles((ContentElement)args.TargetObject); if (rects.Count > 0) { targetRect = MakeVisible((IScrollInfo)RenderScope, (Visual)ich, rects[0]); if(!targetRect.IsEmpty) { GeneralTransform t = RenderScope.TransformToAncestor(this); targetRect = t.TransformBounds(targetRect); } } } 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); } } } } /// /// The Document has changed and needs to be updated. /// private void DocumentChanged(FlowDocument oldDocument, FlowDocument newDocument) { // Use TextSelection to determine whether the new document belongs to another // control or not. if (newDocument != null && newDocument.StructuralCache.TextContainer != null && newDocument.StructuralCache.TextContainer.TextSelection != null) { throw new ArgumentException(SR.Get(SRID.FlowDocumentScrollViewerDocumentBelongsToAnotherFlowDocumentScrollViewerAlready)); } // Cleanup state associated with the old document. if (oldDocument != null) { // If Document was added to logical tree of FlowDocumentScrollViewer before, remove it. if (_documentAsLogicalChild) { RemoveLogicalChild(oldDocument); } // Remove the document from the ContentHost. if (RenderScope != null) { RenderScope.Document = null; } oldDocument.ClearValue(PathNode.HiddenParentProperty); oldDocument.StructuralCache.ClearUpdateInfo(true); } // If FlowDocumentScrollViewer was created through style, then do not modify // the logical tree. Instead, set "core parent" for the Document. if (newDocument != null && LogicalTreeHelper.GetParent(newDocument) != null) { // Set the "core parent" back to us. ContentOperations.SetParent(newDocument, this); _documentAsLogicalChild = false; } else { _documentAsLogicalChild = true; } // Initialize state associated with the new document. if (newDocument != null) { // Set the document on the ContentHost. if (RenderScope != null) { RenderScope.Document = newDocument; } // If Document should be part of FlowDocumentScrollViewer's logical tree, add it. if (_documentAsLogicalChild) { AddLogicalChild(newDocument); } // Set the hidden parent of the document newDocument.SetValue(PathNode.HiddenParentProperty, this); newDocument.StructuralCache.ClearUpdateInfo(true); } // Attach TextEditor, if content supports it. This method will also // detach TextEditor from old content. AttachTextEditor(); // Update the toolbar with our current document state. if (!CanShowFindToolBar) { // Disable FindToolBar, if the content does not support it. if (FindToolBar != null) { ToggleFindToolBar(false); } } // Document is also represented as Automation child. Need to invalidate peer to force update. FlowDocumentScrollViewerAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as FlowDocumentScrollViewerAutomationPeer; if (peer != null) { peer.InvalidatePeer(); } } ////// Retrieves ITextView associated with the content. /// NOTE: Not retrieved from TextEditor, because it may not exist in some cases. /// private ITextView GetTextView() { ITextView textView = null; if (RenderScope is IServiceProvider) { textView = (ITextView)((IServiceProvider)RenderScope).GetService(typeof(ITextView)); } return textView; } ////// Retrieves IContentHost associated with the content. /// private IContentHost GetIContentHost() { IContentHost ich = null; if (RenderScope != null && VisualTreeHelper.GetChildrenCount(RenderScope) > 0) { ich = VisualTreeHelper.GetChild(RenderScope, 0) as IContentHost; } return ich; } ////// Create two way property binding. /// private void CreateTwoWayBinding(FrameworkElement fe, DependencyProperty dp, string propertyPath) { Binding binding = new Binding(propertyPath); binding.Mode = BindingMode.TwoWay; binding.Source = this; fe.SetBinding(dp, binding); } #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); // Create private commands to control content scrolling. It is required because TextEditor binds // those Keys to its own commands (caret navigation), which are useless in viewing scenarios. // Since following commands are private, it is OK to pass String.Empty as descriptive text for the command. _commandLineDown = new RoutedUICommand(String.Empty, "FDSV_LineDown", typeof(FlowDocumentScrollViewer)); _commandLineUp = new RoutedUICommand(String.Empty, "FDSV_LineUp", typeof(FlowDocumentScrollViewer)); _commandLineLeft = new RoutedUICommand(String.Empty, "FDSV_LineLeft", typeof(FlowDocumentScrollViewer)); _commandLineRight = new RoutedUICommand(String.Empty, "FDSV_LineRight", typeof(FlowDocumentScrollViewer)); // Register editing command handlers TextEditor.RegisterCommandHandlers(typeof(FlowDocumentScrollViewer), /*acceptsRichContent:*/true, /*readOnly:*/!IsEditingEnabled, /*registerEventListeners*/true); // Command: ApplicationCommands.Find CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), ApplicationCommands.Find, executedHandler, canExecuteHandler); // Command: ApplicationCommands.Print CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), ApplicationCommands.Print, executedHandler, canExecuteHandler); // Command: ApplicationCommands.CancelPrint CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), ApplicationCommands.CancelPrint, executedHandler, canExecuteHandler); // no key gesture // Command: NavigationCommands.PreviousPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.PreviousPage, executedHandler, canExecuteHandler, Key.PageUp); // Command: NavigationCommands.NextPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.NextPage, executedHandler, canExecuteHandler, Key.PageDown); // Command: NavigationCommands.FirstPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.FirstPage, executedHandler, canExecuteHandler, new KeyGesture(Key.Home), new KeyGesture(Key.Home, ModifierKeys.Control)); // Command: NavigationCommands.LastPage CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.LastPage, executedHandler, canExecuteHandler, new KeyGesture(Key.End), new KeyGesture(Key.End, ModifierKeys.Control)); // Command: NavigationCommands.IncreaseZoom CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.IncreaseZoom, executedHandler, canExecuteHandler, new KeyGesture(Key.OemPlus, ModifierKeys.Control)); // Command: NavigationCommands.DecreaseZoom CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), NavigationCommands.DecreaseZoom, executedHandler, canExecuteHandler, new KeyGesture(Key.OemMinus, ModifierKeys.Control)); // Command: FDSV_LineDown CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineDown, executedHandler, canExecuteHandler, Key.Down); // Command: FDSV_LineUp CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineUp, executedHandler, canExecuteHandler, Key.Up); // Command: FDSV_LineLeft CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineLeft, executedHandler, canExecuteHandler, Key.Left); // Command: FDSV_LineRight CommandHelpers.RegisterCommandHandler(typeof(FlowDocumentScrollViewer), _commandLineRight, executedHandler, canExecuteHandler, Key.Right); } ////// Central handler for CanExecuteRouted events fired by Commands directed at FlowDocumentScrollViewer. /// /// The target of this Command, expected to be FlowDocumentScrollViewer /// The event arguments for this event. private static void CanExecuteRoutedEventHandler(object target, CanExecuteRoutedEventArgs args) { FlowDocumentScrollViewer viewer = target as FlowDocumentScrollViewer; Invariant.Assert(viewer != null, "Target of QueryEnabledEvent must be FlowDocumentScrollViewer."); Invariant.Assert(args != null, "args cannot be null."); // FlowDocumentScrollViewer is capable of execution of the majority of its commands. // Special rules: // a) during printing only CancelPrint is enabled. // b) Find command is enabled only when FindToolBar is enabled. // c) Print command is enabled when Document is attached. // d) CancelPrint command is enabled only during printing. if (viewer._printingState == null) { if (args.Command == ApplicationCommands.Find) { args.CanExecute = viewer.CanShowFindToolBar; } else if (args.Command == ApplicationCommands.Print) { args.CanExecute = (viewer.Document != null); } else if (args.Command == ApplicationCommands.CancelPrint) { args.CanExecute = false; } else { args.CanExecute = true; } } else { args.CanExecute = (args.Command == ApplicationCommands.CancelPrint); } } ////// Central handler for all ExecutedRoutedEvent fired by Commands directed at FlowDocumentScrollViewer. /// /// The target of this Command, expected to be FlowDocumentScrollViewer. /// The event arguments associated with this event. private static void ExecutedRoutedEventHandler(object target, ExecutedRoutedEventArgs args) { FlowDocumentScrollViewer viewer = target as FlowDocumentScrollViewer; Invariant.Assert(viewer != null, "Target of ExecuteEvent must be FlowDocumentScrollViewer."); 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 == ApplicationCommands.Find) { viewer.OnFindCommand(); } else if (args.Command == ApplicationCommands.Print) { viewer.OnPrintCommand(); } else if (args.Command == ApplicationCommands.CancelPrint) { viewer.OnCancelPrintCommand(); } else if (args.Command == NavigationCommands.IncreaseZoom) { viewer.OnIncreaseZoomCommand(); } else if (args.Command == NavigationCommands.DecreaseZoom) { viewer.OnDecreaseZoomCommand(); } else if (args.Command == _commandLineDown) { if (viewer._contentHost != null) { viewer._contentHost.LineDown(); } } else if (args.Command == _commandLineUp) { if (viewer._contentHost != null) { viewer._contentHost.LineUp(); } } else if (args.Command == _commandLineLeft) { if (viewer._contentHost != null) { viewer._contentHost.LineLeft(); } } else if (args.Command == _commandLineRight) { if (viewer._contentHost != null) { viewer._contentHost.LineRight(); } } else if (args.Command == NavigationCommands.NextPage) { if (viewer._contentHost != null) { viewer._contentHost.PageDown(); } } else if (args.Command == NavigationCommands.PreviousPage) { if (viewer._contentHost != null) { viewer._contentHost.PageUp(); } } else if (args.Command == NavigationCommands.FirstPage) { if (viewer._contentHost != null) { viewer._contentHost.ScrollToHome(); } } else if (args.Command == NavigationCommands.LastPage) { if (viewer._contentHost != null) { viewer._contentHost.ScrollToEnd(); } } else { Invariant.Assert(false, "Command not handled in ExecutedRoutedEventHandler."); } } ////// Invoked when the "Find" button in the Find Toolbar is clicked. /// This method invokes the actual Find process. /// /// The object that sent this event /// The Click Events associated with this event private void OnFindInvoked(object sender, EventArgs e) { ITextRange findResult; FindToolBar findToolBar = FindToolBar; if (findToolBar != null && _textEditor != null) { // In order to show current text selection TextEditor requires Focus to be set on the UIScope. // If there embedded controls, it may happen that embedded control currently has focus and find // was invoked through hotkeys. To support this case we manually move focus to the appropriate element. Focus(); findResult = DocumentViewerHelper.Find(findToolBar, _textEditor, _textEditor.TextView, _textEditor.TextView); // If we found something, TextEditor will bring the selection into view. // It is possible, because RenderScope is inside ScrollViewer. // If we did not find anything, alert the user. if ((findResult == null) || findResult.IsEmpty) { DocumentViewerHelper.ShowFindUnsuccessfulMessage(findToolBar); } } } ////// Disable commands on DocumentViewerBase when this printing is in progress. /// private void PreviewCanExecuteRoutedEventHandler(object target, CanExecuteRoutedEventArgs args) { ScrollViewer sv = target as ScrollViewer; Invariant.Assert(sv != null, "Target of PreviewCanExecuteRoutedEventHandler must be ScrollViewer."); Invariant.Assert(args != null, "args cannot be null."); // Disable UI commands, if printing is in progress. if (_printingState != null) { args.CanExecute = false; args.Handled = true; } } ////// Called when a key event occurs. /// private static void KeyDownHandler(object sender, KeyEventArgs e) { DocumentViewerHelper.KeyDownHelper(e, ((FlowDocumentScrollViewer)sender)._findToolBarHost); } #endregion Commands #region Static Methods ////// Wrapper around IScrollInfo.MakeVisible /// /// The IScrollInfo to call MakeVisible on /// visual parameter for call to MakeVisible /// rectangle parameter for call to MakeVisible ///Rectangle representing visible portion of visual relative to scrollInfo's viewport private static Rect MakeVisible(IScrollInfo scrollInfo, Visual visual, Rect rectangle) { // Rect result; if(scrollInfo.GetType() == typeof(System.Windows.Controls.ScrollContentPresenter)) { result = ((ScrollContentPresenter)scrollInfo).MakeVisible(visual, rectangle, false); } else { result = scrollInfo.MakeVisible(visual, rectangle); } return result; } ////// The Document has changed and needs to be updated. /// private static void DocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); ((FlowDocumentScrollViewer)d).DocumentChanged((FlowDocument)e.OldValue, (FlowDocument)e.NewValue); // Since Document state is used to determine CanExecute state, we must invalidate that state. CommandManager.InvalidateRequerySuggested(); } ////// The Zoom has changed and needs to be updated. /// private static void ZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; if (!DoubleUtil.AreClose((double)e.OldValue, (double)e.NewValue)) { // If zoom has been changed, CanIncrease/DecreaseZoom property need to be updated. viewer.SetValue(CanIncreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.GreaterThan(viewer.MaxZoom, viewer.Zoom))); viewer.SetValue(CanDecreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.LessThan(viewer.MinZoom, viewer.Zoom))); // Apply the new zoom value. viewer.ApplyZoom(); } } ////// Coerce Zoom with Max/MinZoom, MinZoom works as the baseline. /// private static object CoerceZoom(DependencyObject d, object value) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; double zoom = (double)value; double maxZoom = viewer.MaxZoom; if (DoubleUtil.LessThan(maxZoom, zoom)) { return maxZoom; } double minZoom = viewer.MinZoom; if (DoubleUtil.GreaterThan(minZoom, zoom)) { return minZoom; } return value; } ////// The MaxZoom has changed and needs to be updated. /// private static void MaxZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; viewer.CoerceValue(ZoomProperty); viewer.SetValue(CanIncreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.GreaterThan(viewer.MaxZoom, viewer.Zoom))); } ////// MaxZoom need to be coerced if MinZoom > MaxZoom /// private static object CoerceMaxZoom(DependencyObject d, object value) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; double min = viewer.MinZoom; return ((double)value < min) ? min : value; } ////// The MinZoom has changed and needs to be updated. /// private static void MinZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; viewer.CoerceValue(MaxZoomProperty); viewer.CoerceValue(ZoomProperty); viewer.SetValue(CanDecreaseZoomPropertyKey, BooleanBoxes.Box(DoubleUtil.LessThan(viewer.MinZoom, viewer.Zoom))); } ////// Validate Zoom, MaxZoom, MinZoom and ZoomIncrement value. /// /// Value to validate. ///True if the value is valid, false otherwise. private static bool ZoomValidateValue(object o) { double value = (double)o; return (!Double.IsNaN(value) && !Double.IsInfinity(value) && DoubleUtil.GreaterThan(value, 0d)); } ////// Called from the event handler to make sure the target is visible in the client area. /// /// 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 FlowDocumentScrollViewer) { ((FlowDocumentScrollViewer)sender).HandleRequestBringIntoView(args); } } ////// The IsSelectionEnabled has changed and needs to be updated. /// private static void IsSelectionEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; viewer.AttachTextEditor(); } ////// The IsToolBarVisible has changed and needs to be updated. /// private static void IsToolBarVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Invariant.Assert(d != null && d is FlowDocumentScrollViewer); FlowDocumentScrollViewer viewer = (FlowDocumentScrollViewer)d; if (viewer._toolBarHost != null) { viewer._toolBarHost.Visibility = (bool)e.NewValue ? Visibility.Visible : Visibility.Collapsed; } } #endregion Static Methods #endregion Private Methods //-------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------- #region Private Properties ////// Returns FindToolBar, if enabled. /// private FindToolBar FindToolBar { get { return (_findToolBarHost != null) ? _findToolBarHost.Child as FindToolBar : null; } } ////// Content of the ScrollViewer which is treated as RenderScope for the TextEditor. /// private FlowDocumentView RenderScope { get { return (_contentHost != null) ? _contentHost.Content as FlowDocumentView : null; } } #endregion Private Properties //------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------- #region Private Fields private TextEditor _textEditor; // Text editor (enables text selection) private Decorator _findToolBarHost; // Host for FindToolBar private Decorator _toolBarHost; // Host for ToolBar private ScrollViewer _contentHost; // Host for content viewer private bool _documentAsLogicalChild; // Is Document part of logical tree private FlowDocumentPrintingState _printingState; // Printing state private const string _contentHostTemplateName = "PART_ContentHost"; // Name for ContentHost private const string _findToolBarHostTemplateName = "PART_FindToolBarHost"; // Name for the Find ToolBar host private const string _toolBarHostTemplateName = "PART_ToolBarHost"; // Name for the ToolBar host private static bool IsEditingEnabled = false; // A flag enabling text editing within the viewer // accessible only through reflection. private static RoutedUICommand _commandLineDown; // Private LineDown command private static RoutedUICommand _commandLineUp; // Private LineUp command private static RoutedUICommand _commandLineLeft; // Private LineLeft command private static RoutedUICommand _commandLineRight; // Private LineRight command #endregion Private Fields //-------------------------------------------------------------------- // // IAddChild Members // //------------------------------------------------------------------- #region IAddChild Members ////// Called to add the object as a Child. /// /// Object to add as a child. ///FlowDocumentScrollViewer only supports a single child of type IDocumentPaginator. 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 ArgumentException(SR.Get(SRID.FlowDocumentScrollViewerCanHaveOnlyOneChild)); } if (!(value is FlowDocument)) { throw new ArgumentException(SR.Get(SRID.UnexpectedParameterType, value.GetType(), typeof(FlowDocument)), "value"); } Document = value as FlowDocument; } ////// Called when text appears under the tag in markup /// /// Text to add to the Object. ///FlowDocumentScrollViewer does not support Text children. void IAddChild.AddText(string text) { XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this); } #endregion IAddChild Members //-------------------------------------------------------------------- // // IServiceProvider Members // //-------------------------------------------------------------------- #region IServiceProvider Members ////// 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 // (2) TextContainer if (serviceType == typeof(ITextView)) { service = GetTextView(); } else if (serviceType == typeof(TextContainer) || serviceType == typeof(ITextContainer)) { if (Document != null) { service = ((IServiceProvider)Document).GetService(serviceType); } } return service; } #endregion IServiceProvider Members //------------------------------------------------------------------- // // IJournalState Members // //-------------------------------------------------------------------- #region IJournalState Members [Serializable] private class JournalState : CustomJournalStateInternal { public JournalState(int contentPosition, LogicalDirection contentPositionDirection, double zoom) { ContentPosition = contentPosition; ContentPositionDirection = contentPositionDirection; Zoom = zoom; } public int ContentPosition; public LogicalDirection ContentPositionDirection; public double Zoom; } ////// CustomJournalStateInternal IJournalState.GetJournalState(JournalReason journalReason) { int cp = -1; LogicalDirection cpDirection = LogicalDirection.Forward; TextPointer contentPosition = ContentPosition; if (contentPosition != null) { cp = contentPosition.Offset; cpDirection = contentPosition.LogicalDirection; } return new JournalState(cp, cpDirection, Zoom); } ////// /// void IJournalState.RestoreJournalState(CustomJournalStateInternal state) { JournalState viewerState = state as JournalState; if (state != null) { Zoom = viewerState.Zoom; if (viewerState.ContentPosition != -1) { FlowDocument document = Document; if (document != null) { TextContainer textContainer = document.StructuralCache.TextContainer; if (viewerState.ContentPosition <= textContainer.SymbolCount) { TextPointer contentPosition = textContainer.CreatePointerAtOffset(viewerState.ContentPosition, viewerState.ContentPositionDirection); // This need be called because the UI may not be ready when Contentposition is set. Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(BringContentPositionIntoView), contentPosition); } } } } } #endregion IJournalState Members //------------------------------------------------------------------- // // DTypeThemeStyleKey // //------------------------------------------------------------------- #region DTypeThemeStyleKey ////// /// Returns the DependencyObjectType for the registered ThemeStyleKey's default /// value. Controls will override this method to return approriate types /// internal override DependencyObjectType DTypeThemeStyleKey { get { return _dType; } } private static DependencyObjectType _dType; #endregion DTypeThemeStyleKey } } // 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
- FlowDocumentFormatter.cs
- MergeEnumerator.cs
- TransformerInfoCollection.cs
- Pen.cs
- ObjectDataSourceWizardForm.cs
- DataRow.cs
- IsolatedStorageFileStream.cs
- EmissiveMaterial.cs
- HostedTcpTransportManager.cs
- PersonalizationProviderHelper.cs
- Size3DConverter.cs
- EmptyControlCollection.cs
- MsmqTransportElement.cs
- BitmapEffectDrawing.cs
- PathTooLongException.cs
- UnitySerializationHolder.cs
- CacheVirtualItemsEvent.cs
- ExtentCqlBlock.cs
- XmlDictionaryWriter.cs
- XhtmlTextWriter.cs
- TextShapeableCharacters.cs
- ToolStripPanel.cs
- SymbolType.cs
- SmtpMail.cs
- MenuItem.cs
- XNodeValidator.cs
- RegistrationServices.cs
- FontFamilyConverter.cs
- Relationship.cs
- CompositeScriptReference.cs
- EventArgs.cs
- MoveSizeWinEventHandler.cs
- DataView.cs
- DesignerOptionService.cs
- SQLDateTime.cs
- autovalidator.cs
- LoginViewDesigner.cs
- EditingMode.cs
- Vector3dCollection.cs
- CustomValidator.cs
- SqlDelegatedTransaction.cs
- ObjectSecurity.cs
- HostingPreferredMapPath.cs
- RequestQueryProcessor.cs
- SafeRsaProviderHandle.cs
- ExclusiveTcpTransportManager.cs
- StrongTypingException.cs
- TextParaLineResult.cs
- UIServiceHelper.cs
- GridLength.cs
- CipherData.cs
- IIS7WorkerRequest.cs
- TypeSystem.cs
- HttpRequestWrapper.cs
- JulianCalendar.cs
- AsymmetricKeyExchangeDeformatter.cs
- SiteMapNodeItem.cs
- RuntimeConfig.cs
- SizeAnimationClockResource.cs
- TimeoutTimer.cs
- Timer.cs
- SQLDecimalStorage.cs
- Transform.cs
- Constraint.cs
- DrawListViewItemEventArgs.cs
- basevalidator.cs
- ReturnValue.cs
- TokenBasedSetEnumerator.cs
- SystemFonts.cs
- NavigatorInput.cs
- AccessKeyManager.cs
- HyperLinkDataBindingHandler.cs
- AuthenticateEventArgs.cs
- ReadOnlyCollection.cs
- LockedAssemblyCache.cs
- SmiTypedGetterSetter.cs
- AttachedAnnotation.cs
- PrimitiveSchema.cs
- TemplateParser.cs
- XmlUrlResolver.cs
- PathTooLongException.cs
- ColorMap.cs
- SafeArrayRankMismatchException.cs
- ToolStripStatusLabel.cs
- ZipIOLocalFileHeader.cs
- x509store.cs
- TableAdapterManagerGenerator.cs
- FileDetails.cs
- AlignmentXValidation.cs
- DefaultValueTypeConverter.cs
- Rfc2898DeriveBytes.cs
- CheckBoxBaseAdapter.cs
- wgx_render.cs
- SourceItem.cs
- CompositeScriptReferenceEventArgs.cs
- MarkupExtensionParser.cs
- ScrollBarAutomationPeer.cs
- GlobalEventManager.cs
- TextSpanModifier.cs
- GenericPrincipal.cs