diff --git a/components/Behaviors/src/Keyboard/KeyDownTriggerBehavior.cs b/components/Behaviors/src/Keyboard/KeyDownTriggerBehavior.cs
index 2eddac0c..650b3b2a 100644
--- a/components/Behaviors/src/Keyboard/KeyDownTriggerBehavior.cs
+++ b/components/Behaviors/src/Keyboard/KeyDownTriggerBehavior.cs
@@ -8,22 +8,26 @@
namespace CommunityToolkit.WinUI.Behaviors;
///
-/// This behavior listens to a key down event on the associated when it is loaded and executes an action.
+/// A behavior that listens to on the associated
+/// and executes its actions when the specified key and
+/// optional modifier keys are pressed. Supports capturing handled events.
///
[TypeConstraint(typeof(FrameworkElement))]
public class KeyDownTriggerBehavior : Trigger
{
+ private KeyEventHandler? _handler;
///
- /// Identifies the property.
+ /// Identifies the dependency property.
///
- public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
- nameof(Key),
- typeof(VirtualKey),
- typeof(KeyDownTriggerBehavior),
- new PropertyMetadata(null));
+ public static readonly DependencyProperty KeyProperty =
+ DependencyProperty.Register(
+ nameof(Key),
+ typeof(VirtualKey),
+ typeof(KeyDownTriggerBehavior),
+ new PropertyMetadata(VirtualKey.None));
///
- /// Gets or sets the key to listen when the associated object is loaded.
+ /// Gets or sets the key that triggers the behavior.
///
public VirtualKey Key
{
@@ -31,30 +35,123 @@ public VirtualKey Key
set => SetValue(KeyProperty, value);
}
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty ModifiersProperty =
+ DependencyProperty.Register(
+ nameof(Modifiers),
+ typeof(VirtualKeyModifiers),
+ typeof(KeyDownTriggerBehavior),
+ new PropertyMetadata(VirtualKeyModifiers.None));
+
+ ///
+ /// Gets or sets the modifier keys that must be pressed together with .
+ ///
+ public VirtualKeyModifiers Modifiers
+ {
+ get => (VirtualKeyModifiers)GetValue(ModifiersProperty);
+ set => SetValue(ModifiersProperty, value);
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty HandledEventsTooProperty =
+ DependencyProperty.Register(
+ nameof(HandledEventsToo),
+ typeof(bool),
+ typeof(KeyDownTriggerBehavior),
+ new PropertyMetadata(true));
+
+ ///
+ /// Gets or sets a value indicating whether the behavior should receive
+ /// events even if they were already handled.
+ ///
+ public bool HandledEventsToo
+ {
+ get => (bool)GetValue(HandledEventsTooProperty);
+ set => SetValue(HandledEventsTooProperty, value);
+ }
+
///
protected override void OnAttached()
{
- AssociatedObject.KeyDown += OnAssociatedObjectKeyDown;
+ _handler = OnPreviewKeyDown;
+
+ AssociatedObject.AddHandler(
+ UIElement.PreviewKeyDownEvent,
+ _handler,
+ HandledEventsToo);
}
///
protected override void OnDetaching()
{
- AssociatedObject.KeyDown -= OnAssociatedObjectKeyDown;
+ if (_handler is not null)
+ {
+ AssociatedObject.RemoveHandler(
+ UIElement.PreviewKeyDownEvent,
+ _handler);
+
+ _handler = null;
+ }
}
///
- /// Invokes the current actions when the is pressed.
+ /// Handles the event and executes the associated actions
+ /// when the specified and match.
///
/// The source instance.
/// The arguments for the event (unused).
- private void OnAssociatedObjectKeyDown(object sender, KeyRoutedEventArgs keyRoutedEventArgs)
+ private void OnPreviewKeyDown(object sender, KeyRoutedEventArgs keyRoutedEventArgs)
{
- if (keyRoutedEventArgs.Key == Key)
+ if (keyRoutedEventArgs.Key != Key)
{
- keyRoutedEventArgs.Handled = true;
- Interaction.ExecuteActions(sender, Actions, keyRoutedEventArgs);
+ return;
}
+
+ if (!CheckModifiers())
+ {
+ return;
+ }
+
+ keyRoutedEventArgs.Handled = true;
+ Interaction.ExecuteActions(sender, Actions, keyRoutedEventArgs);
}
-}
+ ///
+ /// Checks whether all required modifier keys specified in
+ /// are currently pressed.
+ ///
+ /// if the modifier state matches; otherwise, .
+
+ private bool CheckModifiers() =>
+ Match(VirtualKeyModifiers.Control, VirtualKey.Control) &&
+ Match(VirtualKeyModifiers.Shift, VirtualKey.Shift) &&
+ Match(VirtualKeyModifiers.Menu, VirtualKey.Menu);
+
+ ///
+ /// Determines whether a specific modifier key is required and whether it is currently pressed.
+ ///
+ /// The modifier flag to test.
+ /// The physical key corresponding to the modifier.
+ /// if the modifier requirement matches the current key state; otherwise, .
+ private bool Match(VirtualKeyModifiers mod, VirtualKey key)
+ {
+ bool required = (Modifiers & mod) != 0;
+ bool pressed = IsDown(key);
+ return required == pressed;
+ }
+
+ ///
+ /// Checks whether the specified key is currently in the state.
+ ///
+ /// The key to test.
+ /// if the key is pressed; otherwise, .
+ private static bool IsDown(VirtualKey key)
+ {
+ var state = InputKeyboardSource.GetKeyStateForCurrentThread(key);
+ return state.HasFlag(CoreVirtualKeyStates.Down);
+ }
+}