Example #1
0
        /// <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);
        }