Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Annotations / AnnotationService.cs / 1305600 / AnnotationService.cs
#pragma warning disable 1634, 1691 //---------------------------------------------------------------------------- //// Copyright(C) Microsoft Corporation. All rights reserved. // // // Description: // AnnotationService provides DynamicProperties and API for configuring // and invoking the annotation framework. // // History: // 11/25/2002 rruiz: Created AnnotationService.cs // 08/08/2003 magedz: Port to WCP tree // 11/11/2003 magedz: restructure, and add event handling as per service spec // 2004-04-13 axelk: added DP for Chooser and code changes to integrate annotation component manager. // 12/12/2005 rruiz: Made most APIs internal but left them in their places // because we intend to make them public in V2. Added the // new limited public APIs for V2. // 05/12/2005: rruiz: Simplified the creation/enable/disable model. Moved create/delete methods to // helper class. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Threading; using System.IO; using System.Windows; using System.Windows.Annotations.Storage; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Markup; using System.Xml; using MS.Internal; using MS.Internal.Annotations; using MS.Internal.Annotations.Anchoring; using MS.Internal.Annotations.Component; using MS.Internal.Documents; using MS.Utility; using System.Reflection; // for BindingFlags using System.Globalization; // for CultureInfo using System.Threading; namespace System.Windows.Annotations { ////// AnnotationService represents a single configuration of the annotation sub-system. /// AnnotationService is scoped to the DependencyObject it was created on. /// public sealed class AnnotationService : DispatcherObject { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Static constructor registers command bindings on DocumentViewerBase for all annotation commands. /// static AnnotationService() { CommandManager.RegisterClassCommandBinding(typeof(DocumentViewerBase), new CommandBinding(CreateHighlightCommand, AnnotationHelper.OnCreateHighlightCommand, AnnotationHelper.OnQueryCreateHighlightCommand)); CommandManager.RegisterClassCommandBinding(typeof(DocumentViewerBase), new CommandBinding(CreateTextStickyNoteCommand, AnnotationHelper.OnCreateTextStickyNoteCommand, AnnotationHelper.OnQueryCreateTextStickyNoteCommand)); CommandManager.RegisterClassCommandBinding(typeof(DocumentViewerBase), new CommandBinding(CreateInkStickyNoteCommand, AnnotationHelper.OnCreateInkStickyNoteCommand, AnnotationHelper.OnQueryCreateInkStickyNoteCommand)); CommandManager.RegisterClassCommandBinding(typeof(DocumentViewerBase), new CommandBinding(ClearHighlightsCommand, AnnotationHelper.OnClearHighlightsCommand, AnnotationHelper.OnQueryClearHighlightsCommand)); CommandManager.RegisterClassCommandBinding(typeof(DocumentViewerBase), new CommandBinding(DeleteStickyNotesCommand, AnnotationHelper.OnDeleteStickyNotesCommand, AnnotationHelper.OnQueryDeleteStickyNotesCommand)); CommandManager.RegisterClassCommandBinding(typeof(DocumentViewerBase), new CommandBinding(DeleteAnnotationsCommand, AnnotationHelper.OnDeleteAnnotationsCommand, AnnotationHelper.OnQueryDeleteAnnotationsCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentScrollViewer), new CommandBinding(CreateHighlightCommand, AnnotationHelper.OnCreateHighlightCommand, AnnotationHelper.OnQueryCreateHighlightCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentScrollViewer), new CommandBinding(CreateTextStickyNoteCommand, AnnotationHelper.OnCreateTextStickyNoteCommand, AnnotationHelper.OnQueryCreateTextStickyNoteCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentScrollViewer), new CommandBinding(CreateInkStickyNoteCommand, AnnotationHelper.OnCreateInkStickyNoteCommand, AnnotationHelper.OnQueryCreateInkStickyNoteCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentScrollViewer), new CommandBinding(ClearHighlightsCommand, AnnotationHelper.OnClearHighlightsCommand, AnnotationHelper.OnQueryClearHighlightsCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentScrollViewer), new CommandBinding(DeleteStickyNotesCommand, AnnotationHelper.OnDeleteStickyNotesCommand, AnnotationHelper.OnQueryDeleteStickyNotesCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentScrollViewer), new CommandBinding(DeleteAnnotationsCommand, AnnotationHelper.OnDeleteAnnotationsCommand, AnnotationHelper.OnQueryDeleteAnnotationsCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentReader), new CommandBinding(CreateHighlightCommand, AnnotationHelper.OnCreateHighlightCommand, AnnotationHelper.OnQueryCreateHighlightCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentReader), new CommandBinding(CreateTextStickyNoteCommand, AnnotationHelper.OnCreateTextStickyNoteCommand, AnnotationHelper.OnQueryCreateTextStickyNoteCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentReader), new CommandBinding(CreateInkStickyNoteCommand, AnnotationHelper.OnCreateInkStickyNoteCommand, AnnotationHelper.OnQueryCreateInkStickyNoteCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentReader), new CommandBinding(ClearHighlightsCommand, AnnotationHelper.OnClearHighlightsCommand, AnnotationHelper.OnQueryClearHighlightsCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentReader), new CommandBinding(DeleteStickyNotesCommand, AnnotationHelper.OnDeleteStickyNotesCommand, AnnotationHelper.OnQueryDeleteStickyNotesCommand)); CommandManager.RegisterClassCommandBinding(typeof(FlowDocumentReader), new CommandBinding(DeleteAnnotationsCommand, AnnotationHelper.OnDeleteAnnotationsCommand, AnnotationHelper.OnQueryDeleteAnnotationsCommand)); } ////// Creates an instance of the AnnotationService focused on a particular /// DocumentViewerBase. /// /// the viewer this service will operate on ///viewer is null public AnnotationService(DocumentViewerBase viewer) { if (viewer == null) throw new ArgumentNullException("viewer"); Initialize(viewer); } ////// Creates an instance of the AnnotationService focused on a particular /// FlowDocumentScrollViewer. /// /// the viewer this service will operate on ///viewer is null public AnnotationService(FlowDocumentScrollViewer viewer) { if (viewer == null) throw new ArgumentNullException("viewer"); Initialize(viewer); } ////// Creates an instance of the AnnotationService focused on a particular /// FlowDocumentReader. /// /// the viewer this service will operate on ///viewer is null public AnnotationService(FlowDocumentReader viewer) { if (viewer == null) throw new ArgumentNullException("viewer"); Initialize(viewer); } ////// Creates an instance of the AnnotationService focused on a particular /// tree node. /// /// the tree node this service will operate on ///root is null ///element is not a FrameworkElement or FrameworkContentElement internal AnnotationService(DependencyObject root) { if (root == null) throw new ArgumentNullException("root"); if (!(root is FrameworkElement || root is FrameworkContentElement)) throw new ArgumentException(SR.Get(SRID.ParameterMustBeLogicalNode), "root"); Initialize(root); } #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods ////// Enables the service with the given store. /// /// store to use for retreiving and persisting annotations ///store is null ///DocumentViewerBase has content which is neither /// FlowDocument or FixedDocument; only those two are supported ///this service or another service is already /// enabled for the DocumentViewerBase public void Enable(AnnotationStore annotationStore) { if (annotationStore == null) throw new ArgumentNullException("annotationStore"); VerifyAccess(); if (_isEnabled) throw new InvalidOperationException(SR.Get(SRID.AnnotationServiceIsAlreadyEnabled)); // Make sure there isn't a service above or below us VerifyServiceConfiguration(_root); // Post a background work item to load existing annotations. Do this early in the // Enable method in case any later code causes an indirect call to LoadAnnotations, // this cached operation will make that LoadAnnotations call a no-op. _asyncLoadOperation = _root.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(LoadAnnotationsAsync), this); // Enable the store and set it on the tree _isEnabled = true; _root.SetValue(AnnotationService.ServiceProperty, this); _store = annotationStore; // If our root is a DocumentViewerBase we need to setup some special processors. DocumentViewerBase viewer = _root as DocumentViewerBase; if (viewer != null) { bool isFixed = viewer.Document is FixedDocument || viewer.Document is FixedDocumentSequence; bool isFlow = !isFixed && viewer.Document is FlowDocument; if (isFixed || isFlow || viewer.Document == null) { // Take care of some special processors here if (isFixed) { _locatorManager.RegisterSelectionProcessor(new FixedTextSelectionProcessor(), typeof(TextRange)); _locatorManager.RegisterSelectionProcessor(new FixedTextSelectionProcessor(), typeof(TextAnchor)); } else if (isFlow) { _locatorManager.RegisterSelectionProcessor(new TextSelectionProcessor(), typeof(TextRange)); _locatorManager.RegisterSelectionProcessor(new TextSelectionProcessor(), typeof(TextAnchor)); _locatorManager.RegisterSelectionProcessor(new TextViewSelectionProcessor(), typeof(DocumentViewerBase)); } } else { throw new InvalidOperationException(SR.Get(SRID.OnlyFlowFixedSupported)); } } // Don't register on the store until the service is ready to accept events. // Register for content change and anchor change events - we'll need to attach/detach // annotations as they are added/removed from the store or their anchors change annotationStore.StoreContentChanged += new StoreContentChangedEventHandler(OnStoreContentChanged); annotationStore.AnchorChanged += new AnnotationResourceChangedEventHandler(OnAnchorChanged); } ////// Disables annotations on the DocumentViewerBase. Any visible annotations will /// disappear. Service can be enabled again by calling Enable. /// ///service isn't enabled public void Disable() { VerifyAccess(); if (!_isEnabled) throw new InvalidOperationException(SR.Get(SRID.AnnotationServiceNotEnabled)); // If it hasn't been run yet, abort the pending operation. Still need to // unregister and unload annotations. They may have been loaded due to a // store event. if (_asyncLoadOperation != null) { _asyncLoadOperation.Abort(); _asyncLoadOperation = null; } // Unregister for changes to the store - add/deletes/anchor changes - before // unloading annotations. We don't want any events between unloading and unregistering. try { _store.StoreContentChanged -= new StoreContentChangedEventHandler(OnStoreContentChanged); _store.AnchorChanged -= new AnnotationResourceChangedEventHandler(OnAnchorChanged); } finally { IDocumentPaginatorSource document; DocumentViewerBase viewer; GetViewerAndDocument(_root, out viewer, out document); if (viewer != null) { // If our root is a DocumentViewerBase or Reader in pageView mode - // we need to unregister for changes // to its collection of DocumentPageViews. UnregisterOnDocumentViewer(viewer); } else if (document != null) { // If working with a FlowDocumentScrollViewer or a FlowDocumentReader // in Scroll mode, we need to unregister for TextViewUpdated events ITextView textView = GetTextView(document); // ITextView may have gone away already if (textView != null) { textView.Updated -= OnContentChanged; } } // Unload annotations this.UnloadAnnotations(); // This must be cleared no matter what else fails _isEnabled = false; _root.ClearValue(AnnotationService.ServiceProperty); } } ////// Returns the AnnotationService enabled for the viewer. If no service is /// enabled for the viewer, returns null. /// /// viewer to check for an enabled AnnotationService ///the AnnotationService instance for this viewer, null if a service /// is not enabled for this viewer ///viewer is null public static AnnotationService GetService(DocumentViewerBase viewer) { if (viewer == null) throw new ArgumentNullException("viewer"); return viewer.GetValue(AnnotationService.ServiceProperty) as AnnotationService; } ////// Returns the AnnotationService enabled for the reader. If no service is /// enabled for the reader, returns null. /// /// reader to check for an enabled AnnotationService ///the AnnotationService instance for this reader, null if a service /// is not enabled for this reader ///reader is null public static AnnotationService GetService(FlowDocumentReader reader) { if (reader == null) throw new ArgumentNullException("reader"); return reader.GetValue(AnnotationService.ServiceProperty) as AnnotationService; } ////// Returns the AnnotationService enabled for the viewer. If no service is /// enabled for the viewer, returns null. /// /// viewer to check for an enabled AnnotationService ///the AnnotationService instance for this viewer, null if a service /// is not enabled for this viewer ///viewer is null public static AnnotationService GetService(FlowDocumentScrollViewer viewer) { if (viewer == null) throw new ArgumentNullException("viewer"); return viewer.GetValue(AnnotationService.ServiceProperty) as AnnotationService; } #endregion Public Methods //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Loads all annotations that are found for the subtree rooted at element. /// For each annotation found and resolved in the subtree, the annotation service /// will fire an AttachedAnnotationChanged event with action AttachedAnnotation.Added. /// A call to GetAttachedAnnotations() after LoadAnnotations(element) will include the newly /// added AttachedAnnotations. /// /// root of the subtree where annotations are to be loaded ///element is null ///service is not enabled ///element is not a FrameworkElement or FrameworkContentElement internal void LoadAnnotations(DependencyObject element) { // If a LoadAnnotations call has happened before our initial asynchronous // load has happened, we should just ignore this call if (_asyncLoadOperation != null) return; if (element == null) throw new ArgumentNullException("element"); if (!(element is FrameworkElement || element is FrameworkContentElement)) throw new ArgumentException(SR.Get(SRID.ParameterMustBeLogicalNode), "element"); VerifyAccess(); if (!_isEnabled) throw new InvalidOperationException(SR.Get(SRID.AnnotationServiceNotEnabled)); //fire start trace event EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.LoadAnnotationsBegin); IListattachedAnnotations = LocatorManager.ProcessSubTree(element); LoadAnnotationsFromList(attachedAnnotations); //fire end trace event EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordAnnotation, EventTrace.Event.LoadAnnotationsEnd); } /// /// Unloads all the annotations for the subtree rooted at element. /// For each attached annotation unloaded, the annotation service will fire an /// AttachedAnnotationChanged event with AttachedAnnotationAction.Deleted. /// A call to GetAttachedAnnotations() will return a list that excludes the just /// removed AttachedAnnotations. /// Note: Multiple calls to UnloadAnnotations() at the same element are idempotent /// /// root of the subtree where annotations are to be unloaded ///element is null ///service is not enabled ///element is not a FrameworkElement or FrameworkContentElement internal void UnloadAnnotations(DependencyObject element) { if (element == null) throw new ArgumentNullException("element"); if (!(element is FrameworkElement || element is FrameworkContentElement)) throw new ArgumentException(SR.Get(SRID.ParameterMustBeLogicalNode), "element"); VerifyAccess(); if (!_isEnabled) throw new InvalidOperationException(SR.Get(SRID.AnnotationServiceNotEnabled)); // Short circuit search for annotations if the service has no attached annotations if (_annotationMap.IsEmpty) return; // We have to clear all the attached annotations underneath this DependencyObject IList attachedAnnotations = GetAllAttachedAnnotationsFor(element); UnloadAnnotationsFromList(attachedAnnotations); } ////// Unloads all attached annotations at the moment. Does not walk the tree - this is /// invoked when there is no garantee that all annotation parents are still attached /// to the visual tree /// private void UnloadAnnotations() { IList attachedAnnotations = _annotationMap.GetAllAttachedAnnotations(); UnloadAnnotationsFromList(attachedAnnotations); } ////// Returns all attached annotations that the service knows about in no particular order. /// Note: The order of the attached annotations in the list is not significant. Do not /// write code that relies on the order. /// ///a list of IAttachedAnnotations ///service is not enabled internal IListGetAttachedAnnotations() { VerifyAccess(); if (!_isEnabled) throw new InvalidOperationException(SR.Get(SRID.AnnotationServiceNotEnabled)); return _annotationMap.GetAllAttachedAnnotations(); } #endregion Internal Methods //----------------------------------------------------- // // Public Operators // //------------------------------------------------------ //----------------------------------------------------- // // Public Properties // //----------------------------------------------------- #region Public Properties /// /// Command to create Highlight annotation for the current selection. (selection > 0) /// User can pass the color as command parameter. Default yellow color is used otherwise. /// public static readonly RoutedUICommand CreateHighlightCommand = new RoutedUICommand(SR.Get(SRID.CreateHighlight), "CreateHighlight", typeof(AnnotationService), null); ////// Command to create Text StickyNote annotation for the current selection. (selection > 0) /// public static readonly RoutedUICommand CreateTextStickyNoteCommand = new RoutedUICommand(SR.Get(SRID.CreateTextNote), "CreateTextStickyNote", typeof(AnnotationService), null); ////// Command to create Ink StickyNote annotation for the current selection. (selection > 0) /// public static readonly RoutedUICommand CreateInkStickyNoteCommand = new RoutedUICommand(SR.Get(SRID.CreateInkNote), "CreateInkStickyNote", typeof(AnnotationService), null); ////// Command to clear highlight(s) for the current selection. /// public static readonly RoutedUICommand ClearHighlightsCommand = new RoutedUICommand(SR.Get(SRID.ClearHighlight), "ClearHighlights", typeof(AnnotationService), null); ////// Command to delete all Ink and Text StickyNote annotation(s) that are included in a selection. /// public static readonly RoutedUICommand DeleteStickyNotesCommand = new RoutedUICommand(SR.Get(SRID.DeleteNotes), "DeleteStickyNotes", typeof(AnnotationService), null); ////// Command to delete all Ink+Text StickyNote and highlight annotation(s) that are included in a selection. /// public static readonly RoutedUICommand DeleteAnnotationsCommand = new RoutedUICommand(SR.Get(SRID.DeleteAnnotations), "DeleteAnnotations", typeof(AnnotationService), null); ////// Returns whether or not the annotation service is enabled. /// public bool IsEnabled { get { return _isEnabled; } } ////// Soon to be public. Returns the AnnotationStore this service uses. /// public AnnotationStore Store { get { return _store; } } #endregion Public Properties //----------------------------------------------------- // // Public Dependency Properties // //------------------------------------------------------ #region Public Dependency Properties ////// Returns the AnnotationService instance for this object. If the service is not /// enabled for this object, returns null. /// /// object from which to read the attached property ///the AnnotationService instance for this object, null if the service /// is not enabled for this object ///d is null internal static AnnotationService GetService(DependencyObject d) { if (d == null) throw new ArgumentNullException("d"); return d.GetValue(AnnotationService.ServiceProperty) as AnnotationService; } ////// The chooser property holds a AnnotationComponentChooser which determines what annotation components are used /// for a given attached annotation for the subtree the DP is on. If this DP is not set, a default chooser will return the appropriate /// out of box annotation component. If the DP is set to AnnotationComponentChooser.None, no annotation components will be choosen /// for this subtree, in essence disabling the mechanism for the subtree. /// #pragma warning suppress 7009 internal static readonly DependencyProperty ChooserProperty = DependencyProperty.RegisterAttached("Chooser", typeof(AnnotationComponentChooser), typeof(AnnotationService), new FrameworkPropertyMetadata(new AnnotationComponentChooser(), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior)); ////// Return the AnnotationComponentChooser set in the Chooser DP. /// /// Object from which to get the DP. ///AnnotationComponentChooser to use for this subtree internal static AnnotationComponentChooser GetChooser(DependencyObject d) { if (d == null) throw new ArgumentNullException("d"); return (AnnotationComponentChooser)d.GetValue(ChooserProperty); } ////// DependencyProperty used to specify the processor to use for a /// given subtree. When set on a node, all nodes below it will be /// processed with the specified processor, unless overriden. Setting /// this property after annotations have been loaded will have no effect /// on existing annotations. If you want to change how the tree is /// processed, you should set this property and call LoadAnnotations/ /// UnloadAnnotations on the service. /// #pragma warning suppress 7009 internal static readonly DependencyProperty SubTreeProcessorIdProperty = LocatorManager.SubTreeProcessorIdProperty.AddOwner(typeof(AnnotationService)); ////// Sets the value of the SubTreeProcessorId attached property /// of the LocatorManager class. /// /// object to which to write the attached property /// the DP value to set ///d is null internal static void SetSubTreeProcessorId(DependencyObject d, String id) { if (d == null) throw new ArgumentNullException("d"); if (id == null) throw new ArgumentNullException("id"); //d will check the context if needed d.SetValue(SubTreeProcessorIdProperty, id); } ////// Gets the value of the SubTreeProcessorId attached property /// of the LocatorManager class. /// /// the object from which to read the attached property ///the value of the SubTreeProcessorId attached property ///d is null internal static String GetSubTreeProcessorId(DependencyObject d) { if (d == null) throw new ArgumentNullException("d"); //d will check the context if needed return d.GetValue(SubTreeProcessorIdProperty) as String; } ////// Used to specify a unique id for the data represented by a /// logical tree node. Attach this property to the element with a /// unique value. /// #pragma warning suppress 7009 internal static readonly DependencyProperty DataIdProperty = DataIdProcessor.DataIdProperty.AddOwner(typeof(AnnotationService)); ////// Sets the value of the DataId attached property /// of the LocatorManager class. /// /// element to which to write the attached property /// the value to set ///d is null internal static void SetDataId(DependencyObject d, String id) { if (d == null) throw new ArgumentNullException("d"); if (id == null) throw new ArgumentNullException("id"); //d will check the context if needed d.SetValue(DataIdProperty, id); } ////// Gets the value of the DataId attached property /// of the LocatorManager class. /// /// the object from which to read the attached property ///the value of the DataId attached property ///d is null internal static String GetDataId(DependencyObject d) { if (d == null) throw new ArgumentNullException("d"); //d will check the context if needed return d.GetValue(DataIdProperty) as String; } #endregion Public Dependency Properties //----------------------------------------------------- // // Public Events // //------------------------------------------------------ #region Public Events ////// event that notifies the registered objects, typically the annotation panel, with the Add/Delete/Modify /// changes to the AttachedAnnotations /// internal event AttachedAnnotationChangedEventHandler AttachedAnnotationChanged; #endregion Public Events //------------------------------------------------------ // // Internal Properties // //----------------------------------------------------- #region Internal Properties ////// Gets the LocatorManager instance. /// internal LocatorManager LocatorManager { get { return _locatorManager; } } ////// The node the service is enabled on. This used by the LocatorManager /// when resolving a locator that must be resolved from the 'top' of the /// annotatable tree. /// internal DependencyObject Root { get { return _root; } } #endregion Internal Properties //------------------------------------------------------ // // Internal Fields // //----------------------------------------------------- #region Internal Fields ////// ServiceProperty for the AnnotationService. Used to retrieve the service object /// in order to make direct calls such as CreateAnnotation. Kept internal because /// PathNode uses it to short-circuit the path building when there is a service set. /// #pragma warning suppress 7009 internal static readonly DependencyProperty ServiceProperty = DependencyProperty.RegisterAttached("Service", typeof(AnnotationService), typeof(AnnotationService), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior)); #endregion Internal Fields //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- #region Private Methods ////// Initializes the service on this tree node. /// /// the tree node the service is being created on private void Initialize(DependencyObject root) { Invariant.Assert(root != null, "Parameter 'root' is null."); // Root of the subtree this service will operate on _root = root; // Object that resolves and generates locators _locatorManager = new LocatorManager(); // sets up dependency for AnnotationComponentManager _annotationComponentManager = new AnnotationComponentManager(this); //set known component types priorities AdornerPresentationContext.SetTypeZLevel(typeof(StickyNoteControl), 0); AdornerPresentationContext.SetTypeZLevel(typeof(MarkedHighlightComponent), 1); AdornerPresentationContext.SetTypeZLevel(typeof(HighlightComponent), 1); //set ZRanges for the ZLevels AdornerPresentationContext.SetZLevelRange(0, Int32.MaxValue / 2 + 1, Int32.MaxValue); AdornerPresentationContext.SetZLevelRange(1, 0, 0); } ////// Queue work item - put on the queue when the service is first enabled. /// This method causes the service's annotations to be loaded. /// If the service is disabled before this work item is run, this work item /// does nothing. /// /// the service being enabled ///Always returns null private object LoadAnnotationsAsync(object obj) { Invariant.Assert(_isEnabled, "Service was disabled before attach operation executed."); // Operation is now being executed so we can clear the cached operation _asyncLoadOperation = null; IDocumentPaginatorSource document = null; DocumentViewerBase documentViewerBase; GetViewerAndDocument(Root, out documentViewerBase, out document); if (documentViewerBase != null) { // If our root is a DocumentViewerBase - we need to register for changes // to its collection of DocumentPageViews. This allows us to unload/load // annotations as the DPVs change. Prevents us from loading an annotation // because of a store event and not unloading it becuase we havent' registered // on the DPV yet. RegisterOnDocumentViewer(documentViewerBase); } else if (document != null) { // If working with a FlowDocumentScrollViewer or FlowDocumentReader // in Scroll mode, we must register on ITextView to // get notified about relayout changes. For the ScrollViewer OnTextViewUpdated // happens synchronously which is not true for the FDPV - we register OnPageConnected // for each page there. ITextView textView = GetTextView(document); if (textView != null) { textView.Updated += OnContentChanged; } } //if there are too many visible annotations the application will freeze if we load them // synchronously - so do it in batches IListattachedAnnotations = LocatorManager.ProcessSubTree(_root); LoadAnnotationsFromListAsync(attachedAnnotations); return null; } /// /// Queue work item - put on the queue when the service is first enabled. /// This method causes the service's annotations to be loaded. /// If the service is disabled before this work item is run, this work item /// does nothing. /// /// list of annotations to be loaded ///Always returns null private object LoadAnnotationsFromListAsync(object obj) { //if we are executed from the Dispather queue _asyncLoadFromListOperation // will point to us. Set it to null, to avoid Abort _asyncLoadFromListOperation = null; Listannotations = obj as List ; if (annotations == null) return null; if (annotations.Count > _maxAnnotationsBatch) { //put the overhead in a new array List leftover = new List (annotations.Count); leftover = annotations.GetRange(_maxAnnotationsBatch, annotations.Count - _maxAnnotationsBatch); //put another item in the queue for the rest _asyncLoadFromListOperation = _root.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(LoadAnnotationsFromListAsync), leftover); //trim the annotations to process list annotations.RemoveRange(_maxAnnotationsBatch, annotations.Count - _maxAnnotationsBatch); } LoadAnnotationsFromList(annotations); return null; } /// /// Check if the attachment level of the two attached annotations is equal, if the /// two anchoes are TextAnchors and are equal /// /// first attached annotation /// second attached annotation ///true if the attachment level is equal and the two anchors are equal TextAnchors private bool AttachedAnchorsEqual(IAttachedAnnotation firstAttachedAnnotation, IAttachedAnnotation secondAttachedAnnotation) { //the annotation exists, but we do not know if the attached anchor has //changed, so we we always modify it object oldAttachedAnchor = firstAttachedAnnotation.AttachedAnchor; // WorkItem 41404 - Until we have a design that lets us get around // anchor specifics in the service we need this here. // If the attachment levels are the same, we want to test to see if the // anchors are the same as well - if they are we do nothing if (firstAttachedAnnotation.AttachmentLevel == secondAttachedAnnotation.AttachmentLevel) { // If new anchor is text anchor - compare it to old anchor to // detect if any changes actually happened. TextAnchor newAnchor = secondAttachedAnnotation.AttachedAnchor as TextAnchor; if (newAnchor != null) { if (newAnchor.Equals(oldAttachedAnchor)) return true; } } return false; } ////// Loads all annotations that are found for the subtree rooted at element. /// For each annotation found and resolved in the subtree, the annotation service /// will fire an AttachedAnnotationChanged event with action AttachedAnnotation.Added. /// A call to GetAttachedAnnotations() after LoadAnnotations(element) will include the newly /// added AttachedAnnotations. /// /// list of attachedAnnotations to be processed ///element is null ///service is not enabled ///element is not a FrameworkElement or FrameworkContentElement private void LoadAnnotationsFromList(IListattachedAnnotations) { Invariant.Assert(attachedAnnotations != null, "null attachedAnnotations list"); List eventsToFire = new List (attachedAnnotations.Count); IAttachedAnnotation matchingAnnotation = null; foreach (IAttachedAnnotation attachedAnnotation in attachedAnnotations) { Invariant.Assert((attachedAnnotation != null) && (attachedAnnotation.Annotation != null), "invalid attached annotation"); matchingAnnotation = FindAnnotationInList(attachedAnnotation, _annotationMap.GetAttachedAnnotations(attachedAnnotation.Annotation.Id)); if (matchingAnnotation != null) { //the annotation exists, but we do not know if the attached anchor has //changed, so we we always modify it object oldAttachedAnchor = matchingAnnotation.AttachedAnchor; AttachmentLevel oldAttachmentLevel = matchingAnnotation.AttachmentLevel; if (attachedAnnotation.AttachmentLevel != AttachmentLevel.Unresolved && attachedAnnotation.AttachmentLevel != AttachmentLevel.Incomplete) { if (AttachedAnchorsEqual(matchingAnnotation, attachedAnnotation)) continue; ((AttachedAnnotation)matchingAnnotation).Update(attachedAnnotation.AttachedAnchor, attachedAnnotation.AttachmentLevel, null); FullyResolveAnchor(matchingAnnotation); eventsToFire.Add(AttachedAnnotationChangedEventArgs.Modified(matchingAnnotation, oldAttachedAnchor, oldAttachmentLevel)); } // It went from being attached to being partially attached. Since we don't // support attaching partially resolved anchors we remove it here else { DoRemoveAttachedAnnotation(attachedAnnotation); eventsToFire.Add(AttachedAnnotationChangedEventArgs.Unloaded(attachedAnnotation)); } } else { if (attachedAnnotation.AttachmentLevel != AttachmentLevel.Unresolved && attachedAnnotation.AttachmentLevel != AttachmentLevel.Incomplete) { DoAddAttachedAnnotation(attachedAnnotation); eventsToFire.Add(AttachedAnnotationChangedEventArgs.Loaded(attachedAnnotation)); } } } FireEvents(eventsToFire); } /// /// Unloads all the annotations from the list /// /// list of annotations private void UnloadAnnotationsFromList(IList attachedAnnotations) { Invariant.Assert(attachedAnnotations != null, "null attachedAnnotations list"); ListeventsToFire = new List (attachedAnnotations.Count); foreach (IAttachedAnnotation attachedAnnotation in attachedAnnotations) { DoRemoveAttachedAnnotation(attachedAnnotation); eventsToFire.Add(AttachedAnnotationChangedEventArgs.Unloaded(attachedAnnotation)); } FireEvents(eventsToFire); } /// /// LayoutUpdate event handler /// /// event sender (not used) /// event arguments (not used) private void OnLayoutUpdated(object sender, EventArgs args) { // Unregister for the event UIElement root = _root as UIElement; if (root != null) root.LayoutUpdated -= OnLayoutUpdated; UpdateAnnotations(); } ////// Updates the set of attached annotations. Unneeded annotations are unloaded /// the annotations that were there and must stay are invalidated, the annotations that /// must be added are added in batches asinchronously to avoid freezing of the application /// private void UpdateAnnotations() { // If a UpdateAnnotations call has happened before our initial asynchronous // load has finished, we should just ignore this call if (_asyncLoadOperation != null) return; //the service must be Enabled if (!_isEnabled) return; IListattachedAnnotations = null; IList dirtyAnnotations = new List (); //first get the list of annotations to be attachedAnnotations = LocatorManager.ProcessSubTree(_root); //now make list of annotations to remove List existingAnnotations = _annotationMap.GetAllAttachedAnnotations(); for (int i = existingAnnotations.Count - 1; i >= 0; i--) { IAttachedAnnotation match = FindAnnotationInList(existingAnnotations[i], attachedAnnotations); if ((match != null) && AttachedAnchorsEqual(match, existingAnnotations[i])) { //we do not need to load the matching one attachedAnnotations.Remove(match); dirtyAnnotations.Add(existingAnnotations[i]); //we do not need to unload the existing one too existingAnnotations.RemoveAt(i); } } //now every thing that is left in existingAnnotations must be removed if ((existingAnnotations != null) && (existingAnnotations.Count > 0)) { UnloadAnnotationsFromList(existingAnnotations); } //invalidate the annotations that are left IList processedElements = new List (); foreach (IAttachedAnnotation annotation in dirtyAnnotations) { UIElement parent = annotation.Parent as UIElement; if ((parent != null) && !processedElements.Contains(parent)) { processedElements.Add(parent); InvalidateAdorners(parent); } } if (_asyncLoadFromListOperation != null) { //stop this one - we will set a new one in the queue _asyncLoadFromListOperation.Abort(); _asyncLoadFromListOperation = null; } if ((attachedAnnotations != null) && (attachedAnnotations.Count > 0)) LoadAnnotationsFromListAsync(attachedAnnotations); } /// /// Mark all AnnotationAdorners that Annotate this element as Dirty /// annotated element /// private static void InvalidateAdorners(UIElement element) { AdornerLayer layer = AdornerLayer.GetAdornerLayer(element); if (layer != null) { //mark the components that adorn the same element as dirty Adorner[] adorners = layer.GetAdorners(element); if (adorners != null) { for (int i = 0; i < adorners.Length; i++) { AnnotationAdorner adorner = adorners[i] as AnnotationAdorner; if (adorner != null) { Invariant.Assert(adorner.AnnotationComponent != null, "AnnotationAdorner component undefined"); adorner.AnnotationComponent.IsDirty = true; } } layer.Update(element); } } } ////// Verify that no other annotation services are attached above, on or below root /// /// the proposed root for a new AnnotationService being enabled ///Other Instance of AnnotationService Is Already Set static private void VerifyServiceConfiguration(DependencyObject root) { Invariant.Assert(root != null, "Parameter 'root' is null."); // First make sure there isn't a service above us if (AnnotationService.GetService(root) != null) throw new InvalidOperationException(SR.Get(SRID.AnnotationServiceAlreadyExists)); // Now check the tree below us for an existing service DescendentsWalker
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PreviewPrintController.cs
- UnsafeNativeMethods.cs
- ExpressionTable.cs
- HttpChannelHelper.cs
- DependencyPropertyConverter.cs
- DataException.cs
- DbTypeMap.cs
- Compilation.cs
- XmlText.cs
- GZipStream.cs
- PeerTransportCredentialType.cs
- DbProviderFactoriesConfigurationHandler.cs
- UrlAuthFailedErrorFormatter.cs
- ConcatQueryOperator.cs
- SqlUtils.cs
- EdmToObjectNamespaceMap.cs
- IndexingContentUnit.cs
- XsltQilFactory.cs
- CharacterBuffer.cs
- XmlTextReaderImplHelpers.cs
- CheckBoxStandardAdapter.cs
- StylusPointDescription.cs
- StringHandle.cs
- FormViewDeletedEventArgs.cs
- XmlMembersMapping.cs
- DataGridRow.cs
- UrlPropertyAttribute.cs
- Transform.cs
- QuadraticBezierSegment.cs
- CachedCompositeFamily.cs
- UrlPath.cs
- XPathNodeList.cs
- ScriptingScriptResourceHandlerSection.cs
- XmlRootAttribute.cs
- CustomSignedXml.cs
- HebrewNumber.cs
- AttributeProviderAttribute.cs
- DispatcherHookEventArgs.cs
- IsolatedStorageFilePermission.cs
- ImageMapEventArgs.cs
- SchemaTableOptionalColumn.cs
- BitSet.cs
- AutomationPatternInfo.cs
- TypeElementCollection.cs
- DrawingContextWalker.cs
- diagnosticsswitches.cs
- MsmqInputMessage.cs
- AppLevelCompilationSectionCache.cs
- StoryFragments.cs
- SessionStateModule.cs
- InfoCardHelper.cs
- Point3DCollectionValueSerializer.cs
- InputLanguageManager.cs
- RuntimeConfig.cs
- MatrixValueSerializer.cs
- BasicDesignerLoader.cs
- CharConverter.cs
- CredentialCache.cs
- ErrorInfoXmlDocument.cs
- OleDbCommandBuilder.cs
- EmptyEnumerator.cs
- QueueNameHelper.cs
- RadioButton.cs
- AttachedAnnotationChangedEventArgs.cs
- ArrangedElement.cs
- PackageProperties.cs
- AutoResetEvent.cs
- ArgIterator.cs
- ListViewItem.cs
- AstTree.cs
- Random.cs
- ServiceBehaviorElementCollection.cs
- ListManagerBindingsCollection.cs
- mediapermission.cs
- PolicyConversionContext.cs
- ValueChangedEventManager.cs
- DesignerToolStripControlHost.cs
- SerialPinChanges.cs
- Internal.cs
- dbenumerator.cs
- SQLBytesStorage.cs
- TableLayoutStyle.cs
- AuthenticationSection.cs
- ExclusiveHandle.cs
- XmlSchemaCompilationSettings.cs
- TreeSet.cs
- PolyLineSegment.cs
- ReadOnlyMetadataCollection.cs
- AdornerPresentationContext.cs
- TriggerCollection.cs
- XPathException.cs
- PropertyFilterAttribute.cs
- NotEqual.cs
- LineInfo.cs
- SendActivityValidator.cs
- WebPartDescriptionCollection.cs
- FlowDocumentPage.cs
- ComPlusInstanceContextInitializer.cs
- ScriptModule.cs
- GenericPrincipal.cs