Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / Ink / EditingCoordinator.cs / 1 / EditingCoordinator.cs
//---------------------------------------------------------------------------- // // File: EditingCoordinator.cs // // Description: // Coordinates editing for InkCanvas // Features: // // History: // 4/29/2003 waynezen: Rewrote for mid-stroke support // 3/15/2003 samgeo: Created // // Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.ComponentModel; using System.ComponentModel.Design; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Security; using System.Security.Permissions; using System.Windows.Ink; using System.Windows.Input; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Interop; using System.Windows.Navigation; using System.Windows.Media; using MS.Utility; namespace MS.Internal.Ink { ////// Internal class that represents the editing stack of InkCanvas /// Please see the design detain at http://tabletpc/longhorn/Specs/Mid-Stroke%20and%20Pen%20Cursor%20Dev%20Design.mht /// internal class EditingCoordinator { //------------------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------------------- #region Constructors ////// Constructors /// /// InkCanvas instance internal EditingCoordinator(InkCanvas inkCanvas) { if (inkCanvas == null) { throw new ArgumentNullException("inkCanvas"); } _inkCanvas = inkCanvas; // Create a stack for tracking the behavior _activationStack = new Stack(2); // NTRAID#WINDOWS-1104477-2005/02/26-waynezen, // listen to in-air move so that we could get notified when Stylus is inverted. // _inkCanvas.AddHandler(Stylus.StylusInRangeEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove)); _inkCanvas.AddHandler(Stylus.StylusInAirMoveEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove)); _inkCanvas.AddHandler(Stylus.StylusOutOfRangeEvent, new StylusEventHandler(OnInkCanvasStylusOutOfRange)); } #endregion Constructors //-------------------------------------------------------------------------------- // // Internal Methods // //------------------------------------------------------------------------------- #region Internal Methods /// /// Activate a dynamic behavior /// Called from: /// SelectionEditor.OnCanvasMouseMove /// /// /// ////// Critical: Calls critical methods AddStylusPoints and InitializeCapture /// [SecurityCritical] internal void ActivateDynamicBehavior(EditingBehavior dynamicBehavior, InputDevice inputDevice) { // Only SelectionEditor should be enable to initiate dynamic behavior Debug.Assert(ActiveEditingBehavior == SelectionEditor, "Only SelectionEditor should be able to initiate dynamic behavior"); DebugCheckDynamicBehavior(dynamicBehavior); // Push the dynamic behavior. PushEditingBehavior(dynamicBehavior); // If this is LassoSelectionBehavior, we should capture the current stylus and feed the cached if ( dynamicBehavior == LassoSelectionBehavior ) { bool fSucceeded = false; // The below code might call out StrokeErasing or StrokeErased event. // The out-side code could throw exception. // We use try/finally block to protect our status. try { //we pass true for userInitiated because we've simply consulted the InputDevice //(and only StylusDevice or MouseDevice) for the current position of the device InitializeCapture(inputDevice, (IStylusEditing)dynamicBehavior, true /*userInitiated*/, false/*Don't reset the RTI*/); fSucceeded = true; } finally { if ( !fSucceeded ) { // Abort the current editing. ActiveEditingBehavior.Commit(false); // Release capture and do clean-up. ReleaseCapture(true); } } } _inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas)); } ////// Deactivate a dynamic behavior /// Called from: /// EditingBehavior.Commit /// internal void DeactivateDynamicBehavior() { // Make sure we are under correct state: // ActiveEditingBehavior has to be either LassoSelectionBehavior or SelectionEditingBehavior. DebugCheckDynamicBehavior(ActiveEditingBehavior); // Pop the dynamic behavior. PopEditingBehavior(); } ////// Update the current active EditingMode /// Called from: /// EditingCoordinator.UpdateInvertedState /// InkCanvas.Initialize() /// internal void UpdateActiveEditingState() { UpdateEditingState(_stylusIsInverted); } ////// Update the EditingMode /// Called from: /// InkCanvas.RaiseEditingModeChanged /// InkCanvas.RaiseEditingModeInvertedChanged /// /// A flage which indicates whether the new mode is for the Inverted state internal void UpdateEditingState(bool inverted) { // If the mode is inactive currently, we should skip the update. if ( inverted != _stylusIsInverted ) { return; } // Get the current behavior and the new behavior. EditingBehavior currentBehavior = ActiveEditingBehavior; EditingBehavior newBehavior = GetBehavior(ActiveEditingMode); // Check whether the user is editing. if ( UserIsEditing ) { // Check if we are in the mid-stroke. if ( IsInMidStroke ) { // We are in mid-stroke now. Debug.Assert(currentBehavior is StylusEditingBehavior, "The current behavoir has to be one of StylusEditingBehaviors"); ( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode); } else { if ( currentBehavior == SelectionEditingBehavior ) { // Commit the current moving/resizing behavior currentBehavior.Commit(true); } ChangeEditingBehavior(newBehavior); } } else { // Check if we are in the mid-stroke. if ( IsInMidStroke ) { // We are in mid-stroke now. Debug.Assert(currentBehavior is StylusEditingBehavior, "The current behavoir has to be one of StylusEditingBehaviors"); ( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode); } else { // Make sure we are under correct state: // currentBehavior cannot be any of Dynamic Behavior. DebugCheckNonDynamicBehavior(currentBehavior); ChangeEditingBehavior(newBehavior); } } _inkCanvas.UpdateCursor(); } ////// Update the PointEraerCursor /// Called from: /// InkCanvas.set_EraserShape /// internal void UpdatePointEraserCursor() { // We only have to update the point eraser when the active mode is EraseByPoint // In other case, EraseBehavior will update cursor properly in its OnActivate routine. if ( ActiveEditingMode == InkCanvasEditingMode.EraseByPoint ) { InvalidateBehaviorCursor(EraserBehavior); } } ////// InvalidateTransform /// Called by: InkCanvas.OnPropertyChanged /// internal void InvalidateTransform() { // We only have to invalidate transform flags for InkCollectionBehavior and EraserBehavior for now. SetTransformValid(InkCollectionBehavior, false); SetTransformValid(EraserBehavior, false); } ////// Invalidate the behavior cursor /// Call from: /// EditingCoordinator.UpdatePointEraserCursor /// EraserBehavior.OnActivate /// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesReplaced /// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesChanged /// SelectionEditingBehavior.OnActivate /// SelectionEditor.UpdateCursor(InkCanvasSelectionHandle handle) /// /// the behavior which its cursor needs to be updated. internal void InvalidateBehaviorCursor(EditingBehavior behavior) { // Should never be null Debug.Assert(behavior != null); // InvalidateCursor first SetCursorValid(behavior, false); if ( !GetTransformValid(behavior) ) { behavior.UpdateTransform(); SetTransformValid(behavior, true); } // If the behavior is active, we have to update cursor right now. if ( behavior == ActiveEditingBehavior ) { _inkCanvas.UpdateCursor(); } } ////// Check whether the cursor of the specified behavior is valid /// /// EditingBehavior ///True if the cursor doesn't need to be updated internal bool IsCursorValid(EditingBehavior behavior) { return GetCursorValid(behavior); } ////// Check whether the cursor of the specified behavior is valid /// /// EditingBehavior ///True if the cursor doesn't need to be updated internal bool IsTransformValid(EditingBehavior behavior) { return GetTransformValid(behavior); } ////// Change to another StylusEditing mode or None mode. /// /// /// ///internal IStylusEditing ChangeStylusEditingMode(StylusEditingBehavior sourceBehavior, InkCanvasEditingMode newMode) { // NOTICE-2006/04/27-WAYNEZEN, // Before the caller invokes the method, the external event could have been fired. // The user code may interrupt the Mid-Stroke by releasing capture or switching to another mode. // So we should check if we still is under mid-stroke and the source behavior still is active. // If not, we just no-op. if ( IsInMidStroke && ( ( // We expect that either InkCollectionBehavior or EraseBehavior is active. sourceBehavior != LassoSelectionBehavior && sourceBehavior == ActiveEditingBehavior ) // Or We expect SelectionEditor is active here since // LassoSelectionBehavior will be deactivated once it gets committed. || ( sourceBehavior == LassoSelectionBehavior && SelectionEditor == ActiveEditingBehavior ) ) ) { Debug.Assert(_activationStack.Count == 1, "The behavior stack has to contain one behavior."); // Pop up the old behavior PopEditingBehavior(); // Get the new behavior EditingBehavior newBehavior = GetBehavior(ActiveEditingMode); if ( newBehavior != null ) { // Push the new behavior PushEditingBehavior(newBehavior); // If the new mode is Select, we should push the LassoSelectionBehavior now. if ( newMode == InkCanvasEditingMode.Select // NOTICE-2006/04/27-WAYNEZEN, // Make sure the newBehavior is SelectionEditor since it could be changed by the user event handling code. && newBehavior == SelectionEditor ) { PushEditingBehavior(LassoSelectionBehavior); } } else { // None-mode now. We stop collecting the packets. ReleaseCapture(true); } _inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas)); return ActiveEditingBehavior as IStylusEditing; } else { // No-op return null; } } /// /// Debug Checker /// /// [Conditional("DEBUG")] internal void DebugCheckActiveBehavior(EditingBehavior behavior) { Debug.Assert(behavior == ActiveEditingBehavior); } ////// Debug check for the dynamic behavior /// /// [Conditional("DEBUG")] private void DebugCheckDynamicBehavior(EditingBehavior behavior) { Debug.Assert(behavior == LassoSelectionBehavior || behavior == SelectionEditingBehavior, "Only LassoSelectionBehavior or SelectionEditingBehavior is dynamic behavior"); } ////// Debug check for the non dynamic behavior /// /// [Conditional("DEBUG")] private void DebugCheckNonDynamicBehavior(EditingBehavior behavior) { Debug.Assert(behavior != LassoSelectionBehavior && behavior != SelectionEditingBehavior, "behavior cannot be LassoSelectionBehavior or SelectionEditingBehavior"); } #endregion Internal Methods //-------------------------------------------------------------------------------- // // Internal Properties // //-------------------------------------------------------------------------------- #region Internal Properties ////// Gets / sets whether move is enabled or note /// internal bool MoveEnabled { get { return _moveEnabled; } set { _moveEnabled = value; } } ////// The property that indicates if the user is interacting with the current InkCanvas /// internal bool UserIsEditing { get { return _userIsEditing; } set { _userIsEditing = value; } } ////// Helper flag that tells if we're between a preview down and an up. /// internal bool StylusOrMouseIsDown { get { bool stylusDown = false; StylusDevice stylusDevice = Stylus.CurrentStylusDevice; if (stylusDevice != null && _inkCanvas.IsStylusOver && !stylusDevice.InAir) { stylusDown = true; } bool mouseDown = (_inkCanvas.IsMouseOver && Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed); if (stylusDown || mouseDown) { return true; } return false; } } ////// Returns the StylusDevice to call DynamicRenderer.Reset with. Stylus or Mouse /// must be in a down state. If the down device is a Mouse, null is returned, since /// that is what DynamicRenderer.Reset expects for the Mouse. /// ///internal InputDevice GetInputDeviceForReset() { Debug.Assert((_capturedStylus != null && _capturedMouse == null) || (_capturedStylus == null && _capturedMouse != null), "There must be one and at most one device being captured."); if ( _capturedStylus != null && !_capturedStylus.InAir ) { return _capturedStylus; } else if ( _capturedMouse != null && _capturedMouse.LeftButton == MouseButtonState.Pressed ) { return _capturedMouse; } return null; } /// /// Gets / sets whether resize is enabled or note /// internal bool ResizeEnabled { get { return _resizeEnabled; } set { _resizeEnabled = value; } } ////// Lazy init access to the LassoSelectionBehavior /// ///internal LassoSelectionBehavior LassoSelectionBehavior { get { if ( _lassoSelectionBehavior == null ) { _lassoSelectionBehavior = new LassoSelectionBehavior(this, _inkCanvas); } return _lassoSelectionBehavior; } } /// /// Lazy init access to the SelectionEditingBehavior /// ///internal SelectionEditingBehavior SelectionEditingBehavior { get { if ( _selectionEditingBehavior == null ) { _selectionEditingBehavior = new SelectionEditingBehavior(this, _inkCanvas); } return _selectionEditingBehavior; } } /// /// Internal helper prop to indicate the active editing mode /// internal InkCanvasEditingMode ActiveEditingMode { get { if ( _stylusIsInverted ) { return _inkCanvas.EditingModeInverted; } return _inkCanvas.EditingMode; } } ////// Lazy init access to the SelectionEditor /// ///internal SelectionEditor SelectionEditor { get { if ( _selectionEditor == null ) { _selectionEditor = new SelectionEditor(this, _inkCanvas); } return _selectionEditor; } } /// /// Gets the mid-stroke state /// internal bool IsInMidStroke { get { return _capturedStylus != null || _capturedMouse != null; } } ////// Returns Stylus Inverted state /// internal bool IsStylusInverted { get { return _stylusIsInverted; } } #endregion Internal Properties //------------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------------- #region Private Methods ////// Retrieve the behavior instance based on the EditingMode /// /// EditingMode ///private EditingBehavior GetBehavior(InkCanvasEditingMode editingMode) { EditingBehavior newBehavior; switch ( editingMode ) { case InkCanvasEditingMode.Ink: case InkCanvasEditingMode.GestureOnly: case InkCanvasEditingMode.InkAndGesture: newBehavior = InkCollectionBehavior; break; case InkCanvasEditingMode.Select: newBehavior = SelectionEditor; break; case InkCanvasEditingMode.EraseByPoint: case InkCanvasEditingMode.EraseByStroke: newBehavior = EraserBehavior; break; default: // Treat as InkCanvasEditingMode.None. Return null value newBehavior = null; break; } return newBehavior; } /// /// Pushes an EditingBehavior onto our stack, disables any current ones /// /// The EditingBehavior to activate private void PushEditingBehavior(EditingBehavior newEditingBehavior) { Debug.Assert(newEditingBehavior != null); EditingBehavior behavior = ActiveEditingBehavior; // Deactivate the previous behavior if ( behavior != null ) { behavior.Deactivate(); } // Activate the new behavior. _activationStack.Push(newEditingBehavior); newEditingBehavior.Activate(); } ////// Pops an EditingBehavior onto our stack, disables any current ones /// private void PopEditingBehavior() { EditingBehavior behavior = ActiveEditingBehavior; if ( behavior != null ) { behavior.Deactivate(); _activationStack.Pop(); behavior = ActiveEditingBehavior; if ( ActiveEditingBehavior != null ) { behavior.Activate(); } } return; } ////// Handles in-air stylus movements /// private void OnInkCanvasStylusInAirOrInRangeMove(object sender, StylusEventArgs args) { // If the current capture is mouse, we should reset the capture. if ( _capturedMouse != null ) { if (ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null) { // NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN // When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink. // By flipping the Enabled property, the RTI will be reset. _inkCanvas.InternalDynamicRenderer.Enabled = false; _inkCanvas.InternalDynamicRenderer.Enabled = true; } // Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. ActiveEditingBehavior.Commit(true); // Release capture and do clean-up. ReleaseCapture(true); } UpdateInvertedState(args.StylusDevice, args.Inverted); } ////// Handle StylusOutofRange event /// /// /// private void OnInkCanvasStylusOutOfRange(object sender, StylusEventArgs args) { // Reset the inverted state once OutOfRange has been received. UpdateInvertedState(args.StylusDevice, false); } ////// InkCanvas.StylusDown handler /// /// /// ////// Critical: /// Calls SecurityCritcal method: InitializeCapture and AddStylusPoints /// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected /// /// TreatAsSafe: This method is called by the input system, from security transparent /// code, so it can not be marked critical. We check the eventArgs.UserInitiated /// to verify that the input was user initiated and pass this flag to /// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should /// perform gesture recognition /// [SecurityCritical, SecurityTreatAsSafe] internal void OnInkCanvasDeviceDown(object sender, InputEventArgs args) { MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs; bool resetDynamicRenderer = false; if ( mouseButtonEventArgs != null ) { // NTRADI:WINDOWSOS#1563896-2006/03/20-WAYNEZEN, // Note we don't mark Handled for the None EditingMode. // Set focus to InkCanvas. if ( _inkCanvas.Focus() && ActiveEditingMode != InkCanvasEditingMode.None ) { mouseButtonEventArgs.Handled = true; } // ISSUE-2005/09/20-WAYNEZEN, // We will reevaluate whether we should allow the right-button down for the modes other than the Ink mode. // Skip collecting for the non-left buttons. if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left ) { return; } // NTRAID-WINDOWS#1607286-2006/04/14-WAYNEZEN, // If the mouse down event is from a Stylus, make sure we have a correct inverted state. if ( mouseButtonEventArgs.StylusDevice != null ) { UpdateInvertedState(mouseButtonEventArgs.StylusDevice, mouseButtonEventArgs.StylusDevice.Inverted); } } else { // NTRAID-WINDOWS#1395155-2005/12/15-WAYNEZEN, // When StylusDown is received, We should check the Invert state again. // If there is any change, the ActiveEditingMode will be updated. // The dynamic renderer will be reset in InkCollectionBehavior.OnActivated since the device is under down state. StylusEventArgs stylusEventArgs = args as StylusEventArgs; UpdateInvertedState(stylusEventArgs.StylusDevice, stylusEventArgs.Inverted); } // If the active behavior is not one of StylusEditingBehavior, don't even bother here. IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing; // We might receive StylusDown from a different device meanwhile we have already captured one. // Make sure that we are starting from a fresh state. if ( !IsInMidStroke && stylusEditingBehavior != null ) { bool fSucceeded = false; try { InputDevice capturedDevice = null; // Capture the stylus (if mouse event make sure to use stylus if generated by a stylus) if ( mouseButtonEventArgs != null && mouseButtonEventArgs.StylusDevice != null ) { capturedDevice = mouseButtonEventArgs.StylusDevice; resetDynamicRenderer = true; } else { capturedDevice = args.Device; } InitializeCapture(capturedDevice, stylusEditingBehavior, args.UserInitiated, resetDynamicRenderer); // The below code might call out StrokeErasing or StrokeErased event. // The out-side code could throw exception. // We use try/finally block to protect our status. fSucceeded = true; } finally { if ( !fSucceeded ) { // Abort the current editing. ActiveEditingBehavior.Commit(false); // Release capture and do clean-up. ReleaseCapture(IsInMidStroke); } } } } ////// InkCanvas.StylusMove handler /// /// /// ////// Critical: /// Calls SecurityCritcal method: /// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected /// /// TreatAsSafe: This method is called by the input system, from security transparent /// code, so it can not be marked critical. We check the eventArgs.UserInitiated /// to verify that the input was user initiated and pass this flag to /// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should /// perform gesture recognition /// [SecurityCritical, SecurityTreatAsSafe] private void OnInkCanvasDeviceMove(object sender, TEventArgs args) where TEventArgs : InputEventArgs { // Make sure that the stylus is the one we captured previously. if ( IsInputDeviceCaptured(args.Device) ) { IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing; Debug.Assert(stylusEditingBehavior != null || ActiveEditingBehavior == null, "The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing."); if ( stylusEditingBehavior != null ) { StylusPointCollection stylusPoints; if ( _capturedStylus != null ) { stylusPoints = _capturedStylus.GetStylusPoints(_inkCanvas, _commonDescription); } else { // Make sure we ignore stylus generated mouse events. MouseEventArgs mouseEventArgs = args as MouseEventArgs; if ( mouseEventArgs != null && mouseEventArgs.StylusDevice != null ) { return; } stylusPoints = new StylusPointCollection(new Point[] { _capturedMouse.GetPosition(_inkCanvas) }); } bool fSucceeded = false; // The below code might call out StrokeErasing or StrokeErased event. // The out-side code could throw exception. // We use try/finally block to protect our status. try { stylusEditingBehavior.AddStylusPoints(stylusPoints, args.UserInitiated); fSucceeded = true; } finally { if ( !fSucceeded ) { // Abort the current editing. ActiveEditingBehavior.Commit(false); // Release capture and do clean-up. ReleaseCapture(true); } } } } } /// /// InkCanvas.StylusUp handler /// /// /// internal void OnInkCanvasDeviceUp(object sender, InputEventArgs args) { // Make sure that the stylus is the one we captured previously. if ( IsInputDeviceCaptured(args.Device) ) { Debug.Assert(ActiveEditingBehavior == null || ActiveEditingBehavior is IStylusEditing, "The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing."); // Make sure we only look at mouse left button events if watching mouse events. if ( _capturedMouse != null ) { MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs; if ( mouseButtonEventArgs != null ) { if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left ) { return; } } } try { // The follow code raises variety editing events. // The out-side code could throw exception in the their handlers. We use try/finally block to protect our status. if ( ActiveEditingBehavior != null ) { // Commit the current editing. ActiveEditingBehavior.Commit(true); } } finally { // Call ReleaseCapture(true) at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. ReleaseCapture(true); } } } ////// InkCanvas.LostStylusCapture handler /// /// /// private void OnInkCanvasLostDeviceCapture(object sender, TEventArgs args) where TEventArgs : InputEventArgs { // If user is editing, we have to commit the current operation and reset our state. if ( UserIsEditing ) { // Note ReleaseCapture(false) won't raise any external events. It only reset the internal states. ReleaseCapture(false); if ( ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null ) { // NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN // When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink. // By flipping the Enabled property, the RTI will be reset. _inkCanvas.InternalDynamicRenderer.Enabled = false; _inkCanvas.InternalDynamicRenderer.Enabled = true; } // Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. ActiveEditingBehavior.Commit(true); } } /// /// Initialize the capture state /// /// /// /// /// ////// Critical: Returns critical data from the inputDevice, StylusPointCollection. /// [SecurityCritical] private void InitializeCapture(InputDevice inputDevice, IStylusEditing stylusEditingBehavior, bool userInitiated, bool resetDynamicRenderer) { Debug.Assert(inputDevice != null, "A null device is passed in."); Debug.Assert(stylusEditingBehavior != null, "stylusEditingBehavior cannot be null."); Debug.Assert(!IsInMidStroke, "The previous device hasn't been released yet."); StylusPointCollection stylusPoints; _capturedStylus = inputDevice as StylusDevice; _capturedMouse = inputDevice as MouseDevice; // NOTICE-2005/09/15-WAYNEZEN, // We assume that the StylusDown always happens before the MouseDown. So, we could safely add the listeners of // XXXMove and XXXUp as the below which branchs out the coming mouse or stylus events. if ( _capturedStylus != null ) { StylusPointCollection newPoints = _capturedStylus.GetStylusPoints(_inkCanvas); _commonDescription = StylusPointDescription.GetCommonDescription(_inkCanvas.DefaultStylusPointDescription, newPoints.Description); stylusPoints = newPoints.Reformat(_commonDescription); if ( resetDynamicRenderer ) { // Reset the dynamic renderer for InkCollectionBehavior InkCollectionBehavior inkCollectionBehavior = stylusEditingBehavior as InkCollectionBehavior; if ( inkCollectionBehavior != null ) { inkCollectionBehavior.ResetDynamicRenderer(); } } stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated); // If the current down device is stylus, we should only listen to StylusMove, StylusUp and LostStylusCapture // events. _inkCanvas.CaptureStylus(); if ( _inkCanvas.IsStylusCaptured && ActiveEditingMode != InkCanvasEditingMode.None ) { _inkCanvas.AddHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove)); _inkCanvas.AddHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture )); } else { _capturedStylus = null; } } else { Debug.Assert(!resetDynamicRenderer, "The dynamic renderer shouldn't be reset for Mouse"); _commonDescription = null; Point[] points = new Point[] { _capturedMouse.GetPosition(_inkCanvas) }; stylusPoints = new StylusPointCollection(points); stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated); // NTRAID:WINDOWSOS#1536974-2006/03/02-WAYNEZEN, // CaptureMouse triggers MouseDevice.Synchronize which sends a simulated MouseMove event. // So we have to call CaptureMouse after at the end of the initialization. // Otherwise, the MouseMove handlers will be executed in the middle of the initialization. _inkCanvas.CaptureMouse(); // The user code could change mode or call release capture when the simulated Move event is received. // So we have to check the capture still is on and the mode is correct before hooking up our handlers. // If the current down device is mouse, we should only listen to MouseMove, MouseUp and LostMouseCapture // events. if ( _inkCanvas.IsMouseCaptured && ActiveEditingMode != InkCanvasEditingMode.None ) { _inkCanvas.AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove )); _inkCanvas.AddHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture )); } else { _capturedMouse = null; } } } /// /// Clean up the capture state /// /// private void ReleaseCapture(bool releaseDevice) { Debug.Assert(IsInMidStroke || !releaseDevice, "The captured device has been release unexpectly."); if ( _capturedStylus != null ) { // The Stylus was captured. Remove the stylus listeners. _commonDescription = null; _inkCanvas.RemoveHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove)); _inkCanvas.RemoveHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture )); _capturedStylus = null; if ( releaseDevice ) { // Call ReleaseStylusCapture at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. _inkCanvas.ReleaseStylusCapture(); } } else if ( _capturedMouse != null ) { // The Mouse was captured. Remove the mouse listeners. _inkCanvas.RemoveHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove )); _inkCanvas.RemoveHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture )); _capturedMouse = null; if ( releaseDevice ) { // Call ReleaseMouseCapture at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. _inkCanvas.ReleaseMouseCapture(); } } } /// /// Retrieve whether a specified device is captured by us. /// /// ///private bool IsInputDeviceCaptured(InputDevice inputDevice) { Debug.Assert(_capturedStylus == null || _capturedMouse == null, "InkCanvas cannot capture both stylus and mouse at the same time."); return (inputDevice == _capturedStylus && ((StylusDevice)inputDevice).Captured == _inkCanvas) || (inputDevice == _capturedMouse && ( (MouseDevice)inputDevice).Captured == _inkCanvas); } /// /// Return the cursor of the active behavior /// ///internal Cursor GetActiveBehaviorCursor() { EditingBehavior behavior = ActiveEditingBehavior; if ( behavior == null ) { // Return the default Arrow cursor for the None mode. return Cursors.Arrow; } // Retrieve the cursor. Cursor cursor = behavior.Cursor; // Clean up the dirty flag when it's done. if ( !GetCursorValid(behavior) ) { SetCursorValid(behavior, true); } return cursor; } /// /// A private method which returns the valid flag of behaviors' cursor. /// /// ///private bool GetCursorValid(EditingBehavior behavior) { BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior); return GetBitFlag(flag); } /// /// A private method which sets/resets the valid flag of behaviors' cursor /// /// /// private void SetCursorValid(EditingBehavior behavior, bool isValid) { BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior); SetBitFlag(flag, isValid); } ////// A private method which returns the valid flag of behaviors' cursor. /// /// ///private bool GetTransformValid(EditingBehavior behavior) { BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior); return GetBitFlag(flag); } /// /// A private method which sets/resets the valid flag of behaviors' cursor /// /// /// private void SetTransformValid(EditingBehavior behavior, bool isValid) { BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior); SetBitFlag(flag, isValid); } ////// GetBitFlag /// /// ///private bool GetBitFlag(BehaviorValidFlag flag) { return (_behaviorValidFlag & flag) != 0; } /// /// SetBitFlag /// /// /// private void SetBitFlag(BehaviorValidFlag flag, bool value) { if ( value ) { _behaviorValidFlag |= flag; } else { _behaviorValidFlag &= (~flag); } } ////// A helper returns behavior cursor flag from a behavior instance /// /// ///private BehaviorValidFlag GetBehaviorCursorFlag(EditingBehavior behavior) { BehaviorValidFlag flag = 0; if ( behavior == InkCollectionBehavior ) { flag = BehaviorValidFlag.InkCollectionBehaviorCursorValid; } else if ( behavior == EraserBehavior ) { flag = BehaviorValidFlag.EraserBehaviorCursorValid; } else if ( behavior == LassoSelectionBehavior ) { flag = BehaviorValidFlag.LassoSelectionBehaviorCursorValid; } else if ( behavior == SelectionEditingBehavior ) { flag = BehaviorValidFlag.SelectionEditingBehaviorCursorValid; } else if ( behavior == SelectionEditor ) { flag = BehaviorValidFlag.SelectionEditorCursorValid; } else { Debug.Assert(false, "Unknown behavior"); } return flag; } /// /// A helper returns behavior transform flag from a behavior instance /// /// ///private BehaviorValidFlag GetBehaviorTransformFlag(EditingBehavior behavior) { BehaviorValidFlag flag = 0; if ( behavior == InkCollectionBehavior ) { flag = BehaviorValidFlag.InkCollectionBehaviorTransformValid; } else if ( behavior == EraserBehavior ) { flag = BehaviorValidFlag.EraserBehaviorTransformValid; } else if ( behavior == LassoSelectionBehavior ) { flag = BehaviorValidFlag.LassoSelectionBehaviorTransformValid; } else if ( behavior == SelectionEditingBehavior ) { flag = BehaviorValidFlag.SelectionEditingBehaviorTransformValid; } else if ( behavior == SelectionEditor ) { flag = BehaviorValidFlag.SelectionEditorTransformValid; } else { Debug.Assert(false, "Unknown behavior"); } return flag; } private void ChangeEditingBehavior(EditingBehavior newBehavior) { Debug.Assert(!IsInMidStroke, "ChangeEditingBehavior cannot be called in a mid-stroke"); Debug.Assert(_activationStack.Count <= 1, "The behavior stack has to contain at most one behavior when user is not editing."); try { _inkCanvas.ClearSelection(true); } finally { if ( ActiveEditingBehavior != null ) { // Pop the old behavior. PopEditingBehavior(); } // Push the new behavior if ( newBehavior != null ) { PushEditingBehavior(newBehavior); } _inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas)); } } /// /// Update the Inverted state /// /// /// ///true if the behavior is updated private bool UpdateInvertedState(StylusDevice stylusDevice, bool stylusIsInverted) { if ( !IsInMidStroke || ( IsInMidStroke && IsInputDeviceCaptured(stylusDevice) )) { if ( stylusIsInverted != _stylusIsInverted ) { _stylusIsInverted = stylusIsInverted; // // this can change editing state // UpdateActiveEditingState(); return true; } } return false; } #endregion Private Methods //------------------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------------------- #region Private Properties ////// Gets the top level active EditingBehavior /// ///private EditingBehavior ActiveEditingBehavior { get { EditingBehavior EditingBehavior = null; if ( _activationStack.Count > 0 ) { EditingBehavior = _activationStack.Peek(); } return EditingBehavior; } } /// /// Lazy init access to the InkCollectionBehavior /// ///internal InkCollectionBehavior InkCollectionBehavior { get { if ( _inkCollectionBehavior == null ) { _inkCollectionBehavior = new InkCollectionBehavior(this, _inkCanvas); } return _inkCollectionBehavior; } } /// /// Lazy init access to the EraserBehavior /// ///private EraserBehavior EraserBehavior { get { if ( _eraserBehavior == null ) { _eraserBehavior = new EraserBehavior(this, _inkCanvas); } return _eraserBehavior; } } #endregion Private Properties //------------------------------------------------------------------------------- // // Private Enum // //-------------------------------------------------------------------------------- #region Private Enum /// /// Enum values which represent the cursor valid flag for each EditingBehavior. /// [Flags] private enum BehaviorValidFlag { InkCollectionBehaviorCursorValid = 0x00000001, EraserBehaviorCursorValid = 0x00000002, LassoSelectionBehaviorCursorValid = 0x00000004, SelectionEditingBehaviorCursorValid = 0x00000008, SelectionEditorCursorValid = 0x00000010, InkCollectionBehaviorTransformValid = 0x00000020, EraserBehaviorTransformValid = 0x00000040, LassoSelectionBehaviorTransformValid = 0x00000080, SelectionEditingBehaviorTransformValid= 0x00000100, SelectionEditorTransformValid = 0x00000200, } #endregion Private Enum //------------------------------------------------------------------------------- // // Private Fields // //-------------------------------------------------------------------------------- #region Private Fields ////// The InkCanvas this EditingStack is being used in /// private InkCanvas _inkCanvas; private Stack_activationStack; private InkCollectionBehavior _inkCollectionBehavior; private EraserBehavior _eraserBehavior; private LassoSelectionBehavior _lassoSelectionBehavior; private SelectionEditingBehavior _selectionEditingBehavior; private SelectionEditor _selectionEditor; private bool _moveEnabled = true; private bool _resizeEnabled = true; private bool _userIsEditing = false; /// /// Flag that indicates if the stylus is inverted /// private bool _stylusIsInverted = false; private StylusPointDescription _commonDescription; private StylusDevice _capturedStylus; private MouseDevice _capturedMouse; // Fields related to cursor and transform. private BehaviorValidFlag _behaviorValidFlag; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // File: EditingCoordinator.cs // // Description: // Coordinates editing for InkCanvas // Features: // // History: // 4/29/2003 waynezen: Rewrote for mid-stroke support // 3/15/2003 samgeo: Created // // Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.ComponentModel; using System.ComponentModel.Design; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Security; using System.Security.Permissions; using System.Windows.Ink; using System.Windows.Input; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Interop; using System.Windows.Navigation; using System.Windows.Media; using MS.Utility; namespace MS.Internal.Ink { ////// Internal class that represents the editing stack of InkCanvas /// Please see the design detain at http://tabletpc/longhorn/Specs/Mid-Stroke%20and%20Pen%20Cursor%20Dev%20Design.mht /// internal class EditingCoordinator { //------------------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------------------- #region Constructors ////// Constructors /// /// InkCanvas instance internal EditingCoordinator(InkCanvas inkCanvas) { if (inkCanvas == null) { throw new ArgumentNullException("inkCanvas"); } _inkCanvas = inkCanvas; // Create a stack for tracking the behavior _activationStack = new Stack(2); // NTRAID#WINDOWS-1104477-2005/02/26-waynezen, // listen to in-air move so that we could get notified when Stylus is inverted. // _inkCanvas.AddHandler(Stylus.StylusInRangeEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove)); _inkCanvas.AddHandler(Stylus.StylusInAirMoveEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove)); _inkCanvas.AddHandler(Stylus.StylusOutOfRangeEvent, new StylusEventHandler(OnInkCanvasStylusOutOfRange)); } #endregion Constructors //-------------------------------------------------------------------------------- // // Internal Methods // //------------------------------------------------------------------------------- #region Internal Methods /// /// Activate a dynamic behavior /// Called from: /// SelectionEditor.OnCanvasMouseMove /// /// /// ////// Critical: Calls critical methods AddStylusPoints and InitializeCapture /// [SecurityCritical] internal void ActivateDynamicBehavior(EditingBehavior dynamicBehavior, InputDevice inputDevice) { // Only SelectionEditor should be enable to initiate dynamic behavior Debug.Assert(ActiveEditingBehavior == SelectionEditor, "Only SelectionEditor should be able to initiate dynamic behavior"); DebugCheckDynamicBehavior(dynamicBehavior); // Push the dynamic behavior. PushEditingBehavior(dynamicBehavior); // If this is LassoSelectionBehavior, we should capture the current stylus and feed the cached if ( dynamicBehavior == LassoSelectionBehavior ) { bool fSucceeded = false; // The below code might call out StrokeErasing or StrokeErased event. // The out-side code could throw exception. // We use try/finally block to protect our status. try { //we pass true for userInitiated because we've simply consulted the InputDevice //(and only StylusDevice or MouseDevice) for the current position of the device InitializeCapture(inputDevice, (IStylusEditing)dynamicBehavior, true /*userInitiated*/, false/*Don't reset the RTI*/); fSucceeded = true; } finally { if ( !fSucceeded ) { // Abort the current editing. ActiveEditingBehavior.Commit(false); // Release capture and do clean-up. ReleaseCapture(true); } } } _inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas)); } ////// Deactivate a dynamic behavior /// Called from: /// EditingBehavior.Commit /// internal void DeactivateDynamicBehavior() { // Make sure we are under correct state: // ActiveEditingBehavior has to be either LassoSelectionBehavior or SelectionEditingBehavior. DebugCheckDynamicBehavior(ActiveEditingBehavior); // Pop the dynamic behavior. PopEditingBehavior(); } ////// Update the current active EditingMode /// Called from: /// EditingCoordinator.UpdateInvertedState /// InkCanvas.Initialize() /// internal void UpdateActiveEditingState() { UpdateEditingState(_stylusIsInverted); } ////// Update the EditingMode /// Called from: /// InkCanvas.RaiseEditingModeChanged /// InkCanvas.RaiseEditingModeInvertedChanged /// /// A flage which indicates whether the new mode is for the Inverted state internal void UpdateEditingState(bool inverted) { // If the mode is inactive currently, we should skip the update. if ( inverted != _stylusIsInverted ) { return; } // Get the current behavior and the new behavior. EditingBehavior currentBehavior = ActiveEditingBehavior; EditingBehavior newBehavior = GetBehavior(ActiveEditingMode); // Check whether the user is editing. if ( UserIsEditing ) { // Check if we are in the mid-stroke. if ( IsInMidStroke ) { // We are in mid-stroke now. Debug.Assert(currentBehavior is StylusEditingBehavior, "The current behavoir has to be one of StylusEditingBehaviors"); ( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode); } else { if ( currentBehavior == SelectionEditingBehavior ) { // Commit the current moving/resizing behavior currentBehavior.Commit(true); } ChangeEditingBehavior(newBehavior); } } else { // Check if we are in the mid-stroke. if ( IsInMidStroke ) { // We are in mid-stroke now. Debug.Assert(currentBehavior is StylusEditingBehavior, "The current behavoir has to be one of StylusEditingBehaviors"); ( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode); } else { // Make sure we are under correct state: // currentBehavior cannot be any of Dynamic Behavior. DebugCheckNonDynamicBehavior(currentBehavior); ChangeEditingBehavior(newBehavior); } } _inkCanvas.UpdateCursor(); } ////// Update the PointEraerCursor /// Called from: /// InkCanvas.set_EraserShape /// internal void UpdatePointEraserCursor() { // We only have to update the point eraser when the active mode is EraseByPoint // In other case, EraseBehavior will update cursor properly in its OnActivate routine. if ( ActiveEditingMode == InkCanvasEditingMode.EraseByPoint ) { InvalidateBehaviorCursor(EraserBehavior); } } ////// InvalidateTransform /// Called by: InkCanvas.OnPropertyChanged /// internal void InvalidateTransform() { // We only have to invalidate transform flags for InkCollectionBehavior and EraserBehavior for now. SetTransformValid(InkCollectionBehavior, false); SetTransformValid(EraserBehavior, false); } ////// Invalidate the behavior cursor /// Call from: /// EditingCoordinator.UpdatePointEraserCursor /// EraserBehavior.OnActivate /// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesReplaced /// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesChanged /// SelectionEditingBehavior.OnActivate /// SelectionEditor.UpdateCursor(InkCanvasSelectionHandle handle) /// /// the behavior which its cursor needs to be updated. internal void InvalidateBehaviorCursor(EditingBehavior behavior) { // Should never be null Debug.Assert(behavior != null); // InvalidateCursor first SetCursorValid(behavior, false); if ( !GetTransformValid(behavior) ) { behavior.UpdateTransform(); SetTransformValid(behavior, true); } // If the behavior is active, we have to update cursor right now. if ( behavior == ActiveEditingBehavior ) { _inkCanvas.UpdateCursor(); } } ////// Check whether the cursor of the specified behavior is valid /// /// EditingBehavior ///True if the cursor doesn't need to be updated internal bool IsCursorValid(EditingBehavior behavior) { return GetCursorValid(behavior); } ////// Check whether the cursor of the specified behavior is valid /// /// EditingBehavior ///True if the cursor doesn't need to be updated internal bool IsTransformValid(EditingBehavior behavior) { return GetTransformValid(behavior); } ////// Change to another StylusEditing mode or None mode. /// /// /// ///internal IStylusEditing ChangeStylusEditingMode(StylusEditingBehavior sourceBehavior, InkCanvasEditingMode newMode) { // NOTICE-2006/04/27-WAYNEZEN, // Before the caller invokes the method, the external event could have been fired. // The user code may interrupt the Mid-Stroke by releasing capture or switching to another mode. // So we should check if we still is under mid-stroke and the source behavior still is active. // If not, we just no-op. if ( IsInMidStroke && ( ( // We expect that either InkCollectionBehavior or EraseBehavior is active. sourceBehavior != LassoSelectionBehavior && sourceBehavior == ActiveEditingBehavior ) // Or We expect SelectionEditor is active here since // LassoSelectionBehavior will be deactivated once it gets committed. || ( sourceBehavior == LassoSelectionBehavior && SelectionEditor == ActiveEditingBehavior ) ) ) { Debug.Assert(_activationStack.Count == 1, "The behavior stack has to contain one behavior."); // Pop up the old behavior PopEditingBehavior(); // Get the new behavior EditingBehavior newBehavior = GetBehavior(ActiveEditingMode); if ( newBehavior != null ) { // Push the new behavior PushEditingBehavior(newBehavior); // If the new mode is Select, we should push the LassoSelectionBehavior now. if ( newMode == InkCanvasEditingMode.Select // NOTICE-2006/04/27-WAYNEZEN, // Make sure the newBehavior is SelectionEditor since it could be changed by the user event handling code. && newBehavior == SelectionEditor ) { PushEditingBehavior(LassoSelectionBehavior); } } else { // None-mode now. We stop collecting the packets. ReleaseCapture(true); } _inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas)); return ActiveEditingBehavior as IStylusEditing; } else { // No-op return null; } } /// /// Debug Checker /// /// [Conditional("DEBUG")] internal void DebugCheckActiveBehavior(EditingBehavior behavior) { Debug.Assert(behavior == ActiveEditingBehavior); } ////// Debug check for the dynamic behavior /// /// [Conditional("DEBUG")] private void DebugCheckDynamicBehavior(EditingBehavior behavior) { Debug.Assert(behavior == LassoSelectionBehavior || behavior == SelectionEditingBehavior, "Only LassoSelectionBehavior or SelectionEditingBehavior is dynamic behavior"); } ////// Debug check for the non dynamic behavior /// /// [Conditional("DEBUG")] private void DebugCheckNonDynamicBehavior(EditingBehavior behavior) { Debug.Assert(behavior != LassoSelectionBehavior && behavior != SelectionEditingBehavior, "behavior cannot be LassoSelectionBehavior or SelectionEditingBehavior"); } #endregion Internal Methods //-------------------------------------------------------------------------------- // // Internal Properties // //-------------------------------------------------------------------------------- #region Internal Properties ////// Gets / sets whether move is enabled or note /// internal bool MoveEnabled { get { return _moveEnabled; } set { _moveEnabled = value; } } ////// The property that indicates if the user is interacting with the current InkCanvas /// internal bool UserIsEditing { get { return _userIsEditing; } set { _userIsEditing = value; } } ////// Helper flag that tells if we're between a preview down and an up. /// internal bool StylusOrMouseIsDown { get { bool stylusDown = false; StylusDevice stylusDevice = Stylus.CurrentStylusDevice; if (stylusDevice != null && _inkCanvas.IsStylusOver && !stylusDevice.InAir) { stylusDown = true; } bool mouseDown = (_inkCanvas.IsMouseOver && Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed); if (stylusDown || mouseDown) { return true; } return false; } } ////// Returns the StylusDevice to call DynamicRenderer.Reset with. Stylus or Mouse /// must be in a down state. If the down device is a Mouse, null is returned, since /// that is what DynamicRenderer.Reset expects for the Mouse. /// ///internal InputDevice GetInputDeviceForReset() { Debug.Assert((_capturedStylus != null && _capturedMouse == null) || (_capturedStylus == null && _capturedMouse != null), "There must be one and at most one device being captured."); if ( _capturedStylus != null && !_capturedStylus.InAir ) { return _capturedStylus; } else if ( _capturedMouse != null && _capturedMouse.LeftButton == MouseButtonState.Pressed ) { return _capturedMouse; } return null; } /// /// Gets / sets whether resize is enabled or note /// internal bool ResizeEnabled { get { return _resizeEnabled; } set { _resizeEnabled = value; } } ////// Lazy init access to the LassoSelectionBehavior /// ///internal LassoSelectionBehavior LassoSelectionBehavior { get { if ( _lassoSelectionBehavior == null ) { _lassoSelectionBehavior = new LassoSelectionBehavior(this, _inkCanvas); } return _lassoSelectionBehavior; } } /// /// Lazy init access to the SelectionEditingBehavior /// ///internal SelectionEditingBehavior SelectionEditingBehavior { get { if ( _selectionEditingBehavior == null ) { _selectionEditingBehavior = new SelectionEditingBehavior(this, _inkCanvas); } return _selectionEditingBehavior; } } /// /// Internal helper prop to indicate the active editing mode /// internal InkCanvasEditingMode ActiveEditingMode { get { if ( _stylusIsInverted ) { return _inkCanvas.EditingModeInverted; } return _inkCanvas.EditingMode; } } ////// Lazy init access to the SelectionEditor /// ///internal SelectionEditor SelectionEditor { get { if ( _selectionEditor == null ) { _selectionEditor = new SelectionEditor(this, _inkCanvas); } return _selectionEditor; } } /// /// Gets the mid-stroke state /// internal bool IsInMidStroke { get { return _capturedStylus != null || _capturedMouse != null; } } ////// Returns Stylus Inverted state /// internal bool IsStylusInverted { get { return _stylusIsInverted; } } #endregion Internal Properties //------------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------------- #region Private Methods ////// Retrieve the behavior instance based on the EditingMode /// /// EditingMode ///private EditingBehavior GetBehavior(InkCanvasEditingMode editingMode) { EditingBehavior newBehavior; switch ( editingMode ) { case InkCanvasEditingMode.Ink: case InkCanvasEditingMode.GestureOnly: case InkCanvasEditingMode.InkAndGesture: newBehavior = InkCollectionBehavior; break; case InkCanvasEditingMode.Select: newBehavior = SelectionEditor; break; case InkCanvasEditingMode.EraseByPoint: case InkCanvasEditingMode.EraseByStroke: newBehavior = EraserBehavior; break; default: // Treat as InkCanvasEditingMode.None. Return null value newBehavior = null; break; } return newBehavior; } /// /// Pushes an EditingBehavior onto our stack, disables any current ones /// /// The EditingBehavior to activate private void PushEditingBehavior(EditingBehavior newEditingBehavior) { Debug.Assert(newEditingBehavior != null); EditingBehavior behavior = ActiveEditingBehavior; // Deactivate the previous behavior if ( behavior != null ) { behavior.Deactivate(); } // Activate the new behavior. _activationStack.Push(newEditingBehavior); newEditingBehavior.Activate(); } ////// Pops an EditingBehavior onto our stack, disables any current ones /// private void PopEditingBehavior() { EditingBehavior behavior = ActiveEditingBehavior; if ( behavior != null ) { behavior.Deactivate(); _activationStack.Pop(); behavior = ActiveEditingBehavior; if ( ActiveEditingBehavior != null ) { behavior.Activate(); } } return; } ////// Handles in-air stylus movements /// private void OnInkCanvasStylusInAirOrInRangeMove(object sender, StylusEventArgs args) { // If the current capture is mouse, we should reset the capture. if ( _capturedMouse != null ) { if (ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null) { // NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN // When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink. // By flipping the Enabled property, the RTI will be reset. _inkCanvas.InternalDynamicRenderer.Enabled = false; _inkCanvas.InternalDynamicRenderer.Enabled = true; } // Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. ActiveEditingBehavior.Commit(true); // Release capture and do clean-up. ReleaseCapture(true); } UpdateInvertedState(args.StylusDevice, args.Inverted); } ////// Handle StylusOutofRange event /// /// /// private void OnInkCanvasStylusOutOfRange(object sender, StylusEventArgs args) { // Reset the inverted state once OutOfRange has been received. UpdateInvertedState(args.StylusDevice, false); } ////// InkCanvas.StylusDown handler /// /// /// ////// Critical: /// Calls SecurityCritcal method: InitializeCapture and AddStylusPoints /// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected /// /// TreatAsSafe: This method is called by the input system, from security transparent /// code, so it can not be marked critical. We check the eventArgs.UserInitiated /// to verify that the input was user initiated and pass this flag to /// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should /// perform gesture recognition /// [SecurityCritical, SecurityTreatAsSafe] internal void OnInkCanvasDeviceDown(object sender, InputEventArgs args) { MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs; bool resetDynamicRenderer = false; if ( mouseButtonEventArgs != null ) { // NTRADI:WINDOWSOS#1563896-2006/03/20-WAYNEZEN, // Note we don't mark Handled for the None EditingMode. // Set focus to InkCanvas. if ( _inkCanvas.Focus() && ActiveEditingMode != InkCanvasEditingMode.None ) { mouseButtonEventArgs.Handled = true; } // ISSUE-2005/09/20-WAYNEZEN, // We will reevaluate whether we should allow the right-button down for the modes other than the Ink mode. // Skip collecting for the non-left buttons. if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left ) { return; } // NTRAID-WINDOWS#1607286-2006/04/14-WAYNEZEN, // If the mouse down event is from a Stylus, make sure we have a correct inverted state. if ( mouseButtonEventArgs.StylusDevice != null ) { UpdateInvertedState(mouseButtonEventArgs.StylusDevice, mouseButtonEventArgs.StylusDevice.Inverted); } } else { // NTRAID-WINDOWS#1395155-2005/12/15-WAYNEZEN, // When StylusDown is received, We should check the Invert state again. // If there is any change, the ActiveEditingMode will be updated. // The dynamic renderer will be reset in InkCollectionBehavior.OnActivated since the device is under down state. StylusEventArgs stylusEventArgs = args as StylusEventArgs; UpdateInvertedState(stylusEventArgs.StylusDevice, stylusEventArgs.Inverted); } // If the active behavior is not one of StylusEditingBehavior, don't even bother here. IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing; // We might receive StylusDown from a different device meanwhile we have already captured one. // Make sure that we are starting from a fresh state. if ( !IsInMidStroke && stylusEditingBehavior != null ) { bool fSucceeded = false; try { InputDevice capturedDevice = null; // Capture the stylus (if mouse event make sure to use stylus if generated by a stylus) if ( mouseButtonEventArgs != null && mouseButtonEventArgs.StylusDevice != null ) { capturedDevice = mouseButtonEventArgs.StylusDevice; resetDynamicRenderer = true; } else { capturedDevice = args.Device; } InitializeCapture(capturedDevice, stylusEditingBehavior, args.UserInitiated, resetDynamicRenderer); // The below code might call out StrokeErasing or StrokeErased event. // The out-side code could throw exception. // We use try/finally block to protect our status. fSucceeded = true; } finally { if ( !fSucceeded ) { // Abort the current editing. ActiveEditingBehavior.Commit(false); // Release capture and do clean-up. ReleaseCapture(IsInMidStroke); } } } } ////// InkCanvas.StylusMove handler /// /// /// ////// Critical: /// Calls SecurityCritcal method: /// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected /// /// TreatAsSafe: This method is called by the input system, from security transparent /// code, so it can not be marked critical. We check the eventArgs.UserInitiated /// to verify that the input was user initiated and pass this flag to /// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should /// perform gesture recognition /// [SecurityCritical, SecurityTreatAsSafe] private void OnInkCanvasDeviceMove(object sender, TEventArgs args) where TEventArgs : InputEventArgs { // Make sure that the stylus is the one we captured previously. if ( IsInputDeviceCaptured(args.Device) ) { IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing; Debug.Assert(stylusEditingBehavior != null || ActiveEditingBehavior == null, "The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing."); if ( stylusEditingBehavior != null ) { StylusPointCollection stylusPoints; if ( _capturedStylus != null ) { stylusPoints = _capturedStylus.GetStylusPoints(_inkCanvas, _commonDescription); } else { // Make sure we ignore stylus generated mouse events. MouseEventArgs mouseEventArgs = args as MouseEventArgs; if ( mouseEventArgs != null && mouseEventArgs.StylusDevice != null ) { return; } stylusPoints = new StylusPointCollection(new Point[] { _capturedMouse.GetPosition(_inkCanvas) }); } bool fSucceeded = false; // The below code might call out StrokeErasing or StrokeErased event. // The out-side code could throw exception. // We use try/finally block to protect our status. try { stylusEditingBehavior.AddStylusPoints(stylusPoints, args.UserInitiated); fSucceeded = true; } finally { if ( !fSucceeded ) { // Abort the current editing. ActiveEditingBehavior.Commit(false); // Release capture and do clean-up. ReleaseCapture(true); } } } } } /// /// InkCanvas.StylusUp handler /// /// /// internal void OnInkCanvasDeviceUp(object sender, InputEventArgs args) { // Make sure that the stylus is the one we captured previously. if ( IsInputDeviceCaptured(args.Device) ) { Debug.Assert(ActiveEditingBehavior == null || ActiveEditingBehavior is IStylusEditing, "The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing."); // Make sure we only look at mouse left button events if watching mouse events. if ( _capturedMouse != null ) { MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs; if ( mouseButtonEventArgs != null ) { if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left ) { return; } } } try { // The follow code raises variety editing events. // The out-side code could throw exception in the their handlers. We use try/finally block to protect our status. if ( ActiveEditingBehavior != null ) { // Commit the current editing. ActiveEditingBehavior.Commit(true); } } finally { // Call ReleaseCapture(true) at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. ReleaseCapture(true); } } } ////// InkCanvas.LostStylusCapture handler /// /// /// private void OnInkCanvasLostDeviceCapture(object sender, TEventArgs args) where TEventArgs : InputEventArgs { // If user is editing, we have to commit the current operation and reset our state. if ( UserIsEditing ) { // Note ReleaseCapture(false) won't raise any external events. It only reset the internal states. ReleaseCapture(false); if ( ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null ) { // NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN // When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink. // By flipping the Enabled property, the RTI will be reset. _inkCanvas.InternalDynamicRenderer.Enabled = false; _inkCanvas.InternalDynamicRenderer.Enabled = true; } // Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. ActiveEditingBehavior.Commit(true); } } /// /// Initialize the capture state /// /// /// /// /// ////// Critical: Returns critical data from the inputDevice, StylusPointCollection. /// [SecurityCritical] private void InitializeCapture(InputDevice inputDevice, IStylusEditing stylusEditingBehavior, bool userInitiated, bool resetDynamicRenderer) { Debug.Assert(inputDevice != null, "A null device is passed in."); Debug.Assert(stylusEditingBehavior != null, "stylusEditingBehavior cannot be null."); Debug.Assert(!IsInMidStroke, "The previous device hasn't been released yet."); StylusPointCollection stylusPoints; _capturedStylus = inputDevice as StylusDevice; _capturedMouse = inputDevice as MouseDevice; // NOTICE-2005/09/15-WAYNEZEN, // We assume that the StylusDown always happens before the MouseDown. So, we could safely add the listeners of // XXXMove and XXXUp as the below which branchs out the coming mouse or stylus events. if ( _capturedStylus != null ) { StylusPointCollection newPoints = _capturedStylus.GetStylusPoints(_inkCanvas); _commonDescription = StylusPointDescription.GetCommonDescription(_inkCanvas.DefaultStylusPointDescription, newPoints.Description); stylusPoints = newPoints.Reformat(_commonDescription); if ( resetDynamicRenderer ) { // Reset the dynamic renderer for InkCollectionBehavior InkCollectionBehavior inkCollectionBehavior = stylusEditingBehavior as InkCollectionBehavior; if ( inkCollectionBehavior != null ) { inkCollectionBehavior.ResetDynamicRenderer(); } } stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated); // If the current down device is stylus, we should only listen to StylusMove, StylusUp and LostStylusCapture // events. _inkCanvas.CaptureStylus(); if ( _inkCanvas.IsStylusCaptured && ActiveEditingMode != InkCanvasEditingMode.None ) { _inkCanvas.AddHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove)); _inkCanvas.AddHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture )); } else { _capturedStylus = null; } } else { Debug.Assert(!resetDynamicRenderer, "The dynamic renderer shouldn't be reset for Mouse"); _commonDescription = null; Point[] points = new Point[] { _capturedMouse.GetPosition(_inkCanvas) }; stylusPoints = new StylusPointCollection(points); stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated); // NTRAID:WINDOWSOS#1536974-2006/03/02-WAYNEZEN, // CaptureMouse triggers MouseDevice.Synchronize which sends a simulated MouseMove event. // So we have to call CaptureMouse after at the end of the initialization. // Otherwise, the MouseMove handlers will be executed in the middle of the initialization. _inkCanvas.CaptureMouse(); // The user code could change mode or call release capture when the simulated Move event is received. // So we have to check the capture still is on and the mode is correct before hooking up our handlers. // If the current down device is mouse, we should only listen to MouseMove, MouseUp and LostMouseCapture // events. if ( _inkCanvas.IsMouseCaptured && ActiveEditingMode != InkCanvasEditingMode.None ) { _inkCanvas.AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove )); _inkCanvas.AddHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture )); } else { _capturedMouse = null; } } } /// /// Clean up the capture state /// /// private void ReleaseCapture(bool releaseDevice) { Debug.Assert(IsInMidStroke || !releaseDevice, "The captured device has been release unexpectly."); if ( _capturedStylus != null ) { // The Stylus was captured. Remove the stylus listeners. _commonDescription = null; _inkCanvas.RemoveHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove)); _inkCanvas.RemoveHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture )); _capturedStylus = null; if ( releaseDevice ) { // Call ReleaseStylusCapture at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. _inkCanvas.ReleaseStylusCapture(); } } else if ( _capturedMouse != null ) { // The Mouse was captured. Remove the mouse listeners. _inkCanvas.RemoveHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove )); _inkCanvas.RemoveHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture )); _capturedMouse = null; if ( releaseDevice ) { // Call ReleaseMouseCapture at the end of the routine. The method will cause an external event fired. // So it should be invoked after we set up our states. _inkCanvas.ReleaseMouseCapture(); } } } /// /// Retrieve whether a specified device is captured by us. /// /// ///private bool IsInputDeviceCaptured(InputDevice inputDevice) { Debug.Assert(_capturedStylus == null || _capturedMouse == null, "InkCanvas cannot capture both stylus and mouse at the same time."); return (inputDevice == _capturedStylus && ((StylusDevice)inputDevice).Captured == _inkCanvas) || (inputDevice == _capturedMouse && ( (MouseDevice)inputDevice).Captured == _inkCanvas); } /// /// Return the cursor of the active behavior /// ///internal Cursor GetActiveBehaviorCursor() { EditingBehavior behavior = ActiveEditingBehavior; if ( behavior == null ) { // Return the default Arrow cursor for the None mode. return Cursors.Arrow; } // Retrieve the cursor. Cursor cursor = behavior.Cursor; // Clean up the dirty flag when it's done. if ( !GetCursorValid(behavior) ) { SetCursorValid(behavior, true); } return cursor; } /// /// A private method which returns the valid flag of behaviors' cursor. /// /// ///private bool GetCursorValid(EditingBehavior behavior) { BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior); return GetBitFlag(flag); } /// /// A private method which sets/resets the valid flag of behaviors' cursor /// /// /// private void SetCursorValid(EditingBehavior behavior, bool isValid) { BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior); SetBitFlag(flag, isValid); } ////// A private method which returns the valid flag of behaviors' cursor. /// /// ///private bool GetTransformValid(EditingBehavior behavior) { BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior); return GetBitFlag(flag); } /// /// A private method which sets/resets the valid flag of behaviors' cursor /// /// /// private void SetTransformValid(EditingBehavior behavior, bool isValid) { BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior); SetBitFlag(flag, isValid); } ////// GetBitFlag /// /// ///private bool GetBitFlag(BehaviorValidFlag flag) { return (_behaviorValidFlag & flag) != 0; } /// /// SetBitFlag /// /// /// private void SetBitFlag(BehaviorValidFlag flag, bool value) { if ( value ) { _behaviorValidFlag |= flag; } else { _behaviorValidFlag &= (~flag); } } ////// A helper returns behavior cursor flag from a behavior instance /// /// ///private BehaviorValidFlag GetBehaviorCursorFlag(EditingBehavior behavior) { BehaviorValidFlag flag = 0; if ( behavior == InkCollectionBehavior ) { flag = BehaviorValidFlag.InkCollectionBehaviorCursorValid; } else if ( behavior == EraserBehavior ) { flag = BehaviorValidFlag.EraserBehaviorCursorValid; } else if ( behavior == LassoSelectionBehavior ) { flag = BehaviorValidFlag.LassoSelectionBehaviorCursorValid; } else if ( behavior == SelectionEditingBehavior ) { flag = BehaviorValidFlag.SelectionEditingBehaviorCursorValid; } else if ( behavior == SelectionEditor ) { flag = BehaviorValidFlag.SelectionEditorCursorValid; } else { Debug.Assert(false, "Unknown behavior"); } return flag; } /// /// A helper returns behavior transform flag from a behavior instance /// /// ///private BehaviorValidFlag GetBehaviorTransformFlag(EditingBehavior behavior) { BehaviorValidFlag flag = 0; if ( behavior == InkCollectionBehavior ) { flag = BehaviorValidFlag.InkCollectionBehaviorTransformValid; } else if ( behavior == EraserBehavior ) { flag = BehaviorValidFlag.EraserBehaviorTransformValid; } else if ( behavior == LassoSelectionBehavior ) { flag = BehaviorValidFlag.LassoSelectionBehaviorTransformValid; } else if ( behavior == SelectionEditingBehavior ) { flag = BehaviorValidFlag.SelectionEditingBehaviorTransformValid; } else if ( behavior == SelectionEditor ) { flag = BehaviorValidFlag.SelectionEditorTransformValid; } else { Debug.Assert(false, "Unknown behavior"); } return flag; } private void ChangeEditingBehavior(EditingBehavior newBehavior) { Debug.Assert(!IsInMidStroke, "ChangeEditingBehavior cannot be called in a mid-stroke"); Debug.Assert(_activationStack.Count <= 1, "The behavior stack has to contain at most one behavior when user is not editing."); try { _inkCanvas.ClearSelection(true); } finally { if ( ActiveEditingBehavior != null ) { // Pop the old behavior. PopEditingBehavior(); } // Push the new behavior if ( newBehavior != null ) { PushEditingBehavior(newBehavior); } _inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas)); } } /// /// Update the Inverted state /// /// /// ///true if the behavior is updated private bool UpdateInvertedState(StylusDevice stylusDevice, bool stylusIsInverted) { if ( !IsInMidStroke || ( IsInMidStroke && IsInputDeviceCaptured(stylusDevice) )) { if ( stylusIsInverted != _stylusIsInverted ) { _stylusIsInverted = stylusIsInverted; // // this can change editing state // UpdateActiveEditingState(); return true; } } return false; } #endregion Private Methods //------------------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------------------- #region Private Properties ////// Gets the top level active EditingBehavior /// ///private EditingBehavior ActiveEditingBehavior { get { EditingBehavior EditingBehavior = null; if ( _activationStack.Count > 0 ) { EditingBehavior = _activationStack.Peek(); } return EditingBehavior; } } /// /// Lazy init access to the InkCollectionBehavior /// ///internal InkCollectionBehavior InkCollectionBehavior { get { if ( _inkCollectionBehavior == null ) { _inkCollectionBehavior = new InkCollectionBehavior(this, _inkCanvas); } return _inkCollectionBehavior; } } /// /// Lazy init access to the EraserBehavior /// ///private EraserBehavior EraserBehavior { get { if ( _eraserBehavior == null ) { _eraserBehavior = new EraserBehavior(this, _inkCanvas); } return _eraserBehavior; } } #endregion Private Properties //------------------------------------------------------------------------------- // // Private Enum // //-------------------------------------------------------------------------------- #region Private Enum /// /// Enum values which represent the cursor valid flag for each EditingBehavior. /// [Flags] private enum BehaviorValidFlag { InkCollectionBehaviorCursorValid = 0x00000001, EraserBehaviorCursorValid = 0x00000002, LassoSelectionBehaviorCursorValid = 0x00000004, SelectionEditingBehaviorCursorValid = 0x00000008, SelectionEditorCursorValid = 0x00000010, InkCollectionBehaviorTransformValid = 0x00000020, EraserBehaviorTransformValid = 0x00000040, LassoSelectionBehaviorTransformValid = 0x00000080, SelectionEditingBehaviorTransformValid= 0x00000100, SelectionEditorTransformValid = 0x00000200, } #endregion Private Enum //------------------------------------------------------------------------------- // // Private Fields // //-------------------------------------------------------------------------------- #region Private Fields ////// The InkCanvas this EditingStack is being used in /// private InkCanvas _inkCanvas; private Stack_activationStack; private InkCollectionBehavior _inkCollectionBehavior; private EraserBehavior _eraserBehavior; private LassoSelectionBehavior _lassoSelectionBehavior; private SelectionEditingBehavior _selectionEditingBehavior; private SelectionEditor _selectionEditor; private bool _moveEnabled = true; private bool _resizeEnabled = true; private bool _userIsEditing = false; /// /// Flag that indicates if the stylus is inverted /// private bool _stylusIsInverted = false; private StylusPointDescription _commonDescription; private StylusDevice _capturedStylus; private MouseDevice _capturedMouse; // Fields related to cursor and transform. private BehaviorValidFlag _behaviorValidFlag; #endregion Private Fields } } // 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
- HierarchicalDataBoundControlAdapter.cs
- ScrollEventArgs.cs
- ProxyAttribute.cs
- AnnotationResource.cs
- SQLInt64Storage.cs
- PresentationTraceSources.cs
- AttributeCollection.cs
- MLangCodePageEncoding.cs
- _SingleItemRequestCache.cs
- AuthenticationService.cs
- BookmarkInfo.cs
- DbParameterCollectionHelper.cs
- PolicyLevel.cs
- ToolStrip.cs
- ZipPackage.cs
- QueryContinueDragEvent.cs
- MediaTimeline.cs
- COMException.cs
- ObjectViewFactory.cs
- SupportsPreviewControlAttribute.cs
- ObjectQueryProvider.cs
- basevalidator.cs
- OpCopier.cs
- TreeNode.cs
- GridViewRowEventArgs.cs
- XmlObjectSerializerReadContext.cs
- DisplayInformation.cs
- LinkClickEvent.cs
- ParallelEnumerable.cs
- MessageHeader.cs
- MimePart.cs
- Triplet.cs
- TemplatedEditableDesignerRegion.cs
- WeakReadOnlyCollection.cs
- Animatable.cs
- ClientRoleProvider.cs
- Region.cs
- State.cs
- UnsafeNativeMethods.cs
- ConfigurationValues.cs
- ExpressionDumper.cs
- ClientRolePrincipal.cs
- ToggleButton.cs
- Vector3DValueSerializer.cs
- SapiRecognizer.cs
- SimpleTextLine.cs
- LeftCellWrapper.cs
- ParameterBinding.cs
- XmlDictionaryReader.cs
- IndexOutOfRangeException.cs
- InvalidDataException.cs
- WebPartVerbCollection.cs
- OdbcParameterCollection.cs
- RawAppCommandInputReport.cs
- DataListItemCollection.cs
- MustUnderstandBehavior.cs
- Scene3D.cs
- DataGridRowHeader.cs
- FixedSOMElement.cs
- XmlSchemaType.cs
- XmlArrayItemAttributes.cs
- HTMLTagNameToTypeMapper.cs
- HScrollBar.cs
- RecordsAffectedEventArgs.cs
- HScrollProperties.cs
- WebPartConnectionsConfigureVerb.cs
- ControlBindingsCollection.cs
- SharedConnectionInfo.cs
- OdbcParameterCollection.cs
- WorkflowInstanceContextProvider.cs
- SqlTriggerAttribute.cs
- EventLogPermissionAttribute.cs
- NullableFloatAverageAggregationOperator.cs
- BinHexEncoder.cs
- SHA256Managed.cs
- DynamicPropertyHolder.cs
- StackBuilderSink.cs
- ProviderCommandInfoUtils.cs
- DataGrid.cs
- Duration.cs
- MenuCommandService.cs
- MemberDescriptor.cs
- SpoolingTask.cs
- QueuedDeliveryRequirementsMode.cs
- SQLInt64.cs
- ConfigXmlSignificantWhitespace.cs
- WebPartConnectionsDisconnectVerb.cs
- ComponentConverter.cs
- CompilationUtil.cs
- ProfileModule.cs
- BasicBrowserDialog.designer.cs
- Trace.cs
- LayoutSettings.cs
- ResXFileRef.cs
- RemotingServices.cs
- ServiceOperationParameter.cs
- LoadRetryStrategyFactory.cs
- OneToOneMappingSerializer.cs
- RC2.cs
- SpeakInfo.cs