public void Event_Should_Be_Routed_To_Device_Target() { InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.ProcessInput(e); Assert.IsTrue(element.RawEventRaised); }
public void Event_Should_Not_Be_Routed_If_Cancelled() { PreProcessInputEventHandler preProcess = (sender, ev) => { ev.Cancel(); }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(element.RawEventRaised); }
public void PreNotifyInput_Should_Be_Called() { bool notified = false; NotifyInputEventHandler preNotify = (sender, ev) => { notified = true; }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreNotifyInput += preNotify; InputManager.Current.ProcessInput(e); InputManager.Current.PreNotifyInput -= preNotify; Assert.IsTrue(notified); }
private void ChangeFocus(DependencyObject focus, int timestamp) { DependencyObject o = null; if (focus != _focus) { // Update the critical pieces of data. DependencyObject oldFocus = _focus; _focus = focus; _focusRootVisual = InputElement.GetRootVisual(focus); using (Dispatcher.DisableProcessing()) // Disable reentrancy due to locks taken { // Adjust the handlers we use to track everything. if (oldFocus != null) { o = oldFocus; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged -= _isEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged -= _isVisibleChangedEventHandler; ((UIElement)o).FocusableChanged -= _focusableChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged -= _isEnabledChangedEventHandler; // NOTE: there is no IsVisible property for ContentElements. ((ContentElement)o).FocusableChanged -= _focusableChangedEventHandler; } else { ((UIElement3D)o).IsEnabledChanged -= _isEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged -= _isVisibleChangedEventHandler; ((UIElement3D)o).FocusableChanged -= _focusableChangedEventHandler; } } if (_focus != null) { o = _focus; if (InputElement.IsUIElement(o)) { ((UIElement)o).IsEnabledChanged += _isEnabledChangedEventHandler; ((UIElement)o).IsVisibleChanged += _isVisibleChangedEventHandler; ((UIElement)o).FocusableChanged += _focusableChangedEventHandler; } else if (InputElement.IsContentElement(o)) { ((ContentElement)o).IsEnabledChanged += _isEnabledChangedEventHandler; // NOTE: there is no IsVisible property for ContentElements. ((ContentElement)o).FocusableChanged += _focusableChangedEventHandler; } else { ((UIElement3D)o).IsEnabledChanged += _isEnabledChangedEventHandler; ((UIElement3D)o).IsVisibleChanged += _isVisibleChangedEventHandler; ((UIElement3D)o).FocusableChanged += _focusableChangedEventHandler; } } } // Oddly enough, update the FocusWithinProperty properties first. This is // so any callbacks will see the more-common FocusWithinProperty properties // set correctly. UIElement.FocusWithinProperty.OnOriginValueChanged(oldFocus, _focus, ref _focusTreeState); // Invalidate the IsKeyboardFocused properties. if (oldFocus != null) { o = oldFocus; o.SetValue(UIElement.IsKeyboardFocusedPropertyKey, false); // Same property for ContentElements } if (_focus != null) { // Invalidate the IsKeyboardFocused property. o = _focus; o.SetValue(UIElement.IsKeyboardFocusedPropertyKey, true); // Same property for ContentElements } // Call TestServicesManager change the focus of the InputMethod is enable/disabled accordingly // so it's ready befere the GotKeyboardFocusEvent handler is invoked. if (_TsfManager != null) { _TsfManager.Value.Focus(_focus); } // InputLanguageManager checks the preferred input languages. // This should before GotEvent because the preferred input language // should be set at the event handler. InputLanguageManager.Current.Focus(_focus, oldFocus); // Send the LostKeyboardFocus and GotKeyboardFocus events. if (oldFocus != null) { KeyboardFocusChangedEventArgs lostFocus = new KeyboardFocusChangedEventArgs(this, timestamp, (IInputElement)oldFocus, (IInputElement)focus); lostFocus.RoutedEvent = Keyboard.LostKeyboardFocusEvent; lostFocus.Source = oldFocus; if (_inputManager != null) { _inputManager.Value.ProcessInput(lostFocus); } } if (_focus != null) { KeyboardFocusChangedEventArgs gotFocus = new KeyboardFocusChangedEventArgs(this, timestamp, (IInputElement)oldFocus, (IInputElement)_focus); gotFocus.RoutedEvent = Keyboard.GotKeyboardFocusEvent; gotFocus.Source = _focus; if (_inputManager != null) { _inputManager.Value.ProcessInput(gotFocus); } } // InputMethod checks the preferred ime state. // The preferred input methods should be applied after Cicero TIP gots SetFocus callback. InputMethod.Current.GotKeyboardFocus(_focus); //Could be also built-in into IsKeyboardFocused_Changed static on UIElement and ContentElement //However the Automation likes to go immediately back on us so it would be better be last one... AutomationPeer.RaiseFocusChangedEventHelper((IInputElement)_focus); } }
public void ProcessInput_Should_Return_True_If_One_Event_Handled() { PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); } }; InputElement element = new InputElement(); element.SetMutatedEventHandled = true; TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; bool result = InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; // This goes against the documentation which says should return true if all events handled. Assert.IsTrue(element.RawEventRaised); Assert.IsTrue(element.MutatedEventRaised); Assert.IsTrue(result); }
public void Mutated_Event_Should_Be_Raised_With_Nested_ProcessInput() { bool calledWithMutated = false; PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; InputManager.Current.ProcessInput(mutated); } else if (ev.StagingItem.Input.RoutedEvent == InputElement.MutatedEvent) { calledWithMutated = true; } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsTrue(element.RawEventRaised); Assert.IsTrue(element.MutatedEventRaised); Assert.IsTrue(calledWithMutated); }
public void PostProcess_Event_Should_Be_Called_With_Mutated_Event() { bool calledWithMutated = false; PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); ev.Cancel(); } }; ProcessInputEventHandler postProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.MutatedEvent) { calledWithMutated = true; } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.PostProcessInput += postProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PostProcessInput += postProcess; InputManager.Current.PreProcessInput -= preProcess; Assert.IsTrue(calledWithMutated); }
public void ProcessInput_Should_Return_False_If_No_Events_Handled() { PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; bool result = InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(result); }
public void Mutated_Event_Should_Still_Be_Raised_When_Raw_Event_Cancelled() { PreProcessInputEventHandler preProcess = (sender, ev) => { if (ev.StagingItem.Input.RoutedEvent == InputElement.RawEvent) { TestEventArgs mutated = new TestEventArgs(ev.StagingItem.Input.Device.Target); mutated.RoutedEvent = InputElement.MutatedEvent; StagingAreaInputItem stagingItem = CreateStagingItem(mutated); ev.PushInput(stagingItem); ev.Cancel(); } }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(element.RawEventRaised); Assert.IsTrue(element.MutatedEventRaised); }
/// <summary> /// Retrieves the history of intermediate Points up to 64 previous coordinates of the mouse or pen. /// </summary> /// <param name="relativeTo"> /// The element relative which the points need to be returned. /// </param> /// <param name="points"> /// Points relative to the first parameter are returned. /// </param> public static int GetIntermediatePoints(IInputElement relativeTo, Point[] points) { // Security Mitigation: do not give out input state if the device is not active. if (Mouse.PrimaryDevice.IsActive) { if (relativeTo != null) { PresentationSource inputSource = PresentationSource.FromDependencyObject(InputElement.GetContainingVisual(relativeTo as DependencyObject)); if (inputSource != null) { IMouseInputProvider mouseInputProvider = inputSource.GetInputProvider(typeof(MouseDevice)) as IMouseInputProvider; if (null != mouseInputProvider) { return(mouseInputProvider.GetIntermediatePoints(relativeTo, points)); } } } } return(-1); }
/// <summary> /// Determines if we can remain focused on the element we think has focus /// </summary> /// <remarks> /// Invoked asynchronously by ReevaluateFocusAsync. /// Confirms that the element we think has focus is: /// - still enabled /// - still visible /// - still in the tree /// </remarks> private object ReevaluateFocusCallback(object arg) { _reevaluateFocusOperation = null; if (_focus == null) { return(null); } // // Reevaluate the eligability of the focused element to actually // have focus. If that element is no longer focusable, then search // for an ancestor that is. // DependencyObject element = _focus; while (element != null) { if (Keyboard.IsFocusable(element)) { break; } // Walk the current tree structure. element = DeferredElementTreeState.GetCoreParent(element, null); } // Get the PresentationSource that contains the element to be focused. PresentationSource presentationSource = null; DependencyObject visualContainer = InputElement.GetContainingVisual(element); if (visualContainer != null) { presentationSource = PresentationSource.CriticalFromVisual(visualContainer); } // The default action is to reset focus to the root element // of the active presentation source. bool moveFocus = true; DependencyObject moveFocusTo = null; if (presentationSource != null) { IKeyboardInputProvider keyboardProvider = presentationSource.GetInputProvider(typeof(KeyboardDevice)) as IKeyboardInputProvider; if (keyboardProvider != null) { // Confirm with the keyboard provider for this // presentation source that it has acquired focus. if (keyboardProvider.AcquireFocus(true)) { if (element == _focus) { // The focus element is still good. moveFocus = false; } else { // The focus element is no longer focusable, but we found // an ancestor that is, so move focus there. moveFocus = true; moveFocusTo = element; } } } } if (moveFocus) { if (moveFocusTo == null && _activeSource != null) { moveFocusTo = _activeSource.Value.RootVisual as DependencyObject; } Focus(moveFocusTo, /*askOld=*/ false, /*askNew=*/ true, /*forceToNullIfFailed=*/ true); } else { // Refresh FocusWithinProperty so that ReverseInherited Flags are updated. // // We only need to do this if there is any information about the old // tree structure. if (_focusTreeState != null && !_focusTreeState.IsEmpty) { UIElement.FocusWithinProperty.OnOriginValueChanged(_focus, _focus, ref _focusTreeState); } } return(null); }
public void PostProcessInput_Should_Not_Be_Called_If_Cancelled() { bool notified = false; PreProcessInputEventHandler preProcess = (sender, ev) => { ev.Cancel(); }; ProcessInputEventHandler postProcess = (sender, ev) => { notified = true; }; InputElement element = new InputElement(); TestEventArgs e = new TestEventArgs(element); e.RoutedEvent = InputElement.RawEvent; InputManager.Current.PreProcessInput += preProcess; InputManager.Current.PostProcessInput += postProcess; InputManager.Current.ProcessInput(e); InputManager.Current.PostProcessInput += postProcess; InputManager.Current.PreProcessInput -= preProcess; Assert.IsFalse(notified); }
private bool ProcessStagingArea() { bool handled = false; // For performance reasons, try to reuse the input event args. // If we are reentrered, we have to start over with fresh event // args, so we clear the member variables before continuing. // Also, we cannot simply make an single instance of the // PreProcessedInputEventArgs and cast it to NotifyInputEventArgs // or ProcessInputEventArgs because a malicious user could upcast // the object and call inappropriate methods. NotifyInputEventArgs notifyInputEventArgs = (_notifyInputEventArgs != null) ? _notifyInputEventArgs : new NotifyInputEventArgs(); ProcessInputEventArgs processInputEventArgs = (_processInputEventArgs != null) ? _processInputEventArgs : new ProcessInputEventArgs(); PreProcessInputEventArgs preProcessInputEventArgs = (_preProcessInputEventArgs != null) ? _preProcessInputEventArgs : new PreProcessInputEventArgs(); _notifyInputEventArgs = null; _processInputEventArgs = null; _preProcessInputEventArgs = null; // Because we can be reentered, we can't just enumerate over the // staging area - that could throw an exception if the queue // changes underneath us. Instead, just loop until we find a // frame marker or until the staging area is empty. StagingAreaInputItem item = null; while ((item = PopInput()) != null) { // If we found a marker, we have reached the end of a // "section" of the staging area. We just return from // the synchronous processing of the staging area. // If a dispatcher frame has been pushed by someone, this // will not return to the original ProcessInput. Instead // it will unwind to the dispatcher and since we have // already pushed a work item to continue processing the // input, it will simply call back into us to do more // processing. At which point we will continue to drain // the staging area. This could cause strage behavior, // but it is deemed more acceptable than stalling input // processing. // if (item.IsMarker) { break; } // Pre-Process the input. This could modify the staging // area, and it could cancel the processing of this // input event. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_preProcessInput != null) { preProcessInputEventArgs.Reset(item, this); // Invoke the handlers in reverse order so that handlers that // users add are invoked before handlers in the system. Delegate[] handlers = _preProcessInput.GetInvocationList(); for (int i = (handlers.Length - 1); i >= 0; i--) { PreProcessInputEventHandler handler = (PreProcessInputEventHandler)handlers[i]; handler(this, preProcessInputEventArgs); } } if (!preProcessInputEventArgs.Canceled) { // Pre-Notify the input. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_preNotifyInput != null) { notifyInputEventArgs.Reset(item, this); // Invoke the handlers in reverse order so that handlers that // users add are invoked before handlers in the system. Delegate[] handlers = _preNotifyInput.GetInvocationList(); for (int i = (handlers.Length - 1); i >= 0; i--) { NotifyInputEventHandler handler = (NotifyInputEventHandler)handlers[i]; handler(this, notifyInputEventArgs); } } // Raise the input event being processed. InputEventArgs input = item.Input; // Some input events are explicitly associated with // an element. Those that are not are associated with // the target of the input device for this event. DependencyObject eventSource = input.Source as DependencyObject; if (eventSource == null || !InputElement.IsValid(eventSource as IInputElement)) { if (input.Device != null) { eventSource = input.Device.Target as DependencyObject; } } // During synchronized input processing, event should be discarded if not listening for this input type. if (_isSynchronizedInput && SynchronizedInputHelper.IsMappedEvent(input) && Array.IndexOf(SynchronizedInputEvents, input.RoutedEvent) < 0 && Array.IndexOf(PairedSynchronizedInputEvents, input.RoutedEvent) < 0) { if (!SynchronizedInputHelper.ShouldContinueListening(input)) { // Discard the event _synchronizedInputState = SynchronizedInputStates.Discarded; SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); } else { _synchronizedInputAsyncClearOperation = Dispatcher.BeginInvoke((Action) delegate { // Discard the event _synchronizedInputState = SynchronizedInputStates.Discarded; SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); }, DispatcherPriority.Background); } } else { if (eventSource != null) { if (InputElement.IsUIElement(eventSource)) { UIElement e = (UIElement)eventSource; e.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent. } else if (InputElement.IsContentElement(eventSource)) { ContentElement ce = (ContentElement)eventSource; ce.RaiseEvent(input, true);// Call the "trusted" flavor of RaiseEvent. } else if (InputElement.IsUIElement3D(eventSource)) { UIElement3D e3D = (UIElement3D)eventSource; e3D.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent } // If synchronized input raise appropriate automation event. if (_isSynchronizedInput && SynchronizedInputHelper.IsListening(_listeningElement, input)) { if (!SynchronizedInputHelper.ShouldContinueListening(input)) { SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); } else { _synchronizedInputAsyncClearOperation = Dispatcher.BeginInvoke((Action) delegate { SynchronizedInputHelper.RaiseAutomationEvents(); CancelSynchronizedInput(); }, DispatcherPriority.Background); } } } } // Post-Notify the input. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_postNotifyInput != null) { notifyInputEventArgs.Reset(item, this); // Invoke the handlers in reverse order so that handlers that // users add are invoked before handlers in the system. Delegate[] handlers = _postNotifyInput.GetInvocationList(); for (int i = (handlers.Length - 1); i >= 0; i--) { NotifyInputEventHandler handler = (NotifyInputEventHandler)handlers[i]; handler(this, notifyInputEventArgs); } } // Post-Process the input. This could modify the staging // area. // // Because we use multi-cast delegates, we always have to // create a new multi-cast delegate when we add or remove // a handler. This means we can just call the current // multi-cast delegate instance, and it is safe to iterate // over, even if we get reentered. if (_postProcessInput != null) { processInputEventArgs.Reset(item, this); RaiseProcessInputEventHandlers(_postProcessInput, processInputEventArgs); // PreviewInputReport --> InputReport if (item.Input.RoutedEvent == InputManager.PreviewInputReportEvent) { if (!item.Input.Handled) { InputReportEventArgs previewInputReport = (InputReportEventArgs)item.Input; InputReportEventArgs inputReport = new InputReportEventArgs(previewInputReport.Device, previewInputReport.Report); inputReport.RoutedEvent = InputManager.InputReportEvent; PushInput(inputReport, item); } } } if (input.Handled) { handled = true; } } } // Store our input event args so that we can use them again, and // avoid having to allocate more. _notifyInputEventArgs = notifyInputEventArgs; _processInputEventArgs = processInputEventArgs; _preProcessInputEventArgs = preProcessInputEventArgs; // Make sure to throw away the contents of the event args so // we don't keep refs around to things we don't mean to. _notifyInputEventArgs.Reset(null, null); _processInputEventArgs.Reset(null, null); _preProcessInputEventArgs.Reset(null, null); return(handled); }
private IInputElement CriticalHitTest(Point point, bool isSynchronize) { IInputElement over = null; if (_activeSource != null) { switch (_captureMode) { case CaptureMode.None: // No capture, do a regular hit-test. if (_isDown) { if (isSynchronize) { // In a synchronize call, we need to hit-test the window in addition to the element over = GlobalHitTest(point, _activeSource); } else { // Just hit-test the element over = LocalHitTest(point, _activeSource); } EnsureValid(ref over); } break; case CaptureMode.Element: // Capture is to a specific element, so the device will always be over that element. over = _captured; break; case CaptureMode.SubTree: // Capture is set to an entire subtree. Hit-test to determine the element (and window) // the device is over. If the element is within the captured sub-tree (which can span // multiple windows), then the device is over that element. If the element is not within // the sub-tree, then the device is over the captured element. { IInputElement capture = InputElement.GetContainingInputElement(_captured as DependencyObject); if (capture != null) { // We need to re-hit-test to get the "real" UIElement we are over. // This allows us to have our capture-to-subtree span multiple windows. // GlobalHitTest always returns an IInputElement, so we are sure to have one. over = GlobalHitTest(point, _activeSource); } EnsureValid(ref over); // Make sure that the element we hit is acutally underneath // our captured element. Because we did a global hit test, we // could have hit an element in a completely different window. // // Note that we support the child being in a completely different window. // So we use the GetUIParent method instead of just looking at // visual/content parents. if (over != null) { IInputElement ieTest = over; while ((ieTest != null) && (ieTest != _captured)) { UIElement eTest = ieTest as UIElement; if (eTest != null) { ieTest = InputElement.GetContainingInputElement(eTest.GetUIParent(true)); } else { ContentElement ceTest = ieTest as ContentElement; if (ceTest != null) { ieTest = InputElement.GetContainingInputElement(ceTest.GetUIParent(true)); } else { UIElement3D e3DTest = (UIElement3D)ieTest; ieTest = InputElement.GetContainingInputElement(e3DTest.GetUIParent(true)); } } } if (ieTest != _captured) { // If we missed the capture point, consider the device over the capture point. over = _captured; } } else { // If we didn't hit anything, consider the device over the capture point. over = _captured; } } break; } } return(over); }
private static void FindCommandBinding(object sender, RoutedEventArgs e, ICommand command, bool execute) { // Check local command bindings CommandBindingCollection commandBindings = null; DependencyObject senderAsDO = sender as DependencyObject; if (InputElement.IsUIElement(senderAsDO)) { commandBindings = ((UIElement)senderAsDO).CommandBindingsInternal; } else if (InputElement.IsContentElement(senderAsDO)) { commandBindings = ((ContentElement)senderAsDO).CommandBindingsInternal; } else if (InputElement.IsUIElement3D(senderAsDO)) { commandBindings = ((UIElement3D)senderAsDO).CommandBindingsInternal; } if (commandBindings != null) { FindCommandBinding(commandBindings, sender, e, command, execute); } // If no command binding is found, check class command bindings // First find the relevant command bindings, under the lock. // Most of the time there are no such bindings; most of the rest of // the time there is only one. Lazy-allocate with this in mind. Tuple <Type, CommandBinding> tuple = null; // zero or one binding List <Tuple <Type, CommandBinding> > list = null; // more than one lock (_classCommandBindings.SyncRoot) { // Check from the current type to all the base types Type classType = sender.GetType(); while (classType != null) { CommandBindingCollection classCommandBindings = _classCommandBindings[classType] as CommandBindingCollection; if (classCommandBindings != null) { int index = 0; while (true) { CommandBinding commandBinding = classCommandBindings.FindMatch(command, ref index); if (commandBinding != null) { if (tuple == null) { tuple = new Tuple <Type, CommandBinding>(classType, commandBinding); } else { if (list == null) { list = new List <Tuple <Type, CommandBinding> >(); list.Add(tuple); } list.Add(new Tuple <Type, CommandBinding>(classType, commandBinding)); } } else { break; } } } classType = classType.BaseType; } } // execute the bindings. This can call into user code, so it must // be done outside the lock to avoid deadlock. if (list != null) { // more than one binding ExecutedRoutedEventArgs exArgs = execute ? (ExecutedRoutedEventArgs)e : null; CanExecuteRoutedEventArgs canExArgs = execute ? null : (CanExecuteRoutedEventArgs)e; for (int i = 0; i < list.Count; ++i) { // invoke the binding if ((execute && ExecuteCommandBinding(sender, exArgs, list[i].Item2)) || (!execute && CanExecuteCommandBinding(sender, canExArgs, list[i].Item2))) { // if it succeeds, advance past the remaining bindings for this type Type classType = list[i].Item1; while (++i < list.Count && list[i].Item1 == classType) { // no body needed } --i; // back up, so that the outer for-loop advances to the right place } } } else if (tuple != null) { // only one binding if (execute) { ExecuteCommandBinding(sender, (ExecutedRoutedEventArgs)e, tuple.Item2); } else { CanExecuteCommandBinding(sender, (CanExecuteRoutedEventArgs)e, tuple.Item2); } } }
/// <summary> /// Scans input and command bindings for matching gestures and executes the appropriate command /// </summary> /// <remarks> /// Scans for command to execute in the following order: /// - input bindings associated with the targetElement instance /// - input bindings associated with the targetElement class /// - command bindings associated with the targetElement instance /// - command bindings associated with the targetElement class /// </remarks> /// <param name="targetElement">UIElement/ContentElement to be scanned for input and command bindings</param> /// <param name="inputEventArgs">InputEventArgs to be matched against for gestures</param> internal static void TranslateInput(IInputElement targetElement, InputEventArgs inputEventArgs) { if ((targetElement == null) || (inputEventArgs == null)) { return; } ICommand command = null; IInputElement target = null; object parameter = null; // Determine UIElement/ContentElement/Neither type DependencyObject targetElementAsDO = targetElement as DependencyObject; bool isUIElement = InputElement.IsUIElement(targetElementAsDO); bool isContentElement = !isUIElement && InputElement.IsContentElement(targetElementAsDO); bool isUIElement3D = !isUIElement && !isContentElement && InputElement.IsUIElement3D(targetElementAsDO); // Step 1: Check local input bindings InputBindingCollection localInputBindings = null; if (isUIElement) { localInputBindings = ((UIElement)targetElement).InputBindingsInternal; } else if (isContentElement) { localInputBindings = ((ContentElement)targetElement).InputBindingsInternal; } else if (isUIElement3D) { localInputBindings = ((UIElement3D)targetElement).InputBindingsInternal; } if (localInputBindings != null) { InputBinding inputBinding = localInputBindings.FindMatch(targetElement, inputEventArgs); if (inputBinding != null) { command = inputBinding.Command; target = inputBinding.CommandTarget; parameter = inputBinding.CommandParameter; } } // Step 2: If no command, check class input bindings if (command == null) { lock (_classInputBindings.SyncRoot) { Type classType = targetElement.GetType(); while (classType != null) { InputBindingCollection classInputBindings = _classInputBindings[classType] as InputBindingCollection; if (classInputBindings != null) { InputBinding inputBinding = classInputBindings.FindMatch(targetElement, inputEventArgs); if (inputBinding != null) { command = inputBinding.Command; target = inputBinding.CommandTarget; parameter = inputBinding.CommandParameter; break; } } classType = classType.BaseType; } } } // Step 3: If no command, check local command bindings if (command == null) { // Check for the instance level ones Next CommandBindingCollection localCommandBindings = null; if (isUIElement) { localCommandBindings = ((UIElement)targetElement).CommandBindingsInternal; } else if (isContentElement) { localCommandBindings = ((ContentElement)targetElement).CommandBindingsInternal; } else if (isUIElement3D) { localCommandBindings = ((UIElement3D)targetElement).CommandBindingsInternal; } if (localCommandBindings != null) { command = localCommandBindings.FindMatch(targetElement, inputEventArgs); } } // Step 4: If no command, look at class command bindings if (command == null) { lock (_classCommandBindings.SyncRoot) { Type classType = targetElement.GetType(); while (classType != null) { CommandBindingCollection classCommandBindings = _classCommandBindings[classType] as CommandBindingCollection; if (classCommandBindings != null) { command = classCommandBindings.FindMatch(targetElement, inputEventArgs); if (command != null) { break; } } classType = classType.BaseType; } } } // Step 5: If found a command, then execute it (unless it is // the special "NotACommand" command, which we simply ignore without // setting Handled=true, so that the input bubbles up to the parent) if (command != null && command != ApplicationCommands.NotACommand) { // We currently do not support declaring the element with focus as the target // element by setting target == null. Instead, we interpret a null target to indicate // the element that we are routing the event through, e.g. the targetElement parameter. if (target == null) { target = targetElement; } bool continueRouting = false; RoutedCommand routedCommand = command as RoutedCommand; if (routedCommand != null) { if (routedCommand.CriticalCanExecute(parameter, target, inputEventArgs.UserInitiated /*trusted*/, out continueRouting)) { // If the command can be executed, we never continue to route the // input event. continueRouting = false; ExecuteCommand(routedCommand, parameter, target, inputEventArgs); } } else { if (command.CanExecute(parameter)) { command.Execute(parameter); } } // If we mapped an input event to a command, we should always // handle the input event - regardless of whether the command // was executed or not. Unless the CanExecute handler told us // to continue the route. inputEventArgs.Handled = !continueRouting; } }
internal static Point TranslatePoint(Point pt, DependencyObject from, DependencyObject to, out bool translated) { translated = false; Point ptTranslated = pt; // Get the containing and root visuals we are coming from. DependencyObject vFromAsDO = InputElement.GetContainingVisual(from); Visual rootFrom = InputElement.GetRootVisual(from) as Visual; Visual vFrom = vFromAsDO as Visual; if (vFromAsDO != null && vFrom == null) { // must be a Visual3D - get it's 2D visual parent vFrom = VisualTreeHelper.GetContainingVisual2D(vFromAsDO); } if (vFrom != null && rootFrom != null) { GeneralTransform gUp; Matrix mUp; bool isUpSimple = false; isUpSimple = vFrom.TrySimpleTransformToAncestor(rootFrom, false, /* do not apply inverse */ out gUp, out mUp); if (isUpSimple) { ptTranslated = mUp.Transform(ptTranslated); } else if (gUp.TryTransform(ptTranslated, out ptTranslated) == false) { // Error. Out parameter has been set false. return(new Point()); } // If no element was specified to translate to, we leave the coordinates // translated to the root. if (to != null) { // Get the containing and root visuals we are going to. DependencyObject vTo = InputElement.GetContainingVisual(to); Visual rootTo = InputElement.GetRootVisual(to) as Visual; if (vTo != null && rootTo != null) { // If both are under the same root visual, we can easily translate the point // between them by translating up to the root, and then back down. // // However, if both are under different roots, we can only translate // between them if we know how to relate the two root visuals. Currently // we only know how to do that if both roots are sourced in HwndSources. if (rootFrom != rootTo) { HwndSource sourceFrom = PresentationSource.CriticalFromVisual(rootFrom) as HwndSource; HwndSource sourceTo = PresentationSource.CriticalFromVisual(rootTo) as HwndSource; if (sourceFrom != null && sourceFrom.CriticalHandle != IntPtr.Zero && sourceFrom.CompositionTarget != null && sourceTo != null && sourceTo.CriticalHandle != IntPtr.Zero && sourceTo.CompositionTarget != null) { // Translate the point into client coordinates. ptTranslated = PointUtil.RootToClient(ptTranslated, sourceFrom); // Translate the point into screen coordinates. Point ptScreen = PointUtil.ClientToScreen(ptTranslated, sourceFrom); // Translate the point back the the client coordinates of the To window. ptTranslated = PointUtil.ScreenToClient(ptScreen, sourceTo); // Translate the point back to the root element. ptTranslated = PointUtil.ClientToRoot(ptTranslated, sourceTo); } else { // Error. Out parameter has been set false. return(new Point()); } } // Translate the point from the root to the visual. GeneralTransform gDown; Matrix mDown; Visual vToAsVisual = vTo as Visual; if (vToAsVisual == null) { // must be a Visual3D vToAsVisual = VisualTreeHelper.GetContainingVisual2D(vTo); } bool isDownSimple = vToAsVisual.TrySimpleTransformToAncestor(rootTo, true, /* apply inverse */ out gDown, out mDown); if (isDownSimple) { ptTranslated = mDown.Transform(ptTranslated); } else if (gDown != null) { if (gDown.TryTransform(ptTranslated, out ptTranslated) == false) { // Error. Out parameter has been set false. return(new Point()); } } else { // Error. Out parameter has been set false. return(new Point()); } } else { // Error. Out parameter has been set false. return(new Point()); } } } else { // Error. Out parameter has been set false. return(new Point()); } translated = true; return(ptTranslated); }