private void CriticalCanExecuteWrapper(object parameter, IInputElement target, bool trusted, CanExecuteRoutedEventArgs args) { // This cast is ok since we are already testing for UIElement, ContentElement, or UIElement3D // both of which derive from DO DependencyObject targetAsDO = (DependencyObject)target; if (InputElement.IsUIElement(targetAsDO)) { ((UIElement)targetAsDO).RaiseEvent(args, trusted); } else if (InputElement.IsContentElement(targetAsDO)) { ((ContentElement)targetAsDO).RaiseEvent(args, trusted); } else if (InputElement.IsUIElement3D(targetAsDO)) { ((UIElement3D)targetAsDO).RaiseEvent(args, trusted); } }
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. // In the future, in ordre to // make sure we all agree on this. We could also // just delay the rest of the staging area until // the dispatcher frame finishes. Unfortunately, // this means one could receive an input event for // something that happened a long time ago. 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 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); } } }
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); } }
/// <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; } }