/// <summary> /// Determines whether the specified object can be converted into a <see cref="String"/>. /// </summary> /// <param name="value">The object to evaluate for conversion.</param> /// <param name="context">Context information that is used for conversion.</param> /// <returns> /// <see langword="true"/> if the <paramref name="value"/> can be converted into a /// <see cref="String"/>; otherwise, <see langword="false"/>. /// </returns> public override bool CanConvertToString(object value, IValueSerializerContext context) { var gesture = value as MultiKeyGesture; return(gesture != null && ModifierKeysConverter.IsDefinedModifierKeys(gesture.Modifiers) && MultiKeyGestureConverter.IsDefinedKey(gesture.Key)); }
private void OnPreviewKeyDown(object sender, KeyEventArgs eventArgs) { if ((eventArgs == null) || !MultiKeyGestureConverter.IsDefinedKey(eventArgs.Key)) { Reset(); return; } var timeSinceLastKeyPress = TimeSpan.FromMilliseconds(eventArgs.Timestamp - _lastKeyPress); if (timeSinceLastKeyPress > MaximumDelayBetweenKeyPresses) { // Took too long to press next key. Reset(); return; } if (ShouldIgnoreKey(eventArgs.Key)) { // When a modifier key is set, we ignore certain keys (LeftCtrl, RightCtrl, ...). // I.e. the user can release/press LeftCtrl multiple times. We only track the relevant keys. eventArgs.Handled = true; return; } if (eventArgs.Key == Key.Escape) { // Cancel multi-key gesture. eventArgs.Handled = true; Reset(); return; } if (Keyboard.Modifiers != Modifiers) { // Wrong modifiers. Reset(); return; } Debug.Assert(_currentKeyIndex > 0, "At least one key of MultiKeyGesture should have been pressed."); if (_keys[_currentKeyIndex] != eventArgs.Key) { // Wrong key pressed. Reset(); return; } _currentKeyIndex++; if (_currentKeyIndex < _keys.Count) { // We are still in the multi-key gesture, waiting for next key stroke. _lastKeyPress = eventArgs.Timestamp; eventArgs.Handled = true; return; } // Match complete! // Report a success on the next call of Matches(). _reportMatch = true; // Set eventArgs.Handled to prevent other key gestures from firing. // For example: If we have detected "Ctrl+K, Ctrl+X" then we do not want "Ctrl+X" (Cut) // to be fired. eventArgs.Handled = true; // However, now we have a problem! eventArgs.Handled is set, so the KeyDown event is // canceled and Matches() never gets called. To solve this problem, we re-raise a dummy // KeyDown event with Key.None. This way Matches() gets called again. // [MartinG: Sorry, for this little "hack", but this is the only way, we can prevent // conflicting key gestures such as "Ctrl+X" (Cut) from firing.] _currentTarget.Dispatcher.BeginInvoke( DispatcherPriority.Input, new Action( () => _currentTarget.RaiseEvent( new KeyEventArgs( eventArgs.KeyboardDevice, eventArgs.InputSource, eventArgs.Timestamp, Key.None) { RoutedEvent = Keyboard.KeyDownEvent })) ); }
public override bool Matches(object targetElement, InputEventArgs inputEventArgs) { if (_currentTarget != null) { if (_currentTarget == targetElement) { if (_reportMatch) { // We have already checked the key (see OnPreviewKeyDown) and found a match. Reset(); return(true); } else { // We have already installed an event handler: // The check is happening in OnPreviewKeyDown. We can exit here. return(false); } } else { // We are already within a multi-key gesture. But suddenly the targetElement has changed. Reset(); } } KeyEventArgs eventArgs = inputEventArgs as KeyEventArgs; if ((eventArgs == null) || !MultiKeyGestureConverter.IsDefinedKey(eventArgs.Key)) { return(false); } // ----- Check first key stroke if (Keyboard.Modifiers != Modifiers) { // Wrong modifiers. return(false); } if (eventArgs.Key != _keys[0]) { // Wrong key. return(false); } // First key matches! _lastKeyPress = eventArgs.Timestamp; if (_keys.Count == 1) { // The current multi-key gesture only has one key. return(true); } // We need to check more than one key. // --> Install an event handler for PreviewKeyDown. UIElement uiElement = targetElement as UIElement; if (uiElement == null) { // We currently can only check for multiple keys if the target is an UIElement. throw new InvalidOperationException(Invariant($"MultiKeyGesture has invalid target element ({targetElement}). The target element needs to be of type UIElement.")); } _currentTarget = uiElement; _currentTarget.PreviewKeyDown += OnPreviewKeyDown; _currentKeyIndex = 1; return(false); }