Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / System / Windows / Controls / StickyNote.cs / 1 / StickyNote.cs
// In order to disable Presharp warning 6507 - Prefer 'string.IsNullOrEmpty(value)' over checks for null and/or emptiness, // we have to disable warnings 1634 and 1691 to make the compiler happy first. #pragma warning disable 1634, 1691 //---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Implementation of StickyNoteControl control. // // See spec at http://tabletpc/longhorn/Specs/StickyNoteControlSpec.mht // // History: // 04/19/2004 - waynezen - Expose more properties and methods // 02/17/2004 - waynezen - Moved to TabletFramework // 10/06/2003 - waynezen - Added Ink and Snippet Image supports // 09/23/2003 - waynezen - Ported to WCP. // 12/16/2002 - waynezen - Created. // //--------------------------------------------------------------------------- using System; using System.ComponentModel; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; // Assert using System.Globalization; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Serialization; using Microsoft.Win32; // SystemEvents using MS.Internal; using MS.Internal.Annotations.Component; using MS.Internal.Controls.StickyNote; using MS.Internal.Commands; using MS.Internal.KnownBoxes; using MS.Internal.PresentationFramework; using System.Windows.Threading; using System.Windows; using System.Windows.Data; using System.Windows.Annotations; using System.Windows.Automation; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Text; using System.Text.RegularExpressions; using MS.Utility; namespace System.Windows.Controls { ////// The type of data handled by a StickyNoteControl. /// public enum StickyNoteType { ////// Text StickyNote /// Text, ////// Ink StickyNote /// Ink, } ////// StickyNoteControl control intends to be an UI control for user to make annotation. /// [TemplatePart(Name = SNBConstants.c_CloseButtonId, Type = typeof(Button))] [TemplatePart(Name = SNBConstants.c_TitleThumbId, Type = typeof(Thumb))] [TemplatePart(Name = SNBConstants.c_BottomRightResizeThumbId, Type = typeof(Thumb))] [TemplatePart(Name = SNBConstants.c_ContentControlId, Type = typeof(ContentControl))] [TemplatePart(Name = SNBConstants.c_IconButtonId, Type = typeof(Button))] [TemplatePart(Name = SNBConstants.c_CopyMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_PasteMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_InkMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_SelectMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_EraseMenuId, Type = typeof(MenuItem))] public sealed partial class StickyNoteControl : Control, IAnnotationComponent { //------------------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------------------- #region Constructors ////// The static constructor /// static StickyNoteControl() { Type owner = typeof(StickyNoteControl); // Register event handlers // NTRAID-WINDOWS#1136774-WAYNEZEN, // We want to bring a note to front when the input device is pressed down on it at the first time. // StickyNote invokes PresentationContext.BringToFront method to achieve it. // Eventually the Annotation AdornerLayer will re-arrange index of its children to change their "Z-Order". // So, the BringToFront could temporarily remove StickyNote from the visual tree. // But the hosted InkCanvas might capture Stylus for collecting packets. If the host note is removed from the tree, the capture // will be released automatically which will mess up the InkCanvas' states. // So we have to do BringToFront before InkCanvas does capture. Adding a handler of PreviewStylusDownEvent fixes the problem. // Unfortunately we still need the existing handler of PreviewMouseDownEvent since there is no stylus event for text StickyNote. // EventManager.RegisterClassHandler(owner, Stylus.PreviewStylusDownEvent, new StylusDownEventHandler(_OnPreviewDeviceDown)); EventManager.RegisterClassHandler(owner, Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler(_OnPreviewDeviceDown )); EventManager.RegisterClassHandler(owner, Mouse.MouseDownEvent, new MouseButtonEventHandler(_OnDeviceDown )); EventManager.RegisterClassHandler(owner, ContextMenuService.ContextMenuOpeningEvent, new ContextMenuEventHandler(_OnContextMenuOpening)); CommandHelpers.RegisterCommandHandler(typeof(StickyNoteControl), StickyNoteControl.DeleteNoteCommand, new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled)); CommandHelpers.RegisterCommandHandler(typeof(StickyNoteControl), StickyNoteControl.InkCommand, new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled)); // // set the main style CRK to the default theme style key // DefaultStyleKeyProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata( new ComponentResourceKey(typeof(PresentationUIStyleResources), "StickyNoteControlStyleKey"))); KeyboardNavigation.TabNavigationProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(KeyboardNavigationMode.Local)); Control.IsTabStopProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(false)); // Override the changed callback of the Foreground Property. ForegroundProperty.OverrideMetadata( owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(_UpdateInkDrawingAttributes))); FontFamilyProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontSizeProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontStretchProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontStyleProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontWeightProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); } /// /// This is an instance constructor for StickyNoteControl class. Should not be used /// private StickyNoteControl() : this(StickyNoteType.Text) { } ////// Creates an instance of StickyNoteControl that handles the specified type. /// /// the type of data to be handled by the StickyNoteControl internal StickyNoteControl(StickyNoteType type) : base() { _stickyNoteType = type; SetValue(StickyNoteTypePropertyKey, type); InitStickyNoteControl(); } #endregion // Constructors //-------------------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------------------- #region Public Methods ////// Override OnApplyTemplate method. This method will ensure whether the created visual is one which /// StickyNoteControl expects. If so, the internal controls will be cached and Annotation will be applied /// to the UI status. /// ///Whether Visuals were added to the tree public override void OnApplyTemplate() { // No need for calling VerifyAccess since we call the method on the base here. base.OnApplyTemplate(); // Ensure the type if (this.IsExpanded) { EnsureStickyNoteType(); } // Setup the inner controls with the Annotation's data UpdateSNCWithAnnotation(SNCAnnotation.AllValues); // If the visual tree just gets populated from the new style, we have to add our event handles to the individual // elements. if (!this.IsExpanded) { Button button = GetIconButton(); if (button != null) { button.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } } else { Button closeButton = GetCloseButton(); if (closeButton != null) { closeButton.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } Thumb titleThumb = GetTitleThumb(); if (titleThumb != null) { titleThumb.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); titleThumb.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } Thumb resizeThumb = GetResizeThumb(); if (resizeThumb != null) { resizeThumb.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); resizeThumb.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } // Set up the bindings to the menuitems. SetupMenu(); } } #endregion // Public Methods //-------------------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------------------- #region Public Properties ////// The property key of Author /// internal static readonly DependencyPropertyKey AuthorPropertyKey = DependencyProperty.RegisterReadOnly( "Author", typeof(string), typeof(StickyNoteControl), new FrameworkPropertyMetadata(String.Empty)); ////// Author Dependency Property /// public static readonly DependencyProperty AuthorProperty = AuthorPropertyKey.DependencyProperty; ////// Returns the author of the annotation the StickyNoteControl is editing. /// public String Author { get { return (String)GetValue(StickyNoteControl.AuthorProperty); } } ////// Gets/Sets the expanded or minimized state of the StickyNoteControl. /// If Expanded=false, the bubble is in a minimized state. /// public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register( "IsExpanded", typeof(bool), typeof(StickyNoteControl), new FrameworkPropertyMetadata( BooleanBoxes.TrueBox, new PropertyChangedCallback(_OnIsExpandedChanged))); ////// Gets/Sets the expanded or minimized state of the StickyNoteControl. /// public bool IsExpanded { get { return (bool) GetValue(IsExpandedProperty); } set { SetValue(IsExpandedProperty, value); } } ////// true - StickyNoteControl is active (i.e. has the focus or selection includes part of its anchor) ///The value of this property is set by the MarkedHighlightComponent which controls the SN state /// public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(StickyNoteControl), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits)); ////// The state of StickyNoteControl - true : active, false:inactive /// public bool IsActive { get { return (bool)GetValue(StickyNoteControl.IsActiveProperty); } } ////// If true, mouse is over the SticyNoteControl's anchor (which could be used to change its appearance /// in its style). /// internal static readonly DependencyPropertyKey IsMouseOverAnchorPropertyKey = DependencyProperty.RegisterReadOnly("IsMouseOverAnchor", typeof(bool), typeof(StickyNoteControl), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// If true, mouse is over the SticyNoteControl's anchor (which could be used to change its appearance /// in its style). /// public static readonly DependencyProperty IsMouseOverAnchorProperty = IsMouseOverAnchorPropertyKey.DependencyProperty; ////// True if the mouse is over the StickyNote anchor, false otherwise /// public bool IsMouseOverAnchor { get { return (bool) GetValue(StickyNoteControl.IsMouseOverAnchorProperty); } } ////// The DependencyProperty for the CaptionFontFamily property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font /// public static readonly DependencyProperty CaptionFontFamilyProperty = DependencyProperty.Register( "CaptionFontFamily", typeof(FontFamily), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontFamily, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The caption font family /// public FontFamily CaptionFontFamily { get { return (FontFamily) GetValue(CaptionFontFamilyProperty); } set { SetValue(CaptionFontFamilyProperty, value); } } ////// The DependencyProperty for the CaptionFontSize property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font Size /// public static readonly DependencyProperty CaptionFontSizeProperty = DependencyProperty.Register( "CaptionFontSize", typeof(double), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The size of the caption font. /// public double CaptionFontSize { get { return (double) GetValue(CaptionFontSizeProperty); } set { SetValue(CaptionFontSizeProperty, value); } } ////// The DependencyProperty for the CaptionFontStretch property. /// Flags: Can be used in style rules /// Default Value: FontStretches.Normal /// public static readonly DependencyProperty CaptionFontStretchProperty = DependencyProperty.Register( "CaptionFontStretch", typeof(FontStretch), typeof(StickyNoteControl), new FrameworkPropertyMetadata( FontStretches.Normal, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The stretch of the caption font. /// public FontStretch CaptionFontStretch { get { return (FontStretch) GetValue(CaptionFontStretchProperty); } set { SetValue(CaptionFontStretchProperty, value); } } ////// The DependencyProperty for the CaptionFontStyle property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font Style /// public static readonly DependencyProperty CaptionFontStyleProperty = DependencyProperty.Register( "CaptionFontStyle", typeof(FontStyle), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontStyle, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The style of the caption font. /// public FontStyle CaptionFontStyle { get { return (FontStyle) GetValue(CaptionFontStyleProperty); } set { SetValue(CaptionFontStyleProperty, value); } } ////// The DependencyProperty for the CaptionFontWeight property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font Weight /// public static readonly DependencyProperty CaptionFontWeightProperty = DependencyProperty.Register( "CaptionFontWeight", typeof(FontWeight), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontWeight, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The weight or thickness of the caption font. /// public FontWeight CaptionFontWeight { get { return (FontWeight) GetValue(CaptionFontWeightProperty); } set { SetValue(CaptionFontWeightProperty, value); } } ////// The DependencyProperty for the PenWidth property. /// Flags: Can be used in style rules /// Default Value: The default width of System.Windows.Ink.DrawingAttributes. /// public static readonly DependencyProperty PenWidthProperty = DependencyProperty.Register( "PenWidth", typeof(double), typeof(StickyNoteControl), new FrameworkPropertyMetadata( (new DrawingAttributes()).Width, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(_UpdateInkDrawingAttributes))); ////// The width of the pen for the ink StickyNoteControl. /// public double PenWidth { get { return (double) GetValue(PenWidthProperty); } set { SetValue(PenWidthProperty, value); } } ////// StickyNoteType Property Key /// private static readonly DependencyPropertyKey StickyNoteTypePropertyKey = DependencyProperty.RegisterReadOnly( "StickyNoteType", typeof(StickyNoteType), typeof(StickyNoteControl), new FrameworkPropertyMetadata(StickyNoteType.Text)); ////// StickyNoteType Dependency Property /// public static readonly DependencyProperty StickyNoteTypeProperty = StickyNoteTypePropertyKey.DependencyProperty; ////// Gets StickyNoteType Property /// public StickyNoteType StickyNoteType { get{ return (StickyNoteType) GetValue(StickyNoteTypeProperty); } } ////// Returns the Annotation this StickyNote is representing. /// public IAnchorInfo AnchorInfo { get { if (_attachedAnnotation != null) return _attachedAnnotation; return null; } } #endregion Public Properties //------------------------------------------------------------------------------- // // Public Commands // //-------------------------------------------------------------------------------- #region Public Commands ////// Delete a note /// public static readonly RoutedCommand DeleteNoteCommand = new RoutedCommand("DeleteNote", typeof(StickyNoteControl)); ////// Ink Mode /// public static readonly RoutedCommand InkCommand = new RoutedCommand("Ink", typeof(StickyNoteControl)); #endregion Public Commands //------------------------------------------------------------------------------- // // Protected Methods // //------------------------------------------------------------------------------- #region Protected Methods ////// Called whenever the template changes. /// /// Old template /// New template protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate) { // No need for calling VerifyAccess since we call the method on the base here. base.OnTemplateChanged(oldTemplate, newTemplate); // If StickyNote's control template has been changed, we should invalidate current cached controls. ClearCachedControls(); } ////// Called whenever focus enters or leaves the StickyNoteControl. This includes when focus /// is set/removed from any element within the StickyNoteControl. /// /// arguments describing the property change protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs args) { base.OnIsKeyboardFocusWithinChanged(args); // If we lost focus due to a context menu on some element within us, // we don't consider that a loss of focus. We simply exit early. ContextMenu menu = Keyboard.FocusedElement as ContextMenu; if (menu != null) { if (menu.PlacementTarget != null && menu.PlacementTarget.IsDescendantOf(this)) { return; } } // Must update our anchor that we are now focused or not focused. _anchor.Focused = IsKeyboardFocusWithin; } ////// An event announcing that the keyboard is focused on this bubble. /// /// >FocusChangedEvent Argument protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs args) { // No need for calling VerifyAccess since we call the method on the base here. base.OnGotKeyboardFocus(args); // Dwayne's Input BC(#73066) changed the behavior of mouse button event. // So we could get GotKeyboardFocus event without the visual being set up. // Now, we verify the visual is ready by invoking ApplyTemplate. ApplyTemplate(); // We are interested in the expanded note. if ( IsExpanded == true ) { Invariant.Assert(Content != null); BringToFront(); // If focus was set on us, we should set the focus on our inner control if ( args.NewFocus == this ) { UIElement innerControl = this.Content.InnerControl as UIElement; Invariant.Assert(innerControl != null, "InnerControl is null or not a UIElement."); // Don't mess with focus if its already on our inner control if ( innerControl.IsKeyboardFocused == false ) { // We should set the focus to the inner control after it is added the visual tree. innerControl.Focus(); } } } } #endregion // Protected Methods //------------------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------------------- #region Internal Methods ////// Creates our inner control and adds it to the tree. If the inner control /// already exists we verify its of the right type for our current data. /// This is called when the template is applied or when the annotation for this /// control is set. /// private void EnsureStickyNoteType() { UIElement contentContainer = GetContentContainer(); // Check whether we need to recreate a content control based on the new type. if (_contentControl != null) { // Check if the type has been changed if (_contentControl.Type != _stickyNoteType) { // Recreate the content control when the type has been changed. DisconnectContent(); _contentControl = StickyNoteContentControlFactory.CreateContentControl(_stickyNoteType, contentContainer); ConnectContent(); } } else { _contentControl = StickyNoteContentControlFactory.CreateContentControl(_stickyNoteType, contentContainer); ConnectContent(); } } ////// When our inner control is changing, we disconnect from the existing one /// by unregistering for events and removing the control from the visual tree. /// private void DisconnectContent() { Invariant.Assert(Content != null, "Content is null."); // Unregister for all events and clear bindings StopListenToContentControlEvent(); UnbindContentControlProperties(); _contentControl = null; } ////// When a new content control is created we register on different portions of /// the visual tree for certain events. /// private void ConnectContent() { Invariant.Assert(Content != null); // Set the default inking mode and attributes InkCanvas innerInkCanvas = Content.InnerControl as InkCanvas; if (innerInkCanvas != null) { // Create the event handlers we'll use for ink notes InitializeEventHandlers(); // We set the value on the StickyNoteControl which eventually gets // set on the InkCanvas. The property on the InkCanvas isn't a DP // we can't create a one-way binding as would be preferred. this.SetValue(InkEditingModeProperty, InkCanvasEditingMode.Ink); UpdateInkDrawingAttributes(); } // Register for events and setup bindings StartListenToContentControlEvent(); BindContentControlProperties(); } ////// Returns the Content element for this StickyNoteControl. Should never /// be null when IsExpanded = true. /// internal StickyNoteContentControl Content { get { return _contentControl; } } ////// Returns the button used to close the StickyNoteControl (it actually sets IsExpanded to false). /// private Button GetCloseButton() { return GetTemplateChild(SNBConstants.c_CloseButtonId) as Button; } ////// Returns the button when the StickyNoteControl has IsExpanded=false. /// private Button GetIconButton() { return GetTemplateChild(SNBConstants.c_IconButtonId) as Button; } ////// Returns the thumb that controls the dragging of the StickyNote as a whole. /// private Thumb GetTitleThumb() { return GetTemplateChild(SNBConstants.c_TitleThumbId) as Thumb; } ////// Return the ContentControl viewer in the StickyNoteControl. /// private UIElement GetContentContainer() { return GetTemplateChild(SNBConstants.c_ContentControlId) as UIElement; } ////// Return the resize thumb in the StickyNoteControl. /// private Thumb GetResizeThumb() { return GetTemplateChild(SNBConstants.c_BottomRightResizeThumbId) as Thumb; } #endregion Internal Methods //------------------------------------------------------------------------------- // // Internal Properties // //-------------------------------------------------------------------------------- #region Internal Properties ////// InkEditingMode Property Key /// private static readonly DependencyProperty InkEditingModeProperty = DependencyProperty.Register( "InkEditingMode", typeof(InkCanvasEditingMode), typeof(StickyNoteControl), new FrameworkPropertyMetadata( InkCanvasEditingMode.None)); ////// Gets/Sets the dirty state of the control. /// private bool IsDirty { get { return _dirty; } set { _dirty = value; } } #endregion // Internal Properties //-------------------------------------------------------------------------------- // // Private Methods // //------------------------------------------------------------------------------- #region Private Methods ////// Called when a StickyNoteControl's IsExpanded property changes. Simply /// pass it on to the StickyNoteControl instance. /// private static void _OnIsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { StickyNoteControl snc = (StickyNoteControl)d; snc.OnIsExpandedChanged(); } private static void OnFontPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { StickyNoteControl stickyNoteControl = (StickyNoteControl)d; if (stickyNoteControl.Content != null && stickyNoteControl.Content.Type != StickyNoteType.Ink) { FrameworkElement innerControl = stickyNoteControl.Content.InnerControl; if (innerControl != null) innerControl.SetValue(e.Property, e.NewValue); } } ////// The changed callback attached to the Foreground and PenWidth DPs /// private static void _UpdateInkDrawingAttributes(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Update the DrawingAttributes StickyNoteControl stickyNoteControl = (StickyNoteControl)d; stickyNoteControl.UpdateInkDrawingAttributes(); if (e.Property == ForegroundProperty && stickyNoteControl.Content != null && stickyNoteControl.Content.Type != StickyNoteType.Ink) { FrameworkElement innerControl = stickyNoteControl.Content.InnerControl; if (innerControl != null) innerControl.SetValue(ForegroundProperty, e.NewValue); } } // A class handler for TextBox.TextChanged events // obj - the event sender // args - Event argument private void OnTextChanged(object obj, TextChangedEventArgs args) { // We must update the annotation asynchronously because we can't Since Textbox doesn't allow any layout measurement during its content is changing, we have to do // the update asynchronously. We also prevent updating the annotation when the content in the // textbox was changed due to a change in the annotation itself. if (!InternalLocker.IsLocked(LockHelper.LockFlag.DataChanged)) { //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONTEXTCHANGEDGUID, EventType.StartEvent); AsyncUpdateAnnotation(XmlToken.Text); //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(AsyncUpdateAnnotation), XmlToken.Text); IsDirty = true; //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONTEXTCHANGEDGUID, EventType.EndEvent); } } private static void _OnDeviceDown(object sender, TEventArgs args) where TEventArgs : InputEventArgs { // Block all mouse downs from leaving this control args.Handled = true; } private static void _OnContextMenuOpening(object sender, ContextMenuEventArgs args) { // Block all ContextMenuOpenings from leaving this control if (!(args.TargetElement is ScrollBar)) { args.Handled = true; } } // A generic class handler for both Stylus.PreviewStylusDown and Mouse.PreviewMouseDown events // dc - the event sender // args - Event argument private static void _OnPreviewDeviceDown (object sender, TEventArgs args) where TEventArgs : InputEventArgs { StickyNoteControl snc = sender as StickyNoteControl; IInputElement captured = null; StylusDevice stylusDevice = null; MouseDevice mouseDevice = null; // Check whether Stylus or Mouse has been captured. stylusDevice = args.Device as StylusDevice; if ( stylusDevice != null ) { captured = stylusDevice.Captured; } else { mouseDevice = args.Device as MouseDevice; if (mouseDevice != null) { captured = mouseDevice.Captured; } } // ContextMenu may capture the inputdevice in front. // If the device is captured by an element other than StickyNote, we should not try to bring note to front. if ( snc != null && ( captured == snc || captured == null ) ) { snc.OnPreviewDeviceDown(sender, args); } } /// /// When the Strokes are replaced on an InkCanvas we must unregister on the previous set /// of strokes and register on the new set of strokes. /// private void OnInkCanvasStrokesReplacedEventHandler(object sender, InkCanvasStrokesReplacedEventArgs e) { StopListenToStrokesEvent(e.PreviousStrokes); StartListenToStrokesEvent(e.NewStrokes); } ////// Raised when the user moves the selection. We prevent the selection from going into negative /// territory because ScrollViewer does not scroll content there and the ink gets lost. /// /// For move, we just clamp X or Y to 0. /// private void OnInkCanvasSelectionMovingEventHandler(object sender, InkCanvasSelectionEditingEventArgs e) { Rect newRectangle = e.NewRectangle; if (newRectangle.X < 0 || newRectangle.Y < 0) { newRectangle.X = newRectangle.X < 0d ? 0d : newRectangle.X; newRectangle.Y = newRectangle.Y < 0d ? 0d : newRectangle.Y; e.NewRectangle = newRectangle; } } ////// Raised when the user resizes the selection. We prevent the selection from going into negative /// territory because ScrollViewer does not scroll content there and the ink gets lost. /// /// For Resize, we recompute the new rect after clamping x or y (or both) to 0,0 /// private void OnInkCanvasSelectionResizingEventHandler(object sender, InkCanvasSelectionEditingEventArgs e) { Rect newRectangle = e.NewRectangle; if (newRectangle.X < 0 || newRectangle.Y < 0) { if (newRectangle.X < 0) { //newRect.X is negative, simply add it to width to subtract it newRectangle.Width = newRectangle.Width + newRectangle.X; newRectangle.X = 0d; } if (newRectangle.Y < 0) { //newRect.Y is negative, simply add it to height to subtract it newRectangle.Height = newRectangle.Height + newRectangle.Y; newRectangle.Y = 0d; } e.NewRectangle = newRectangle; } } // A handler for the events of ink stroke being changed. private void OnInkStrokesChanged(object sender, StrokeCollectionChangedEventArgs args) { // We have two options for tracking ink's dirty flag. // 1) use the UndoStateChanged event to detect when any data changes in the Ink object model // Advantages: // Handles ALL data changes in Ink object model // One event handler // Disadvantages: // Very perf intensive since undo serialization is triggered // 2) Handle the StrokesChanged or DrawingAttributesChanged events to detect when specific kinds of data change in the Ink object model. // Advantages: // Efficient since no serialization occurs. // Very targeted handling of types of data changes (e.g. points/transforms, drawing attributes) // Disadvantages: // Does not include changes to ExtendedProperties/ExtendedProperties and potentially 3rd party events. - not a true 100% perfect dirty flag // Since we only care about transforms/points and drawing attributes for now, #2 is a better choice. StopListenToStrokeEvent(args.Removed); StartListenToStrokeEvent(args.Added); if (args.Removed.Count > 0 || args.Added.Count > 0) { Invariant.Assert(Content != null && Content.InnerControl is InkCanvas); FrameworkElement parent = VisualTreeHelper.GetParent(Content.InnerControl) as FrameworkElement; if (parent != null) { // Invalidate ContentArea's measure so that scrollbar could be updated correctly. parent.InvalidateMeasure(); } } //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONINKCHANGEDGUID, EventType.StartEvent); // Update the Ink in the annotation. UpdateAnnotationWithSNC(XmlToken.Ink); IsDirty = true; //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONINKCHANGEDGUID, EventType.EndEvent); } ////// Initializes a StickyNoteControl's private members. /// private void InitStickyNoteControl() { //set the anchor as DataContext XmlQualifiedName type = _stickyNoteType == StickyNoteType.Text ? TextSchemaName : InkSchemaName; _anchor = new MarkedHighlightComponent(type, this); IsDirty = false; //listen to Loaded event to set Focus if needed Loaded += new RoutedEventHandler(OnLoadedEventHandler); } ////// Create the listeners we will use to register on our InkCanvas when it gets created. /// Only called once this StickyNote is first used for ink content. /// private void InitializeEventHandlers() { _propertyDataChangedHandler = new StrokeChangedHandler(this); _strokeDrawingAttributesReplacedHandler = new StrokeChangedHandler (this); _strokePacketDataChangedHandler = new StrokeChangedHandler (this); } /// /// Listens for click events from the close button and the icon button. /// Both cause the IsExpanded property to be negated. /// private void OnButtonClick(object sender, RoutedEventArgs e) { bool currentExpanded = IsExpanded; IsExpanded = !currentExpanded; } ////// Simple method that deletes the current annotation from the annotation store. /// This is called in response to the DeleteNote command. /// private void DeleteStickyNote() { Invariant.Assert(_attachedAnnotation != null, "AttachedAnnotation is null."); Invariant.Assert(_attachedAnnotation.Store != null, "AttachedAnnotation's Store is null."); _attachedAnnotation.Store.DeleteAnnotation(_attachedAnnotation.Annotation.Id); } ////// Handles drag completed events for both thumbs in the Sticky Note Control. /// When the drag is completed, we write out the new values that might have changed. /// private void OnDragCompleted(object sender, DragCompletedEventArgs args) { Thumb source = args.Source as Thumb; // Update the cached offsets of StickyNote if (source == GetTitleThumb()) { // Update the Top and Left in the annotation. UpdateAnnotationWithSNC(XmlToken.XOffset | XmlToken.YOffset | XmlToken.Left | XmlToken.Top); } else if (source == GetResizeThumb()) { // Update the Width and Height and Top and Left in the annotation. UpdateAnnotationWithSNC(XmlToken.XOffset | XmlToken.YOffset | XmlToken.Width | XmlToken.Height | XmlToken.Left | XmlToken.Top); } } ////// Called when either thumb is dragged. We then process the drag in specific ways depending /// on which thumb was dragged. /// private void OnDragDelta(object sender, DragDeltaEventArgs args) { Invariant.Assert(IsExpanded == true, "Dragging occurred when the StickyNoteControl was not expanded."); Thumb source = args.Source as Thumb; double horizontalChange = args.HorizontalChange; // Because we are self-mirroring, we have flipped the layout within the note // but our environment isn't flipped. The Thumb (within the note) is providing // mirrored values but we will use them to position ourselves within the unmirrored // environment, so we flip the values again before using them. if (_selfMirroring) { horizontalChange = -horizontalChange; } if (source == GetTitleThumb()) { OnTitleDragDelta(horizontalChange, args.VerticalChange); } else if (source == GetResizeThumb()) { OnResizeDragDelta(args.HorizontalChange, args.VerticalChange); } // Update the cached offsets of StickyNote UpdateOffsets(); } ////// Handles dragging the title thumb. This updates the StickyNoteControl's /// position. /// private void OnTitleDragDelta(double horizontalChange, double verticalChange) { Invariant.Assert(IsExpanded != false); Rect rectNote = StickyNoteBounds; Rect rectPage = PageBounds; // These are the minimum widths that must be visible when a note is partially off // the left or right side of a page. The difference is due to the Close button // being on one side and wanting some of the title bar to be visible on that side. double leftBoundary = 45; double rightBoundary = 20; // Because we are self-mirroring, we need to flip the minimum widths if (_selfMirroring) { double temp = rightBoundary; rightBoundary = leftBoundary; leftBoundary = temp; } // Figure out the new position while enforcing a portion of the note being always on the page. Point minBoundary = new Point(-(rectNote.X + rectNote.Width - leftBoundary), - rectNote.Y); Point maxBoundary = new Point(rectPage.Width - rectNote.X - rightBoundary, rectPage.Height - rectNote.Y - 20); horizontalChange = Math.Min(Math.Max(minBoundary.X, horizontalChange), maxBoundary.X); verticalChange = Math.Min(Math.Max(minBoundary.Y, verticalChange), maxBoundary.Y); TranslateTransform currentTransform = PositionTransform; // Include any temporary delta we are currently using to avoid any visible jumping to the user PositionTransform = new TranslateTransform(currentTransform.X + horizontalChange + _deltaX, currentTransform.Y + verticalChange + _deltaY); _deltaX = _deltaY = 0; IsDirty = true; } ////// Handles dragging the bottom/right resize thumb. Updates the StickyNoteControl's size. /// private void OnResizeDragDelta(double horizontalChange, double verticalChange) { Invariant.Assert(IsExpanded != false); Rect rectNote = StickyNoteBounds; double wNew = rectNote.Width + horizontalChange; double hNew = rectNote.Height + verticalChange; // This method doesn't apply during self-mirroring because // we are actually moving the note during which time the note's // location is bounded by the page borders (plus a cushion). if (!_selfMirroring) { // If the new size would put the right side of the SN off the page // we don't allow that resize anymore. if (rectNote.X + wNew < 45) wNew = rectNote.Width; } double minWidth = MinWidth; double minHeight = MinHeight; if (wNew < minWidth) { wNew = minWidth; // This is only used in self-mirroring - if we clamp the size change, // we must also clamp the move (see below) horizontalChange = wNew - this.Width; } if (hNew < minHeight) { hNew = minHeight; } SetValue(WidthProperty, wNew); SetValue(HeightProperty, hNew); // Because we are self-mirroring, as the note changes size we have to // move it to make it appear its changing size from the left (where the // resize handle is located during mirroring) instead of the right which // is what is actually happening. if (_selfMirroring) { OnTitleDragDelta(-horizontalChange, 0); } else { // This appears to be a no-op but OnTitleDragDelta also takes // care of applying permanently any temporary offsets OnTitleDragDelta(0, 0); } IsDirty = true; } ////// Any click in the StickyNoteControl brings it to the top in z-order and /// requests the focus. /// Additionally we must `swallow the event here if the click happend on /// our InkCanvas because the RTI engine isn't setup yet and the user will /// be creating ink without seeing it. /// private void OnPreviewDeviceDown(object dc, InputEventArgs args) { if (IsExpanded) { bool eatEvent = false; if (!IsKeyboardFocusWithin && this.StickyNoteType == StickyNoteType.Ink) { // Only event we want to `swallow here is a click on the InkCanvas // when the StickyNote isn't focused because RTI isn't set up yet Visual source = args.OriginalSource as Visual; if (source != null) { Invariant.Assert(Content.InnerControl != null, "InnerControl is null."); eatEvent = source.IsDescendantOf(this.Content.InnerControl); } } // Will have no effect if already in front BringToFront(); if (!IsActive || !IsKeyboardFocusWithin) { Focus(); } if (eatEvent == true) { args.Handled = true; } } } ////// Set the focus on SN if needed /// /// sender - not used /// arguments - not used private void OnLoadedEventHandler(object sender, RoutedEventArgs e) { if (IsExpanded) { // Setup the inner controls with the Annotation's data - we must have correct // values for SN sizes before Focus->BringIntoView is invoked UpdateSNCWithAnnotation(SNCAnnotation.Sizes); if (_sncAnnotation.IsNewAnnotation) { // NTRAID#WINOS-1169084-2005/05/19-WAYNEZEN // After the annotations has been added, we should set focus on the element. Focus(); } } //unregister Loaded -= new RoutedEventHandler(OnLoadedEventHandler); } ////// Unregister from any events on the current visual tree. Also disconnect the current /// content control. /// private void ClearCachedControls() { if (Content != null) { // Disconnect the content control which will be re-connected to the new ContentControl // in ApplyTemplate when the new visual tree is populated from the new control template. DisconnectContent(); } Button closeButton = GetCloseButton(); if (closeButton != null) { closeButton.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } Button iconButton = GetIconButton(); if (iconButton != null) { iconButton.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } Thumb titleThumb = GetTitleThumb(); if (titleThumb != null) { titleThumb.RemoveHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); titleThumb.RemoveHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } Thumb resizeThumb = GetResizeThumb(); if (resizeThumb != null) { resizeThumb.RemoveHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); resizeThumb.RemoveHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } } ////// Called when this StickyNoteControl's IsExpanded property changes. /// private void OnIsExpandedChanged() { InvalidateTransform(); // Update the Iconized in the annotation. UpdateAnnotationWithSNC(XmlToken.IsExpanded); IsDirty = true; if (IsExpanded) { BringToFront(); // We request the focus from a dispatcher callback because we may // have been called from a this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(AsyncTakeFocus), null); } else { GiveUpFocus(); SendToBack(); } } ////// Requests focus to this control. Used asynchronously from event handlers /// to prevent other event handlers from stealing focus right back. /// private object AsyncTakeFocus(object notUsed) { this.Focus(); return null; } ////// If focus us within this control, sets the focus on the first element in the attached /// annotation's parent's ancestor chain that accepts it. If no element accepts it, /// then focus is set to null. /// private void GiveUpFocus() { // Only send focus away when we actually have it. if (IsKeyboardFocusWithin) { // Start with the attached annotation's parent, // walk up the tree looking for the first element // to take focus. bool transferred = false; DependencyObject parent = _attachedAnnotation.Parent; IInputElement newFocus = null; while (parent != null && !transferred) { newFocus = parent as IInputElement; if (newFocus != null) { transferred = newFocus.Focus(); } // Go up the parent chain if focus wasn't taken if (!transferred) { parent = FrameworkElement.GetFrameworkParent(parent); } } // If no element was found, just give up focus if (!transferred) { Keyboard.Focus(null); } } } ////// Request this control be sent to the front of the z-order stack in its /// presentation context. Call to PresentationContext should have no affect /// if its already at the front. /// private void BringToFront() { PresentationContext pc = ((IAnnotationComponent)this).PresentationContext; if ( pc != null ) { pc.BringToFront(this); } } ////// Request this control be sent to the back of the z-order stack in its /// presentation context. Call to PresentationContext should have no affect /// if its already at the back. /// private void SendToBack() { PresentationContext pc = ((IAnnotationComponent)this).PresentationContext; if (pc != null) { pc.SendToBack(this); } } ////// Invalidate the Transform for this control in its presentation context. /// private void InvalidateTransform() { PresentationContext pc = ((IAnnotationComponent)this).PresentationContext; if ( pc != null ) { pc.InvalidateTransform(this); } } ////// Updates the attached annotation with the data currently in this control. /// Called asynchronously from event handler because TextBox can't handle /// changing its content from an event handler. // Do we need this to be async? /// private object AsyncUpdateAnnotation(object arg) { UpdateAnnotationWithSNC((XmlToken)arg); return null; } ////// This binds a new content control's properties to those set on /// the StickyNoteControl. Should be called each time a new content /// control is created. /// private void BindContentControlProperties() { Invariant.Assert(Content != null); // ISSUE-2005/03/23/WAYNEZEN, // Somehow, the bound font family can't be loaded again by Parser once the attribute persists in XAML. // Since InkCanvas doesn't care about FontFamily, we just walk the issue around by not binding the property. if (Content.Type != StickyNoteType.Ink) { FrameworkElement innerControl = Content.InnerControl; innerControl.SetValue(FontFamilyProperty, GetValue(FontFamilyProperty)); innerControl.SetValue(FontSizeProperty, GetValue(FontSizeProperty)); innerControl.SetValue(FontStretchProperty, GetValue(FontStretchProperty)); innerControl.SetValue(FontStyleProperty, GetValue(FontStyleProperty)); innerControl.SetValue(FontWeightProperty, GetValue(FontWeightProperty)); innerControl.SetValue(ForegroundProperty, GetValue(ForegroundProperty)); } else { // Create a TwoWay MultiBinding for InkCanvas.EditingMode. // The internal InkCanvas' EditingMode will be determined by // both StickyNoteControl.InkEditingMode and StickyNoteControl.IsKeyboardFocusWithin // If StickyNoteControl.IsKeyboardFocusWithin is false, the InkCanvas.EditingMode should be none. // Otherwise InkCanvas.EditingMode is same as the StickyNoteControl.InkEditingMode. MultiBinding inkCanvasEditingMode = new MultiBinding(); inkCanvasEditingMode.Mode = BindingMode.TwoWay; inkCanvasEditingMode.Converter = new InkEditingModeIsKeyboardFocusWithin2EditingMode(); Binding stickyNoteInkEditingMode = new Binding(); stickyNoteInkEditingMode.Mode = BindingMode.TwoWay; stickyNoteInkEditingMode.Path = new PropertyPath(StickyNoteControl.InkEditingModeProperty); stickyNoteInkEditingMode.Source = this; inkCanvasEditingMode.Bindings.Add(stickyNoteInkEditingMode); Binding stickyNoteIsKeyboardFocusWithin = new Binding(); stickyNoteIsKeyboardFocusWithin.Path = new PropertyPath(UIElement.IsKeyboardFocusWithinProperty); stickyNoteIsKeyboardFocusWithin.Source = this; inkCanvasEditingMode.Bindings.Add(stickyNoteIsKeyboardFocusWithin); Content.InnerControl.SetBinding(InkCanvas.EditingModeProperty, inkCanvasEditingMode); } } ////// Removes the bindings we previously created between the content control /// and the StickyNoteControl. /// private void UnbindContentControlProperties() { Invariant.Assert(Content != null); FrameworkElement innerControl = (FrameworkElement)Content.InnerControl; if (Content.Type != StickyNoteType.Ink) { innerControl.ClearValue(FontFamilyProperty); innerControl.ClearValue(FontSizeProperty); innerControl.ClearValue(FontStretchProperty); innerControl.ClearValue(FontStyleProperty); innerControl.ClearValue(FontWeightProperty); innerControl.ClearValue(ForegroundProperty); } else { BindingOperations.ClearBinding(innerControl, InkCanvas.EditingModeProperty); } } ////// Registers for change events from the content control. This lets us /// update the annotation when something in the controls change. Should /// be called when a new content control is created. /// private void StartListenToContentControlEvent() { Invariant.Assert(Content != null); if (Content.Type == StickyNoteType.Ink) { InkCanvas inkCanvas = Content.InnerControl as InkCanvas; Invariant.Assert(inkCanvas != null, "InnerControl is not an InkCanvas for note of type Ink."); inkCanvas.StrokesReplaced += new InkCanvasStrokesReplacedEventHandler(OnInkCanvasStrokesReplacedEventHandler); inkCanvas.SelectionMoving += new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionMovingEventHandler); inkCanvas.SelectionResizing += new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionResizingEventHandler); StartListenToStrokesEvent(inkCanvas.Strokes); } else { TextBoxBase textBoxBase = Content.InnerControl as TextBoxBase; Invariant.Assert(textBoxBase != null, "InnerControl is not a TextBoxBase for note of type Text."); textBoxBase.TextChanged += new TextChangedEventHandler(OnTextChanged); } } ////// Unregisters for any events from the content control. Should be called /// when a content control is being changed. /// private void StopListenToContentControlEvent() { Invariant.Assert(Content != null); if (Content.Type == StickyNoteType.Ink) { InkCanvas inkCanvas = Content.InnerControl as InkCanvas; Invariant.Assert(inkCanvas != null, "InnerControl is not an InkCanvas for note of type Ink."); inkCanvas.StrokesReplaced -= new InkCanvasStrokesReplacedEventHandler(OnInkCanvasStrokesReplacedEventHandler); inkCanvas.SelectionMoving -= new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionMovingEventHandler); inkCanvas.SelectionResizing -= new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionResizingEventHandler); StopListenToStrokesEvent(inkCanvas.Strokes); } else { TextBoxBase textBoxBase = Content.InnerControl as TextBoxBase; Invariant.Assert(textBoxBase != null, "InnerControl is not a TextBoxBase for note of type Text."); textBoxBase.TextChanged -= new TextChangedEventHandler(OnTextChanged); } } ////// Register for events on the StrokeCollection from the InkCanvas. /// private void StartListenToStrokesEvent(StrokeCollection strokes) { strokes.StrokesChanged += new StrokeCollectionChangedEventHandler(OnInkStrokesChanged); strokes.PropertyDataChanged += new PropertyDataChangedEventHandler(_propertyDataChangedHandler.OnStrokeChanged); StartListenToStrokeEvent(strokes); } ////// Unregister for events on the StrokeCollection from the InkCanvas. /// private void StopListenToStrokesEvent(StrokeCollection strokes) { strokes.StrokesChanged -= new StrokeCollectionChangedEventHandler(OnInkStrokesChanged); strokes.PropertyDataChanged -= new PropertyDataChangedEventHandler(_propertyDataChangedHandler.OnStrokeChanged); StopListenToStrokeEvent(strokes); } ////// Register on each stroke in the InkCanvas. If any of them change we need to know about it. /// private void StartListenToStrokeEvent(IEnumerablestrokes) { foreach (Stroke s in strokes) { s.DrawingAttributes.AttributeChanged += new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); s.DrawingAttributesReplaced += new DrawingAttributesReplacedEventHandler( _strokeDrawingAttributesReplacedHandler.OnStrokeChanged); s.StylusPointsReplaced += new StylusPointsReplacedEventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.StylusPoints.Changed += new EventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.PropertyDataChanged += new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); } } /// /// Unregister on each stroke in the InkCanvas. We previously registered on each stroke and /// now need to disconnect from them. /// private void StopListenToStrokeEvent(IEnumerablestrokes) { foreach (Stroke s in strokes) { s.DrawingAttributes.AttributeChanged -= new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); s.DrawingAttributesReplaced -= new DrawingAttributesReplacedEventHandler( _strokeDrawingAttributesReplacedHandler.OnStrokeChanged); s.StylusPointsReplaced -= new StylusPointsReplacedEventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.StylusPoints.Changed -= new EventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.PropertyDataChanged -= new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); } } /// /// Set bindings on the menuitems which StickyNote knows about /// private void SetupMenu() { MenuItem inkMenuItem = GetInkMenuItem(); if (inkMenuItem != null) { // Bind the EditingMode to item's IsChecked DP. Binding checkedBind = new Binding("InkEditingMode"); checkedBind.Mode = BindingMode.OneWay; checkedBind.RelativeSource = RelativeSource.TemplatedParent; checkedBind.Converter = new InkEditingModeConverter(); checkedBind.ConverterParameter = InkCanvasEditingMode.Ink; inkMenuItem.SetBinding(MenuItem.IsCheckedProperty, checkedBind); } MenuItem selectMenuItem = GetSelectMenuItem(); if (selectMenuItem != null) { // Bind the EditingMode to item's IsChecked DP. Binding checkedBind = new Binding("InkEditingMode"); checkedBind.Mode = BindingMode.OneWay; checkedBind.RelativeSource = RelativeSource.TemplatedParent; checkedBind.Converter = new InkEditingModeConverter(); checkedBind.ConverterParameter = InkCanvasEditingMode.Select; selectMenuItem.SetBinding(MenuItem.IsCheckedProperty, checkedBind); } MenuItem eraseMenuItem = GetEraseMenuItem(); if (eraseMenuItem != null) { // Bind the EditingMode to item's IsChecked DP. Binding checkedBind = new Binding("InkEditingMode"); checkedBind.Mode = BindingMode.OneWay; checkedBind.RelativeSource = RelativeSource.TemplatedParent; checkedBind.Converter = new InkEditingModeConverter(); checkedBind.ConverterParameter = InkCanvasEditingMode.EraseByStroke; eraseMenuItem.SetBinding(MenuItem.IsCheckedProperty, checkedBind); } // Copy and Paste menu items (and their separator) are removed if // we don't have Clipboard permissions. bool hasClipboardPermission = SecurityHelper.CallerHasAllClipboardPermission(); // Set the target for the Copy/Paste commands to our inner control MenuItem copyMenuItem = GetCopyMenuItem(); if (copyMenuItem != null) { if (hasClipboardPermission) { copyMenuItem.CommandTarget = Content.InnerControl; } else { copyMenuItem.Visibility = Visibility.Collapsed; } } MenuItem pasteMenuItem = GetPasteMenuItem(); if (pasteMenuItem != null) { if (hasClipboardPermission) { pasteMenuItem.CommandTarget = Content.InnerControl; } else { pasteMenuItem.Visibility = Visibility.Collapsed; } } Separator clipboardSeparator = GetClipboardSeparator(); if (clipboardSeparator != null) { if (!hasClipboardPermission) { clipboardSeparator.Visibility = Visibility.Collapsed; } } } ////// CommandExecuted Handler - processes the commands for SNC. /// DeleteNoteCommand - deletes the annotation from the store (causing the SNC to disappear) /// InkCommand - changes the mode of the ink canvas /// Copy/Paste - we pass on to our inner control /// /// /// private static void _OnCommandExecuted(object sender, ExecutedRoutedEventArgs args) { RoutedCommand command = (RoutedCommand)(args.Command); StickyNoteControl snc = sender as StickyNoteControl; Invariant.Assert(snc != null, "Unexpected Commands"); Invariant.Assert(command == StickyNoteControl.DeleteNoteCommand || command == StickyNoteControl.InkCommand, "Unknown Commands"); if ( command == StickyNoteControl.DeleteNoteCommand ) { // DeleteNote Command snc.DeleteStickyNote(); } else if (command == StickyNoteControl.InkCommand) { StickyNoteContentControl content = snc.Content; if (content == null || content.Type != StickyNoteType.Ink) { throw new InvalidOperationException(SR.Get(SRID.CannotProcessInkCommand)); } // Set the StickyNoteControl's ink editing mode to the command's parameter InkCanvasEditingMode mode = (InkCanvasEditingMode)args.Parameter; snc.SetValue(InkEditingModeProperty, mode); } } ////// QueryCommandEnabled Handler - determines if a command should be enabled or not. /// DeleteNoteCommand - if the SNC has an attached annotation /// InkCommand - if the SNC is of type InkStickyNote /// AllOthers - if focus is on our inner control, we let the inner control decide, /// otherwise we return false /// private static void _OnQueryCommandEnabled(object sender, CanExecuteRoutedEventArgs args) { RoutedCommand command = (RoutedCommand)( args.Command ); StickyNoteControl snc = sender as StickyNoteControl; Invariant.Assert(snc != null, "Unexpected Commands"); Invariant.Assert(command == StickyNoteControl.DeleteNoteCommand || command == StickyNoteControl.InkCommand, "Unknown Commands"); if ( command == StickyNoteControl.DeleteNoteCommand ) { // Enable/Disable DeleteNote Command based on the Attached Annotation. args.CanExecute = snc._attachedAnnotation != null; } else if (command == StickyNoteControl.InkCommand) { StickyNoteContentControl content = snc.Content; // Enabled/Disable InkCommand based on the StickyNote type args.CanExecute = (content != null && content.Type == StickyNoteType.Ink); } else { Invariant.Assert(false, "Unknown command."); } } ////// Update DrawingAttributes on InkCanvas /// private void UpdateInkDrawingAttributes() { if ( Content == null || Content.Type != StickyNoteType.Ink ) { // Return now if there is no InkCanvas. return; } DrawingAttributes da = new DrawingAttributes(); SolidColorBrush foreground = Foreground as SolidColorBrush; // Make sure the foreground is type of SolidColorBrush. if ( foreground == null ) { throw new ArgumentException(SR.Get(SRID.InvalidInkForeground), "Foreground"); } da.StylusTip = StylusTip.Ellipse; da.Width = PenWidth; da.Height = PenWidth; da.Color = foreground.Color; // Update the DA on InkCanvas. ( (InkCanvas)( Content.InnerControl ) ).DefaultDrawingAttributes = da; } #endregion // Private Methods //-------------------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------------------- #region Private Properties ////// Returns Ink MenuItem /// private MenuItem GetInkMenuItem() { return GetTemplateChild(SNBConstants.c_InkMenuId) as MenuItem; } ////// Returns Select MenuItem /// private MenuItem GetSelectMenuItem() { return GetTemplateChild(SNBConstants.c_SelectMenuId) as MenuItem; } ////// Returns Erase MenuItem /// private MenuItem GetEraseMenuItem() { return GetTemplateChild(SNBConstants.c_EraseMenuId) as MenuItem; } ////// Returns Copy MenuItem /// private MenuItem GetCopyMenuItem() { return GetTemplateChild(SNBConstants.c_CopyMenuId) as MenuItem; } ////// Returns Paste MenuItem /// private MenuItem GetPasteMenuItem() { return GetTemplateChild(SNBConstants.c_PasteMenuId) as MenuItem; } ////// Returns Separator for clipboard MenuItems /// private Separator GetClipboardSeparator() { return GetTemplateChild(SNBConstants.c_ClipboardSeparatorId) as Separator; } // This is the getter of _lockHelper which is a helper object for locking/unlocking a specified flag automatically. private LockHelper InternalLocker { get { // Check if we have create a helper. If not, we go ahead create one. if (_lockHelper == null) { _lockHelper = new LockHelper(); } return _lockHelper; } } #endregion // Private Properties //------------------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------------------- #region Private Fields private LockHelper _lockHelper; private MarkedHighlightComponent _anchor; //holds inactive, active, focused state private bool _dirty = false; // Cache for the dependency properties private StickyNoteType _stickyNoteType = StickyNoteType.Text; private StickyNoteContentControl _contentControl; private StrokeChangedHandler_propertyDataChangedHandler; private StrokeChangedHandler _strokeDrawingAttributesReplacedHandler; private StrokeChangedHandler _strokePacketDataChangedHandler; #endregion // Private Fields //-------------------------------------------------------------------------------- // // Private classes // //------------------------------------------------------------------------------- #region Private classes // This is a binding converter which alters the IsChecked DP based on ink StickyNote's EditingMode of its InkCanvas. private class InkEditingModeConverter : IValueConverter { public object Convert(object o, Type type, object parameter, CultureInfo culture) { InkCanvasEditingMode expectedMode = (InkCanvasEditingMode)parameter; InkCanvasEditingMode currentMode = (InkCanvasEditingMode)o; // If the current EditingMode is the mode which menuitem is expecting, return true for IsChecked. if ( currentMode == expectedMode ) { return true; } else { return DependencyProperty.UnsetValue; } } public object ConvertBack(object o, Type type, object parameter, CultureInfo culture) { return null; } } // This is a binding converter which alters the InkCanvas.EditingMode DP // based on StickyNoteControl.InkEditingMode and StickyNoteControl.IsKeyboardFocusWithin. private class InkEditingModeIsKeyboardFocusWithin2EditingMode : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { InkCanvasEditingMode sncInkEditingMode = (InkCanvasEditingMode)values[0]; bool sncIsKeyboardFocusWithin = (bool)values[1]; // If there is no focus on the StickyNote, we should return InkCanvasEditingMode.None to disable the RTI. // Otherwise return the value of the StickyNoteControl.InkEditingMode property. if ( sncIsKeyboardFocusWithin ) { return sncInkEditingMode; } else { return InkCanvasEditingMode.None; } } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { return new object[] { value, Binding.DoNothing }; } } // A helper class which suppresses severial handlers to a single methods by using generic private class StrokeChangedHandler { public StrokeChangedHandler(StickyNoteControl snc) { Invariant.Assert(snc != null); _snc = snc; } public void OnStrokeChanged(object sender, TEventArgs t) { // Dirty the ink data _snc.UpdateAnnotationWithSNC(XmlToken.Ink); _snc._dirty = true; } private StickyNoteControl _snc; } #endregion Private classes } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. // In order to disable Presharp warning 6507 - Prefer 'string.IsNullOrEmpty(value)' over checks for null and/or emptiness, // we have to disable warnings 1634 and 1691 to make the compiler happy first. #pragma warning disable 1634, 1691 //---------------------------------------------------------------------------- // // // Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Implementation of StickyNoteControl control. // // See spec at http://tabletpc/longhorn/Specs/StickyNoteControlSpec.mht // // History: // 04/19/2004 - waynezen - Expose more properties and methods // 02/17/2004 - waynezen - Moved to TabletFramework // 10/06/2003 - waynezen - Added Ink and Snippet Image supports // 09/23/2003 - waynezen - Ported to WCP. // 12/16/2002 - waynezen - Created. // //--------------------------------------------------------------------------- using System; using System.ComponentModel; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; // Assert using System.Globalization; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Serialization; using Microsoft.Win32; // SystemEvents using MS.Internal; using MS.Internal.Annotations.Component; using MS.Internal.Controls.StickyNote; using MS.Internal.Commands; using MS.Internal.KnownBoxes; using MS.Internal.PresentationFramework; using System.Windows.Threading; using System.Windows; using System.Windows.Data; using System.Windows.Annotations; using System.Windows.Automation; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Text; using System.Text.RegularExpressions; using MS.Utility; namespace System.Windows.Controls { ////// The type of data handled by a StickyNoteControl. /// public enum StickyNoteType { ////// Text StickyNote /// Text, ////// Ink StickyNote /// Ink, } ////// StickyNoteControl control intends to be an UI control for user to make annotation. /// [TemplatePart(Name = SNBConstants.c_CloseButtonId, Type = typeof(Button))] [TemplatePart(Name = SNBConstants.c_TitleThumbId, Type = typeof(Thumb))] [TemplatePart(Name = SNBConstants.c_BottomRightResizeThumbId, Type = typeof(Thumb))] [TemplatePart(Name = SNBConstants.c_ContentControlId, Type = typeof(ContentControl))] [TemplatePart(Name = SNBConstants.c_IconButtonId, Type = typeof(Button))] [TemplatePart(Name = SNBConstants.c_CopyMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_PasteMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_InkMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_SelectMenuId, Type = typeof(MenuItem))] [TemplatePart(Name = SNBConstants.c_EraseMenuId, Type = typeof(MenuItem))] public sealed partial class StickyNoteControl : Control, IAnnotationComponent { //------------------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------------------- #region Constructors ////// The static constructor /// static StickyNoteControl() { Type owner = typeof(StickyNoteControl); // Register event handlers // NTRAID-WINDOWS#1136774-WAYNEZEN, // We want to bring a note to front when the input device is pressed down on it at the first time. // StickyNote invokes PresentationContext.BringToFront method to achieve it. // Eventually the Annotation AdornerLayer will re-arrange index of its children to change their "Z-Order". // So, the BringToFront could temporarily remove StickyNote from the visual tree. // But the hosted InkCanvas might capture Stylus for collecting packets. If the host note is removed from the tree, the capture // will be released automatically which will mess up the InkCanvas' states. // So we have to do BringToFront before InkCanvas does capture. Adding a handler of PreviewStylusDownEvent fixes the problem. // Unfortunately we still need the existing handler of PreviewMouseDownEvent since there is no stylus event for text StickyNote. // EventManager.RegisterClassHandler(owner, Stylus.PreviewStylusDownEvent, new StylusDownEventHandler(_OnPreviewDeviceDown)); EventManager.RegisterClassHandler(owner, Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler(_OnPreviewDeviceDown )); EventManager.RegisterClassHandler(owner, Mouse.MouseDownEvent, new MouseButtonEventHandler(_OnDeviceDown )); EventManager.RegisterClassHandler(owner, ContextMenuService.ContextMenuOpeningEvent, new ContextMenuEventHandler(_OnContextMenuOpening)); CommandHelpers.RegisterCommandHandler(typeof(StickyNoteControl), StickyNoteControl.DeleteNoteCommand, new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled)); CommandHelpers.RegisterCommandHandler(typeof(StickyNoteControl), StickyNoteControl.InkCommand, new ExecutedRoutedEventHandler(_OnCommandExecuted), new CanExecuteRoutedEventHandler(_OnQueryCommandEnabled)); // // set the main style CRK to the default theme style key // DefaultStyleKeyProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata( new ComponentResourceKey(typeof(PresentationUIStyleResources), "StickyNoteControlStyleKey"))); KeyboardNavigation.TabNavigationProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(KeyboardNavigationMode.Local)); Control.IsTabStopProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(false)); // Override the changed callback of the Foreground Property. ForegroundProperty.OverrideMetadata( owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(_UpdateInkDrawingAttributes))); FontFamilyProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontSizeProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontStretchProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontStyleProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); FontWeightProperty.OverrideMetadata(owner, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFontPropertyChanged))); } /// /// This is an instance constructor for StickyNoteControl class. Should not be used /// private StickyNoteControl() : this(StickyNoteType.Text) { } ////// Creates an instance of StickyNoteControl that handles the specified type. /// /// the type of data to be handled by the StickyNoteControl internal StickyNoteControl(StickyNoteType type) : base() { _stickyNoteType = type; SetValue(StickyNoteTypePropertyKey, type); InitStickyNoteControl(); } #endregion // Constructors //-------------------------------------------------------------------------------- // // Public Methods // //------------------------------------------------------------------------------- #region Public Methods ////// Override OnApplyTemplate method. This method will ensure whether the created visual is one which /// StickyNoteControl expects. If so, the internal controls will be cached and Annotation will be applied /// to the UI status. /// ///Whether Visuals were added to the tree public override void OnApplyTemplate() { // No need for calling VerifyAccess since we call the method on the base here. base.OnApplyTemplate(); // Ensure the type if (this.IsExpanded) { EnsureStickyNoteType(); } // Setup the inner controls with the Annotation's data UpdateSNCWithAnnotation(SNCAnnotation.AllValues); // If the visual tree just gets populated from the new style, we have to add our event handles to the individual // elements. if (!this.IsExpanded) { Button button = GetIconButton(); if (button != null) { button.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } } else { Button closeButton = GetCloseButton(); if (closeButton != null) { closeButton.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } Thumb titleThumb = GetTitleThumb(); if (titleThumb != null) { titleThumb.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); titleThumb.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } Thumb resizeThumb = GetResizeThumb(); if (resizeThumb != null) { resizeThumb.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); resizeThumb.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } // Set up the bindings to the menuitems. SetupMenu(); } } #endregion // Public Methods //-------------------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------------------- #region Public Properties ////// The property key of Author /// internal static readonly DependencyPropertyKey AuthorPropertyKey = DependencyProperty.RegisterReadOnly( "Author", typeof(string), typeof(StickyNoteControl), new FrameworkPropertyMetadata(String.Empty)); ////// Author Dependency Property /// public static readonly DependencyProperty AuthorProperty = AuthorPropertyKey.DependencyProperty; ////// Returns the author of the annotation the StickyNoteControl is editing. /// public String Author { get { return (String)GetValue(StickyNoteControl.AuthorProperty); } } ////// Gets/Sets the expanded or minimized state of the StickyNoteControl. /// If Expanded=false, the bubble is in a minimized state. /// public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register( "IsExpanded", typeof(bool), typeof(StickyNoteControl), new FrameworkPropertyMetadata( BooleanBoxes.TrueBox, new PropertyChangedCallback(_OnIsExpandedChanged))); ////// Gets/Sets the expanded or minimized state of the StickyNoteControl. /// public bool IsExpanded { get { return (bool) GetValue(IsExpandedProperty); } set { SetValue(IsExpandedProperty, value); } } ////// true - StickyNoteControl is active (i.e. has the focus or selection includes part of its anchor) ///The value of this property is set by the MarkedHighlightComponent which controls the SN state /// public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(StickyNoteControl), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits)); ////// The state of StickyNoteControl - true : active, false:inactive /// public bool IsActive { get { return (bool)GetValue(StickyNoteControl.IsActiveProperty); } } ////// If true, mouse is over the SticyNoteControl's anchor (which could be used to change its appearance /// in its style). /// internal static readonly DependencyPropertyKey IsMouseOverAnchorPropertyKey = DependencyProperty.RegisterReadOnly("IsMouseOverAnchor", typeof(bool), typeof(StickyNoteControl), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); ////// If true, mouse is over the SticyNoteControl's anchor (which could be used to change its appearance /// in its style). /// public static readonly DependencyProperty IsMouseOverAnchorProperty = IsMouseOverAnchorPropertyKey.DependencyProperty; ////// True if the mouse is over the StickyNote anchor, false otherwise /// public bool IsMouseOverAnchor { get { return (bool) GetValue(StickyNoteControl.IsMouseOverAnchorProperty); } } ////// The DependencyProperty for the CaptionFontFamily property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font /// public static readonly DependencyProperty CaptionFontFamilyProperty = DependencyProperty.Register( "CaptionFontFamily", typeof(FontFamily), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontFamily, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The caption font family /// public FontFamily CaptionFontFamily { get { return (FontFamily) GetValue(CaptionFontFamilyProperty); } set { SetValue(CaptionFontFamilyProperty, value); } } ////// The DependencyProperty for the CaptionFontSize property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font Size /// public static readonly DependencyProperty CaptionFontSizeProperty = DependencyProperty.Register( "CaptionFontSize", typeof(double), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The size of the caption font. /// public double CaptionFontSize { get { return (double) GetValue(CaptionFontSizeProperty); } set { SetValue(CaptionFontSizeProperty, value); } } ////// The DependencyProperty for the CaptionFontStretch property. /// Flags: Can be used in style rules /// Default Value: FontStretches.Normal /// public static readonly DependencyProperty CaptionFontStretchProperty = DependencyProperty.Register( "CaptionFontStretch", typeof(FontStretch), typeof(StickyNoteControl), new FrameworkPropertyMetadata( FontStretches.Normal, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The stretch of the caption font. /// public FontStretch CaptionFontStretch { get { return (FontStretch) GetValue(CaptionFontStretchProperty); } set { SetValue(CaptionFontStretchProperty, value); } } ////// The DependencyProperty for the CaptionFontStyle property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font Style /// public static readonly DependencyProperty CaptionFontStyleProperty = DependencyProperty.Register( "CaptionFontStyle", typeof(FontStyle), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontStyle, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The style of the caption font. /// public FontStyle CaptionFontStyle { get { return (FontStyle) GetValue(CaptionFontStyleProperty); } set { SetValue(CaptionFontStyleProperty, value); } } ////// The DependencyProperty for the CaptionFontWeight property. /// Flags: Can be used in style rules /// Default Value: System Dialog Font Weight /// public static readonly DependencyProperty CaptionFontWeightProperty = DependencyProperty.Register( "CaptionFontWeight", typeof(FontWeight), typeof(StickyNoteControl), new FrameworkPropertyMetadata( SystemFonts.MessageFontWeight, FrameworkPropertyMetadataOptions.AffectsMeasure)); ////// The weight or thickness of the caption font. /// public FontWeight CaptionFontWeight { get { return (FontWeight) GetValue(CaptionFontWeightProperty); } set { SetValue(CaptionFontWeightProperty, value); } } ////// The DependencyProperty for the PenWidth property. /// Flags: Can be used in style rules /// Default Value: The default width of System.Windows.Ink.DrawingAttributes. /// public static readonly DependencyProperty PenWidthProperty = DependencyProperty.Register( "PenWidth", typeof(double), typeof(StickyNoteControl), new FrameworkPropertyMetadata( (new DrawingAttributes()).Width, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(_UpdateInkDrawingAttributes))); ////// The width of the pen for the ink StickyNoteControl. /// public double PenWidth { get { return (double) GetValue(PenWidthProperty); } set { SetValue(PenWidthProperty, value); } } ////// StickyNoteType Property Key /// private static readonly DependencyPropertyKey StickyNoteTypePropertyKey = DependencyProperty.RegisterReadOnly( "StickyNoteType", typeof(StickyNoteType), typeof(StickyNoteControl), new FrameworkPropertyMetadata(StickyNoteType.Text)); ////// StickyNoteType Dependency Property /// public static readonly DependencyProperty StickyNoteTypeProperty = StickyNoteTypePropertyKey.DependencyProperty; ////// Gets StickyNoteType Property /// public StickyNoteType StickyNoteType { get{ return (StickyNoteType) GetValue(StickyNoteTypeProperty); } } ////// Returns the Annotation this StickyNote is representing. /// public IAnchorInfo AnchorInfo { get { if (_attachedAnnotation != null) return _attachedAnnotation; return null; } } #endregion Public Properties //------------------------------------------------------------------------------- // // Public Commands // //-------------------------------------------------------------------------------- #region Public Commands ////// Delete a note /// public static readonly RoutedCommand DeleteNoteCommand = new RoutedCommand("DeleteNote", typeof(StickyNoteControl)); ////// Ink Mode /// public static readonly RoutedCommand InkCommand = new RoutedCommand("Ink", typeof(StickyNoteControl)); #endregion Public Commands //------------------------------------------------------------------------------- // // Protected Methods // //------------------------------------------------------------------------------- #region Protected Methods ////// Called whenever the template changes. /// /// Old template /// New template protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate) { // No need for calling VerifyAccess since we call the method on the base here. base.OnTemplateChanged(oldTemplate, newTemplate); // If StickyNote's control template has been changed, we should invalidate current cached controls. ClearCachedControls(); } ////// Called whenever focus enters or leaves the StickyNoteControl. This includes when focus /// is set/removed from any element within the StickyNoteControl. /// /// arguments describing the property change protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs args) { base.OnIsKeyboardFocusWithinChanged(args); // If we lost focus due to a context menu on some element within us, // we don't consider that a loss of focus. We simply exit early. ContextMenu menu = Keyboard.FocusedElement as ContextMenu; if (menu != null) { if (menu.PlacementTarget != null && menu.PlacementTarget.IsDescendantOf(this)) { return; } } // Must update our anchor that we are now focused or not focused. _anchor.Focused = IsKeyboardFocusWithin; } ////// An event announcing that the keyboard is focused on this bubble. /// /// >FocusChangedEvent Argument protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs args) { // No need for calling VerifyAccess since we call the method on the base here. base.OnGotKeyboardFocus(args); // Dwayne's Input BC(#73066) changed the behavior of mouse button event. // So we could get GotKeyboardFocus event without the visual being set up. // Now, we verify the visual is ready by invoking ApplyTemplate. ApplyTemplate(); // We are interested in the expanded note. if ( IsExpanded == true ) { Invariant.Assert(Content != null); BringToFront(); // If focus was set on us, we should set the focus on our inner control if ( args.NewFocus == this ) { UIElement innerControl = this.Content.InnerControl as UIElement; Invariant.Assert(innerControl != null, "InnerControl is null or not a UIElement."); // Don't mess with focus if its already on our inner control if ( innerControl.IsKeyboardFocused == false ) { // We should set the focus to the inner control after it is added the visual tree. innerControl.Focus(); } } } } #endregion // Protected Methods //------------------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------------------- #region Internal Methods ////// Creates our inner control and adds it to the tree. If the inner control /// already exists we verify its of the right type for our current data. /// This is called when the template is applied or when the annotation for this /// control is set. /// private void EnsureStickyNoteType() { UIElement contentContainer = GetContentContainer(); // Check whether we need to recreate a content control based on the new type. if (_contentControl != null) { // Check if the type has been changed if (_contentControl.Type != _stickyNoteType) { // Recreate the content control when the type has been changed. DisconnectContent(); _contentControl = StickyNoteContentControlFactory.CreateContentControl(_stickyNoteType, contentContainer); ConnectContent(); } } else { _contentControl = StickyNoteContentControlFactory.CreateContentControl(_stickyNoteType, contentContainer); ConnectContent(); } } ////// When our inner control is changing, we disconnect from the existing one /// by unregistering for events and removing the control from the visual tree. /// private void DisconnectContent() { Invariant.Assert(Content != null, "Content is null."); // Unregister for all events and clear bindings StopListenToContentControlEvent(); UnbindContentControlProperties(); _contentControl = null; } ////// When a new content control is created we register on different portions of /// the visual tree for certain events. /// private void ConnectContent() { Invariant.Assert(Content != null); // Set the default inking mode and attributes InkCanvas innerInkCanvas = Content.InnerControl as InkCanvas; if (innerInkCanvas != null) { // Create the event handlers we'll use for ink notes InitializeEventHandlers(); // We set the value on the StickyNoteControl which eventually gets // set on the InkCanvas. The property on the InkCanvas isn't a DP // we can't create a one-way binding as would be preferred. this.SetValue(InkEditingModeProperty, InkCanvasEditingMode.Ink); UpdateInkDrawingAttributes(); } // Register for events and setup bindings StartListenToContentControlEvent(); BindContentControlProperties(); } ////// Returns the Content element for this StickyNoteControl. Should never /// be null when IsExpanded = true. /// internal StickyNoteContentControl Content { get { return _contentControl; } } ////// Returns the button used to close the StickyNoteControl (it actually sets IsExpanded to false). /// private Button GetCloseButton() { return GetTemplateChild(SNBConstants.c_CloseButtonId) as Button; } ////// Returns the button when the StickyNoteControl has IsExpanded=false. /// private Button GetIconButton() { return GetTemplateChild(SNBConstants.c_IconButtonId) as Button; } ////// Returns the thumb that controls the dragging of the StickyNote as a whole. /// private Thumb GetTitleThumb() { return GetTemplateChild(SNBConstants.c_TitleThumbId) as Thumb; } ////// Return the ContentControl viewer in the StickyNoteControl. /// private UIElement GetContentContainer() { return GetTemplateChild(SNBConstants.c_ContentControlId) as UIElement; } ////// Return the resize thumb in the StickyNoteControl. /// private Thumb GetResizeThumb() { return GetTemplateChild(SNBConstants.c_BottomRightResizeThumbId) as Thumb; } #endregion Internal Methods //------------------------------------------------------------------------------- // // Internal Properties // //-------------------------------------------------------------------------------- #region Internal Properties ////// InkEditingMode Property Key /// private static readonly DependencyProperty InkEditingModeProperty = DependencyProperty.Register( "InkEditingMode", typeof(InkCanvasEditingMode), typeof(StickyNoteControl), new FrameworkPropertyMetadata( InkCanvasEditingMode.None)); ////// Gets/Sets the dirty state of the control. /// private bool IsDirty { get { return _dirty; } set { _dirty = value; } } #endregion // Internal Properties //-------------------------------------------------------------------------------- // // Private Methods // //------------------------------------------------------------------------------- #region Private Methods ////// Called when a StickyNoteControl's IsExpanded property changes. Simply /// pass it on to the StickyNoteControl instance. /// private static void _OnIsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { StickyNoteControl snc = (StickyNoteControl)d; snc.OnIsExpandedChanged(); } private static void OnFontPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { StickyNoteControl stickyNoteControl = (StickyNoteControl)d; if (stickyNoteControl.Content != null && stickyNoteControl.Content.Type != StickyNoteType.Ink) { FrameworkElement innerControl = stickyNoteControl.Content.InnerControl; if (innerControl != null) innerControl.SetValue(e.Property, e.NewValue); } } ////// The changed callback attached to the Foreground and PenWidth DPs /// private static void _UpdateInkDrawingAttributes(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Update the DrawingAttributes StickyNoteControl stickyNoteControl = (StickyNoteControl)d; stickyNoteControl.UpdateInkDrawingAttributes(); if (e.Property == ForegroundProperty && stickyNoteControl.Content != null && stickyNoteControl.Content.Type != StickyNoteType.Ink) { FrameworkElement innerControl = stickyNoteControl.Content.InnerControl; if (innerControl != null) innerControl.SetValue(ForegroundProperty, e.NewValue); } } // A class handler for TextBox.TextChanged events // obj - the event sender // args - Event argument private void OnTextChanged(object obj, TextChangedEventArgs args) { // We must update the annotation asynchronously because we can't Since Textbox doesn't allow any layout measurement during its content is changing, we have to do // the update asynchronously. We also prevent updating the annotation when the content in the // textbox was changed due to a change in the annotation itself. if (!InternalLocker.IsLocked(LockHelper.LockFlag.DataChanged)) { //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONTEXTCHANGEDGUID, EventType.StartEvent); AsyncUpdateAnnotation(XmlToken.Text); //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(AsyncUpdateAnnotation), XmlToken.Text); IsDirty = true; //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONTEXTCHANGEDGUID, EventType.EndEvent); } } private static void _OnDeviceDown(object sender, TEventArgs args) where TEventArgs : InputEventArgs { // Block all mouse downs from leaving this control args.Handled = true; } private static void _OnContextMenuOpening(object sender, ContextMenuEventArgs args) { // Block all ContextMenuOpenings from leaving this control if (!(args.TargetElement is ScrollBar)) { args.Handled = true; } } // A generic class handler for both Stylus.PreviewStylusDown and Mouse.PreviewMouseDown events // dc - the event sender // args - Event argument private static void _OnPreviewDeviceDown (object sender, TEventArgs args) where TEventArgs : InputEventArgs { StickyNoteControl snc = sender as StickyNoteControl; IInputElement captured = null; StylusDevice stylusDevice = null; MouseDevice mouseDevice = null; // Check whether Stylus or Mouse has been captured. stylusDevice = args.Device as StylusDevice; if ( stylusDevice != null ) { captured = stylusDevice.Captured; } else { mouseDevice = args.Device as MouseDevice; if (mouseDevice != null) { captured = mouseDevice.Captured; } } // ContextMenu may capture the inputdevice in front. // If the device is captured by an element other than StickyNote, we should not try to bring note to front. if ( snc != null && ( captured == snc || captured == null ) ) { snc.OnPreviewDeviceDown(sender, args); } } /// /// When the Strokes are replaced on an InkCanvas we must unregister on the previous set /// of strokes and register on the new set of strokes. /// private void OnInkCanvasStrokesReplacedEventHandler(object sender, InkCanvasStrokesReplacedEventArgs e) { StopListenToStrokesEvent(e.PreviousStrokes); StartListenToStrokesEvent(e.NewStrokes); } ////// Raised when the user moves the selection. We prevent the selection from going into negative /// territory because ScrollViewer does not scroll content there and the ink gets lost. /// /// For move, we just clamp X or Y to 0. /// private void OnInkCanvasSelectionMovingEventHandler(object sender, InkCanvasSelectionEditingEventArgs e) { Rect newRectangle = e.NewRectangle; if (newRectangle.X < 0 || newRectangle.Y < 0) { newRectangle.X = newRectangle.X < 0d ? 0d : newRectangle.X; newRectangle.Y = newRectangle.Y < 0d ? 0d : newRectangle.Y; e.NewRectangle = newRectangle; } } ////// Raised when the user resizes the selection. We prevent the selection from going into negative /// territory because ScrollViewer does not scroll content there and the ink gets lost. /// /// For Resize, we recompute the new rect after clamping x or y (or both) to 0,0 /// private void OnInkCanvasSelectionResizingEventHandler(object sender, InkCanvasSelectionEditingEventArgs e) { Rect newRectangle = e.NewRectangle; if (newRectangle.X < 0 || newRectangle.Y < 0) { if (newRectangle.X < 0) { //newRect.X is negative, simply add it to width to subtract it newRectangle.Width = newRectangle.Width + newRectangle.X; newRectangle.X = 0d; } if (newRectangle.Y < 0) { //newRect.Y is negative, simply add it to height to subtract it newRectangle.Height = newRectangle.Height + newRectangle.Y; newRectangle.Y = 0d; } e.NewRectangle = newRectangle; } } // A handler for the events of ink stroke being changed. private void OnInkStrokesChanged(object sender, StrokeCollectionChangedEventArgs args) { // We have two options for tracking ink's dirty flag. // 1) use the UndoStateChanged event to detect when any data changes in the Ink object model // Advantages: // Handles ALL data changes in Ink object model // One event handler // Disadvantages: // Very perf intensive since undo serialization is triggered // 2) Handle the StrokesChanged or DrawingAttributesChanged events to detect when specific kinds of data change in the Ink object model. // Advantages: // Efficient since no serialization occurs. // Very targeted handling of types of data changes (e.g. points/transforms, drawing attributes) // Disadvantages: // Does not include changes to ExtendedProperties/ExtendedProperties and potentially 3rd party events. - not a true 100% perfect dirty flag // Since we only care about transforms/points and drawing attributes for now, #2 is a better choice. StopListenToStrokeEvent(args.Removed); StartListenToStrokeEvent(args.Added); if (args.Removed.Count > 0 || args.Added.Count > 0) { Invariant.Assert(Content != null && Content.InnerControl is InkCanvas); FrameworkElement parent = VisualTreeHelper.GetParent(Content.InnerControl) as FrameworkElement; if (parent != null) { // Invalidate ContentArea's measure so that scrollbar could be updated correctly. parent.InvalidateMeasure(); } } //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONINKCHANGEDGUID, EventType.StartEvent); // Update the Ink in the annotation. UpdateAnnotationWithSNC(XmlToken.Ink); IsDirty = true; //fire trace event EventTrace.NormalTraceEvent(EventTraceGuidId.ANNOTATIONINKCHANGEDGUID, EventType.EndEvent); } ////// Initializes a StickyNoteControl's private members. /// private void InitStickyNoteControl() { //set the anchor as DataContext XmlQualifiedName type = _stickyNoteType == StickyNoteType.Text ? TextSchemaName : InkSchemaName; _anchor = new MarkedHighlightComponent(type, this); IsDirty = false; //listen to Loaded event to set Focus if needed Loaded += new RoutedEventHandler(OnLoadedEventHandler); } ////// Create the listeners we will use to register on our InkCanvas when it gets created. /// Only called once this StickyNote is first used for ink content. /// private void InitializeEventHandlers() { _propertyDataChangedHandler = new StrokeChangedHandler(this); _strokeDrawingAttributesReplacedHandler = new StrokeChangedHandler (this); _strokePacketDataChangedHandler = new StrokeChangedHandler (this); } /// /// Listens for click events from the close button and the icon button. /// Both cause the IsExpanded property to be negated. /// private void OnButtonClick(object sender, RoutedEventArgs e) { bool currentExpanded = IsExpanded; IsExpanded = !currentExpanded; } ////// Simple method that deletes the current annotation from the annotation store. /// This is called in response to the DeleteNote command. /// private void DeleteStickyNote() { Invariant.Assert(_attachedAnnotation != null, "AttachedAnnotation is null."); Invariant.Assert(_attachedAnnotation.Store != null, "AttachedAnnotation's Store is null."); _attachedAnnotation.Store.DeleteAnnotation(_attachedAnnotation.Annotation.Id); } ////// Handles drag completed events for both thumbs in the Sticky Note Control. /// When the drag is completed, we write out the new values that might have changed. /// private void OnDragCompleted(object sender, DragCompletedEventArgs args) { Thumb source = args.Source as Thumb; // Update the cached offsets of StickyNote if (source == GetTitleThumb()) { // Update the Top and Left in the annotation. UpdateAnnotationWithSNC(XmlToken.XOffset | XmlToken.YOffset | XmlToken.Left | XmlToken.Top); } else if (source == GetResizeThumb()) { // Update the Width and Height and Top and Left in the annotation. UpdateAnnotationWithSNC(XmlToken.XOffset | XmlToken.YOffset | XmlToken.Width | XmlToken.Height | XmlToken.Left | XmlToken.Top); } } ////// Called when either thumb is dragged. We then process the drag in specific ways depending /// on which thumb was dragged. /// private void OnDragDelta(object sender, DragDeltaEventArgs args) { Invariant.Assert(IsExpanded == true, "Dragging occurred when the StickyNoteControl was not expanded."); Thumb source = args.Source as Thumb; double horizontalChange = args.HorizontalChange; // Because we are self-mirroring, we have flipped the layout within the note // but our environment isn't flipped. The Thumb (within the note) is providing // mirrored values but we will use them to position ourselves within the unmirrored // environment, so we flip the values again before using them. if (_selfMirroring) { horizontalChange = -horizontalChange; } if (source == GetTitleThumb()) { OnTitleDragDelta(horizontalChange, args.VerticalChange); } else if (source == GetResizeThumb()) { OnResizeDragDelta(args.HorizontalChange, args.VerticalChange); } // Update the cached offsets of StickyNote UpdateOffsets(); } ////// Handles dragging the title thumb. This updates the StickyNoteControl's /// position. /// private void OnTitleDragDelta(double horizontalChange, double verticalChange) { Invariant.Assert(IsExpanded != false); Rect rectNote = StickyNoteBounds; Rect rectPage = PageBounds; // These are the minimum widths that must be visible when a note is partially off // the left or right side of a page. The difference is due to the Close button // being on one side and wanting some of the title bar to be visible on that side. double leftBoundary = 45; double rightBoundary = 20; // Because we are self-mirroring, we need to flip the minimum widths if (_selfMirroring) { double temp = rightBoundary; rightBoundary = leftBoundary; leftBoundary = temp; } // Figure out the new position while enforcing a portion of the note being always on the page. Point minBoundary = new Point(-(rectNote.X + rectNote.Width - leftBoundary), - rectNote.Y); Point maxBoundary = new Point(rectPage.Width - rectNote.X - rightBoundary, rectPage.Height - rectNote.Y - 20); horizontalChange = Math.Min(Math.Max(minBoundary.X, horizontalChange), maxBoundary.X); verticalChange = Math.Min(Math.Max(minBoundary.Y, verticalChange), maxBoundary.Y); TranslateTransform currentTransform = PositionTransform; // Include any temporary delta we are currently using to avoid any visible jumping to the user PositionTransform = new TranslateTransform(currentTransform.X + horizontalChange + _deltaX, currentTransform.Y + verticalChange + _deltaY); _deltaX = _deltaY = 0; IsDirty = true; } ////// Handles dragging the bottom/right resize thumb. Updates the StickyNoteControl's size. /// private void OnResizeDragDelta(double horizontalChange, double verticalChange) { Invariant.Assert(IsExpanded != false); Rect rectNote = StickyNoteBounds; double wNew = rectNote.Width + horizontalChange; double hNew = rectNote.Height + verticalChange; // This method doesn't apply during self-mirroring because // we are actually moving the note during which time the note's // location is bounded by the page borders (plus a cushion). if (!_selfMirroring) { // If the new size would put the right side of the SN off the page // we don't allow that resize anymore. if (rectNote.X + wNew < 45) wNew = rectNote.Width; } double minWidth = MinWidth; double minHeight = MinHeight; if (wNew < minWidth) { wNew = minWidth; // This is only used in self-mirroring - if we clamp the size change, // we must also clamp the move (see below) horizontalChange = wNew - this.Width; } if (hNew < minHeight) { hNew = minHeight; } SetValue(WidthProperty, wNew); SetValue(HeightProperty, hNew); // Because we are self-mirroring, as the note changes size we have to // move it to make it appear its changing size from the left (where the // resize handle is located during mirroring) instead of the right which // is what is actually happening. if (_selfMirroring) { OnTitleDragDelta(-horizontalChange, 0); } else { // This appears to be a no-op but OnTitleDragDelta also takes // care of applying permanently any temporary offsets OnTitleDragDelta(0, 0); } IsDirty = true; } ////// Any click in the StickyNoteControl brings it to the top in z-order and /// requests the focus. /// Additionally we must `swallow the event here if the click happend on /// our InkCanvas because the RTI engine isn't setup yet and the user will /// be creating ink without seeing it. /// private void OnPreviewDeviceDown(object dc, InputEventArgs args) { if (IsExpanded) { bool eatEvent = false; if (!IsKeyboardFocusWithin && this.StickyNoteType == StickyNoteType.Ink) { // Only event we want to `swallow here is a click on the InkCanvas // when the StickyNote isn't focused because RTI isn't set up yet Visual source = args.OriginalSource as Visual; if (source != null) { Invariant.Assert(Content.InnerControl != null, "InnerControl is null."); eatEvent = source.IsDescendantOf(this.Content.InnerControl); } } // Will have no effect if already in front BringToFront(); if (!IsActive || !IsKeyboardFocusWithin) { Focus(); } if (eatEvent == true) { args.Handled = true; } } } ////// Set the focus on SN if needed /// /// sender - not used /// arguments - not used private void OnLoadedEventHandler(object sender, RoutedEventArgs e) { if (IsExpanded) { // Setup the inner controls with the Annotation's data - we must have correct // values for SN sizes before Focus->BringIntoView is invoked UpdateSNCWithAnnotation(SNCAnnotation.Sizes); if (_sncAnnotation.IsNewAnnotation) { // NTRAID#WINOS-1169084-2005/05/19-WAYNEZEN // After the annotations has been added, we should set focus on the element. Focus(); } } //unregister Loaded -= new RoutedEventHandler(OnLoadedEventHandler); } ////// Unregister from any events on the current visual tree. Also disconnect the current /// content control. /// private void ClearCachedControls() { if (Content != null) { // Disconnect the content control which will be re-connected to the new ContentControl // in ApplyTemplate when the new visual tree is populated from the new control template. DisconnectContent(); } Button closeButton = GetCloseButton(); if (closeButton != null) { closeButton.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } Button iconButton = GetIconButton(); if (iconButton != null) { iconButton.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnButtonClick)); } Thumb titleThumb = GetTitleThumb(); if (titleThumb != null) { titleThumb.RemoveHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); titleThumb.RemoveHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } Thumb resizeThumb = GetResizeThumb(); if (resizeThumb != null) { resizeThumb.RemoveHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta)); resizeThumb.RemoveHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted)); } } ////// Called when this StickyNoteControl's IsExpanded property changes. /// private void OnIsExpandedChanged() { InvalidateTransform(); // Update the Iconized in the annotation. UpdateAnnotationWithSNC(XmlToken.IsExpanded); IsDirty = true; if (IsExpanded) { BringToFront(); // We request the focus from a dispatcher callback because we may // have been called from a this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(AsyncTakeFocus), null); } else { GiveUpFocus(); SendToBack(); } } ////// Requests focus to this control. Used asynchronously from event handlers /// to prevent other event handlers from stealing focus right back. /// private object AsyncTakeFocus(object notUsed) { this.Focus(); return null; } ////// If focus us within this control, sets the focus on the first element in the attached /// annotation's parent's ancestor chain that accepts it. If no element accepts it, /// then focus is set to null. /// private void GiveUpFocus() { // Only send focus away when we actually have it. if (IsKeyboardFocusWithin) { // Start with the attached annotation's parent, // walk up the tree looking for the first element // to take focus. bool transferred = false; DependencyObject parent = _attachedAnnotation.Parent; IInputElement newFocus = null; while (parent != null && !transferred) { newFocus = parent as IInputElement; if (newFocus != null) { transferred = newFocus.Focus(); } // Go up the parent chain if focus wasn't taken if (!transferred) { parent = FrameworkElement.GetFrameworkParent(parent); } } // If no element was found, just give up focus if (!transferred) { Keyboard.Focus(null); } } } ////// Request this control be sent to the front of the z-order stack in its /// presentation context. Call to PresentationContext should have no affect /// if its already at the front. /// private void BringToFront() { PresentationContext pc = ((IAnnotationComponent)this).PresentationContext; if ( pc != null ) { pc.BringToFront(this); } } ////// Request this control be sent to the back of the z-order stack in its /// presentation context. Call to PresentationContext should have no affect /// if its already at the back. /// private void SendToBack() { PresentationContext pc = ((IAnnotationComponent)this).PresentationContext; if (pc != null) { pc.SendToBack(this); } } ////// Invalidate the Transform for this control in its presentation context. /// private void InvalidateTransform() { PresentationContext pc = ((IAnnotationComponent)this).PresentationContext; if ( pc != null ) { pc.InvalidateTransform(this); } } ////// Updates the attached annotation with the data currently in this control. /// Called asynchronously from event handler because TextBox can't handle /// changing its content from an event handler. // Do we need this to be async? /// private object AsyncUpdateAnnotation(object arg) { UpdateAnnotationWithSNC((XmlToken)arg); return null; } ////// This binds a new content control's properties to those set on /// the StickyNoteControl. Should be called each time a new content /// control is created. /// private void BindContentControlProperties() { Invariant.Assert(Content != null); // ISSUE-2005/03/23/WAYNEZEN, // Somehow, the bound font family can't be loaded again by Parser once the attribute persists in XAML. // Since InkCanvas doesn't care about FontFamily, we just walk the issue around by not binding the property. if (Content.Type != StickyNoteType.Ink) { FrameworkElement innerControl = Content.InnerControl; innerControl.SetValue(FontFamilyProperty, GetValue(FontFamilyProperty)); innerControl.SetValue(FontSizeProperty, GetValue(FontSizeProperty)); innerControl.SetValue(FontStretchProperty, GetValue(FontStretchProperty)); innerControl.SetValue(FontStyleProperty, GetValue(FontStyleProperty)); innerControl.SetValue(FontWeightProperty, GetValue(FontWeightProperty)); innerControl.SetValue(ForegroundProperty, GetValue(ForegroundProperty)); } else { // Create a TwoWay MultiBinding for InkCanvas.EditingMode. // The internal InkCanvas' EditingMode will be determined by // both StickyNoteControl.InkEditingMode and StickyNoteControl.IsKeyboardFocusWithin // If StickyNoteControl.IsKeyboardFocusWithin is false, the InkCanvas.EditingMode should be none. // Otherwise InkCanvas.EditingMode is same as the StickyNoteControl.InkEditingMode. MultiBinding inkCanvasEditingMode = new MultiBinding(); inkCanvasEditingMode.Mode = BindingMode.TwoWay; inkCanvasEditingMode.Converter = new InkEditingModeIsKeyboardFocusWithin2EditingMode(); Binding stickyNoteInkEditingMode = new Binding(); stickyNoteInkEditingMode.Mode = BindingMode.TwoWay; stickyNoteInkEditingMode.Path = new PropertyPath(StickyNoteControl.InkEditingModeProperty); stickyNoteInkEditingMode.Source = this; inkCanvasEditingMode.Bindings.Add(stickyNoteInkEditingMode); Binding stickyNoteIsKeyboardFocusWithin = new Binding(); stickyNoteIsKeyboardFocusWithin.Path = new PropertyPath(UIElement.IsKeyboardFocusWithinProperty); stickyNoteIsKeyboardFocusWithin.Source = this; inkCanvasEditingMode.Bindings.Add(stickyNoteIsKeyboardFocusWithin); Content.InnerControl.SetBinding(InkCanvas.EditingModeProperty, inkCanvasEditingMode); } } ////// Removes the bindings we previously created between the content control /// and the StickyNoteControl. /// private void UnbindContentControlProperties() { Invariant.Assert(Content != null); FrameworkElement innerControl = (FrameworkElement)Content.InnerControl; if (Content.Type != StickyNoteType.Ink) { innerControl.ClearValue(FontFamilyProperty); innerControl.ClearValue(FontSizeProperty); innerControl.ClearValue(FontStretchProperty); innerControl.ClearValue(FontStyleProperty); innerControl.ClearValue(FontWeightProperty); innerControl.ClearValue(ForegroundProperty); } else { BindingOperations.ClearBinding(innerControl, InkCanvas.EditingModeProperty); } } ////// Registers for change events from the content control. This lets us /// update the annotation when something in the controls change. Should /// be called when a new content control is created. /// private void StartListenToContentControlEvent() { Invariant.Assert(Content != null); if (Content.Type == StickyNoteType.Ink) { InkCanvas inkCanvas = Content.InnerControl as InkCanvas; Invariant.Assert(inkCanvas != null, "InnerControl is not an InkCanvas for note of type Ink."); inkCanvas.StrokesReplaced += new InkCanvasStrokesReplacedEventHandler(OnInkCanvasStrokesReplacedEventHandler); inkCanvas.SelectionMoving += new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionMovingEventHandler); inkCanvas.SelectionResizing += new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionResizingEventHandler); StartListenToStrokesEvent(inkCanvas.Strokes); } else { TextBoxBase textBoxBase = Content.InnerControl as TextBoxBase; Invariant.Assert(textBoxBase != null, "InnerControl is not a TextBoxBase for note of type Text."); textBoxBase.TextChanged += new TextChangedEventHandler(OnTextChanged); } } ////// Unregisters for any events from the content control. Should be called /// when a content control is being changed. /// private void StopListenToContentControlEvent() { Invariant.Assert(Content != null); if (Content.Type == StickyNoteType.Ink) { InkCanvas inkCanvas = Content.InnerControl as InkCanvas; Invariant.Assert(inkCanvas != null, "InnerControl is not an InkCanvas for note of type Ink."); inkCanvas.StrokesReplaced -= new InkCanvasStrokesReplacedEventHandler(OnInkCanvasStrokesReplacedEventHandler); inkCanvas.SelectionMoving -= new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionMovingEventHandler); inkCanvas.SelectionResizing -= new InkCanvasSelectionEditingEventHandler(OnInkCanvasSelectionResizingEventHandler); StopListenToStrokesEvent(inkCanvas.Strokes); } else { TextBoxBase textBoxBase = Content.InnerControl as TextBoxBase; Invariant.Assert(textBoxBase != null, "InnerControl is not a TextBoxBase for note of type Text."); textBoxBase.TextChanged -= new TextChangedEventHandler(OnTextChanged); } } ////// Register for events on the StrokeCollection from the InkCanvas. /// private void StartListenToStrokesEvent(StrokeCollection strokes) { strokes.StrokesChanged += new StrokeCollectionChangedEventHandler(OnInkStrokesChanged); strokes.PropertyDataChanged += new PropertyDataChangedEventHandler(_propertyDataChangedHandler.OnStrokeChanged); StartListenToStrokeEvent(strokes); } ////// Unregister for events on the StrokeCollection from the InkCanvas. /// private void StopListenToStrokesEvent(StrokeCollection strokes) { strokes.StrokesChanged -= new StrokeCollectionChangedEventHandler(OnInkStrokesChanged); strokes.PropertyDataChanged -= new PropertyDataChangedEventHandler(_propertyDataChangedHandler.OnStrokeChanged); StopListenToStrokeEvent(strokes); } ////// Register on each stroke in the InkCanvas. If any of them change we need to know about it. /// private void StartListenToStrokeEvent(IEnumerablestrokes) { foreach (Stroke s in strokes) { s.DrawingAttributes.AttributeChanged += new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); s.DrawingAttributesReplaced += new DrawingAttributesReplacedEventHandler( _strokeDrawingAttributesReplacedHandler.OnStrokeChanged); s.StylusPointsReplaced += new StylusPointsReplacedEventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.StylusPoints.Changed += new EventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.PropertyDataChanged += new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); } } /// /// Unregister on each stroke in the InkCanvas. We previously registered on each stroke and /// now need to disconnect from them. /// private void StopListenToStrokeEvent(IEnumerablestrokes) { foreach (Stroke s in strokes) { s.DrawingAttributes.AttributeChanged -= new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); s.DrawingAttributesReplaced -= new DrawingAttributesReplacedEventHandler( _strokeDrawingAttributesReplacedHandler.OnStrokeChanged); s.StylusPointsReplaced -= new StylusPointsReplacedEventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.StylusPoints.Changed -= new EventHandler( _strokePacketDataChangedHandler.OnStrokeChanged); s.PropertyDataChanged -= new PropertyDataChangedEventHandler( _propertyDataChangedHandler.OnStrokeChanged); } } /// /// Set bindings on the menuitems which StickyNote knows about /// private void SetupMenu() { MenuItem inkMenuItem = GetInkMenuItem(); if (inkMenuItem != null) { // Bind the EditingMode to item's IsChecked DP. Binding checkedBind = new Binding("InkEditingMode"); checkedBind.Mode = BindingMode.OneWay; checkedBind.RelativeSource = RelativeSource.TemplatedParent; checkedBind.Converter = new InkEditingModeConverter(); checkedBind.ConverterParameter = InkCanvasEditingMode.Ink; inkMenuItem.SetBinding(MenuItem.IsCheckedProperty, checkedBind); } MenuItem selectMenuItem = GetSelectMenuItem(); if (selectMenuItem != null) { // Bind the EditingMode to item's IsChecked DP. Binding checkedBind = new Binding("InkEditingMode"); checkedBind.Mode = BindingMode.OneWay; checkedBind.RelativeSource = RelativeSource.TemplatedParent; checkedBind.Converter = new InkEditingModeConverter(); checkedBind.ConverterParameter = InkCanvasEditingMode.Select; selectMenuItem.SetBinding(MenuItem.IsCheckedProperty, checkedBind); } MenuItem eraseMenuItem = GetEraseMenuItem(); if (eraseMenuItem != null) { // Bind the EditingMode to item's IsChecked DP. Binding checkedBind = new Binding("InkEditingMode"); checkedBind.Mode = BindingMode.OneWay; checkedBind.RelativeSource = RelativeSource.TemplatedParent; checkedBind.Converter = new InkEditingModeConverter(); checkedBind.ConverterParameter = InkCanvasEditingMode.EraseByStroke; eraseMenuItem.SetBinding(MenuItem.IsCheckedProperty, checkedBind); } // Copy and Paste menu items (and their separator) are removed if // we don't have Clipboard permissions. bool hasClipboardPermission = SecurityHelper.CallerHasAllClipboardPermission(); // Set the target for the Copy/Paste commands to our inner control MenuItem copyMenuItem = GetCopyMenuItem(); if (copyMenuItem != null) { if (hasClipboardPermission) { copyMenuItem.CommandTarget = Content.InnerControl; } else { copyMenuItem.Visibility = Visibility.Collapsed; } } MenuItem pasteMenuItem = GetPasteMenuItem(); if (pasteMenuItem != null) { if (hasClipboardPermission) { pasteMenuItem.CommandTarget = Content.InnerControl; } else { pasteMenuItem.Visibility = Visibility.Collapsed; } } Separator clipboardSeparator = GetClipboardSeparator(); if (clipboardSeparator != null) { if (!hasClipboardPermission) { clipboardSeparator.Visibility = Visibility.Collapsed; } } } ////// CommandExecuted Handler - processes the commands for SNC. /// DeleteNoteCommand - deletes the annotation from the store (causing the SNC to disappear) /// InkCommand - changes the mode of the ink canvas /// Copy/Paste - we pass on to our inner control /// /// /// private static void _OnCommandExecuted(object sender, ExecutedRoutedEventArgs args) { RoutedCommand command = (RoutedCommand)(args.Command); StickyNoteControl snc = sender as StickyNoteControl; Invariant.Assert(snc != null, "Unexpected Commands"); Invariant.Assert(command == StickyNoteControl.DeleteNoteCommand || command == StickyNoteControl.InkCommand, "Unknown Commands"); if ( command == StickyNoteControl.DeleteNoteCommand ) { // DeleteNote Command snc.DeleteStickyNote(); } else if (command == StickyNoteControl.InkCommand) { StickyNoteContentControl content = snc.Content; if (content == null || content.Type != StickyNoteType.Ink) { throw new InvalidOperationException(SR.Get(SRID.CannotProcessInkCommand)); } // Set the StickyNoteControl's ink editing mode to the command's parameter InkCanvasEditingMode mode = (InkCanvasEditingMode)args.Parameter; snc.SetValue(InkEditingModeProperty, mode); } } ////// QueryCommandEnabled Handler - determines if a command should be enabled or not. /// DeleteNoteCommand - if the SNC has an attached annotation /// InkCommand - if the SNC is of type InkStickyNote /// AllOthers - if focus is on our inner control, we let the inner control decide, /// otherwise we return false /// private static void _OnQueryCommandEnabled(object sender, CanExecuteRoutedEventArgs args) { RoutedCommand command = (RoutedCommand)( args.Command ); StickyNoteControl snc = sender as StickyNoteControl; Invariant.Assert(snc != null, "Unexpected Commands"); Invariant.Assert(command == StickyNoteControl.DeleteNoteCommand || command == StickyNoteControl.InkCommand, "Unknown Commands"); if ( command == StickyNoteControl.DeleteNoteCommand ) { // Enable/Disable DeleteNote Command based on the Attached Annotation. args.CanExecute = snc._attachedAnnotation != null; } else if (command == StickyNoteControl.InkCommand) { StickyNoteContentControl content = snc.Content; // Enabled/Disable InkCommand based on the StickyNote type args.CanExecute = (content != null && content.Type == StickyNoteType.Ink); } else { Invariant.Assert(false, "Unknown command."); } } ////// Update DrawingAttributes on InkCanvas /// private void UpdateInkDrawingAttributes() { if ( Content == null || Content.Type != StickyNoteType.Ink ) { // Return now if there is no InkCanvas. return; } DrawingAttributes da = new DrawingAttributes(); SolidColorBrush foreground = Foreground as SolidColorBrush; // Make sure the foreground is type of SolidColorBrush. if ( foreground == null ) { throw new ArgumentException(SR.Get(SRID.InvalidInkForeground), "Foreground"); } da.StylusTip = StylusTip.Ellipse; da.Width = PenWidth; da.Height = PenWidth; da.Color = foreground.Color; // Update the DA on InkCanvas. ( (InkCanvas)( Content.InnerControl ) ).DefaultDrawingAttributes = da; } #endregion // Private Methods //-------------------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------------------- #region Private Properties ////// Returns Ink MenuItem /// private MenuItem GetInkMenuItem() { return GetTemplateChild(SNBConstants.c_InkMenuId) as MenuItem; } ////// Returns Select MenuItem /// private MenuItem GetSelectMenuItem() { return GetTemplateChild(SNBConstants.c_SelectMenuId) as MenuItem; } ////// Returns Erase MenuItem /// private MenuItem GetEraseMenuItem() { return GetTemplateChild(SNBConstants.c_EraseMenuId) as MenuItem; } ////// Returns Copy MenuItem /// private MenuItem GetCopyMenuItem() { return GetTemplateChild(SNBConstants.c_CopyMenuId) as MenuItem; } ////// Returns Paste MenuItem /// private MenuItem GetPasteMenuItem() { return GetTemplateChild(SNBConstants.c_PasteMenuId) as MenuItem; } ////// Returns Separator for clipboard MenuItems /// private Separator GetClipboardSeparator() { return GetTemplateChild(SNBConstants.c_ClipboardSeparatorId) as Separator; } // This is the getter of _lockHelper which is a helper object for locking/unlocking a specified flag automatically. private LockHelper InternalLocker { get { // Check if we have create a helper. If not, we go ahead create one. if (_lockHelper == null) { _lockHelper = new LockHelper(); } return _lockHelper; } } #endregion // Private Properties //------------------------------------------------------------------------------- // // Private Fields // //------------------------------------------------------------------------------- #region Private Fields private LockHelper _lockHelper; private MarkedHighlightComponent _anchor; //holds inactive, active, focused state private bool _dirty = false; // Cache for the dependency properties private StickyNoteType _stickyNoteType = StickyNoteType.Text; private StickyNoteContentControl _contentControl; private StrokeChangedHandler_propertyDataChangedHandler; private StrokeChangedHandler _strokeDrawingAttributesReplacedHandler; private StrokeChangedHandler _strokePacketDataChangedHandler; #endregion // Private Fields //-------------------------------------------------------------------------------- // // Private classes // //------------------------------------------------------------------------------- #region Private classes // This is a binding converter which alters the IsChecked DP based on ink StickyNote's EditingMode of its InkCanvas. private class InkEditingModeConverter : IValueConverter { public object Convert(object o, Type type, object parameter, CultureInfo culture) { InkCanvasEditingMode expectedMode = (InkCanvasEditingMode)parameter; InkCanvasEditingMode currentMode = (InkCanvasEditingMode)o; // If the current EditingMode is the mode which menuitem is expecting, return true for IsChecked. if ( currentMode == expectedMode ) { return true; } else { return DependencyProperty.UnsetValue; } } public object ConvertBack(object o, Type type, object parameter, CultureInfo culture) { return null; } } // This is a binding converter which alters the InkCanvas.EditingMode DP // based on StickyNoteControl.InkEditingMode and StickyNoteControl.IsKeyboardFocusWithin. private class InkEditingModeIsKeyboardFocusWithin2EditingMode : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { InkCanvasEditingMode sncInkEditingMode = (InkCanvasEditingMode)values[0]; bool sncIsKeyboardFocusWithin = (bool)values[1]; // If there is no focus on the StickyNote, we should return InkCanvasEditingMode.None to disable the RTI. // Otherwise return the value of the StickyNoteControl.InkEditingMode property. if ( sncIsKeyboardFocusWithin ) { return sncInkEditingMode; } else { return InkCanvasEditingMode.None; } } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { return new object[] { value, Binding.DoNothing }; } } // A helper class which suppresses severial handlers to a single methods by using generic private class StrokeChangedHandler { public StrokeChangedHandler(StickyNoteControl snc) { Invariant.Assert(snc != null); _snc = snc; } public void OnStrokeChanged(object sender, TEventArgs t) { // Dirty the ink data _snc.UpdateAnnotationWithSNC(XmlToken.Ink); _snc._dirty = true; } private StickyNoteControl _snc; } #endregion Private classes } } // 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
- DataGrid.cs
- RewritingSimplifier.cs
- DecoratedNameAttribute.cs
- FileDialog.cs
- Byte.cs
- ConfigXmlSignificantWhitespace.cs
- WmlObjectListAdapter.cs
- TriggerCollection.cs
- ProcessInputEventArgs.cs
- ClientTargetCollection.cs
- DataRelationPropertyDescriptor.cs
- EventEntry.cs
- MenuAutomationPeer.cs
- TransactionFlowProperty.cs
- ThreadTrace.cs
- XmlDataCollection.cs
- DrawListViewSubItemEventArgs.cs
- PublisherIdentityPermission.cs
- AlternateView.cs
- AutomationElementIdentifiers.cs
- SurrogateDataContract.cs
- DriveInfo.cs
- StylusButton.cs
- ApplicationInfo.cs
- Shape.cs
- COM2ExtendedTypeConverter.cs
- BasicExpressionVisitor.cs
- FieldBuilder.cs
- ColumnCollectionEditor.cs
- TransformCollection.cs
- SplineQuaternionKeyFrame.cs
- SynchronizedDispatch.cs
- DefaultPrintController.cs
- Regex.cs
- BamlWriter.cs
- HostProtectionPermission.cs
- Profiler.cs
- LifetimeServices.cs
- CodeMethodInvokeExpression.cs
- DesignConnectionCollection.cs
- FlowLayoutSettings.cs
- DbParameterCollectionHelper.cs
- CompModHelpers.cs
- StrokeNodeOperations.cs
- Clause.cs
- QueryAccessibilityHelpEvent.cs
- WebPartDeleteVerb.cs
- LinkArea.cs
- AncestorChangedEventArgs.cs
- VisualBrush.cs
- SID.cs
- VectorValueSerializer.cs
- XmlSchemaImporter.cs
- PagedDataSource.cs
- RegisteredExpandoAttribute.cs
- WebPartConnectionsEventArgs.cs
- nulltextcontainer.cs
- ScriptingJsonSerializationSection.cs
- InputEventArgs.cs
- ResourcePool.cs
- ProxySimple.cs
- StylusDownEventArgs.cs
- DesignerAttribute.cs
- NegotiateStream.cs
- Variant.cs
- AsyncInvokeContext.cs
- ProgressBarRenderer.cs
- TextTreeFixupNode.cs
- Expression.cs
- BasicExpressionVisitor.cs
- StorageComplexTypeMapping.cs
- DataListItemEventArgs.cs
- NetCodeGroup.cs
- RoutedPropertyChangedEventArgs.cs
- RoleManagerSection.cs
- Resources.Designer.cs
- webclient.cs
- ParseHttpDate.cs
- WebZone.cs
- InstanceDataCollectionCollection.cs
- AssemblyCache.cs
- querybuilder.cs
- FontResourceCache.cs
- EventWaitHandle.cs
- AdRotatorDesigner.cs
- TogglePattern.cs
- OutgoingWebResponseContext.cs
- CategoryNameCollection.cs
- MemoryPressure.cs
- WindowsAuthenticationEventArgs.cs
- HttpTransportElement.cs
- ValidatingPropertiesEventArgs.cs
- RegexGroup.cs
- CacheEntry.cs
- ReceiveParametersContent.cs
- UTF32Encoding.cs
- ProtocolsConfiguration.cs
- RemotingException.cs
- Resources.Designer.cs
- ServiceDescription.cs