/// <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)); }
/// <summary> /// Gets the display string. /// </summary> /// <param name="keys">The keys.</param> /// <param name="modifiers">The modifiers.</param> /// <param name="displayString">The user-defined display string.</param> /// <returns>The display string.</returns> private static string GetDisplayString(IEnumerable <Key> keys, ModifierKeys modifiers, string displayString) { // This static method is another little "hack": We want to set KeyGesture.DisplayString // to make sure that the key gesture, such as "Ctrl+K, Ctrl+D" is automatically set for // MenuItems. However, KeyGesture.DisplayString is readonly and can only be set in the // constructor. Therefore, we call this method immediately before the base constructor. if (!string.IsNullOrEmpty(displayString)) { // Use the user-defined display string. return(displayString); } // Automatically create the display string. return(MultiKeyGestureConverter.GetDisplayStringForCulture(keys, modifiers, null, CultureInfo.CurrentCulture)); }
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); }