diff --git a/Test/UnitTests/EventTriggerTests.cs b/Test/UnitTests/EventTriggerTests.cs
index 369e6ec..c021233 100644
--- a/Test/UnitTests/EventTriggerTests.cs
+++ b/Test/UnitTests/EventTriggerTests.cs
@@ -16,9 +16,9 @@ public class EventTriggerTest
[TestInitialize]
public void Setup()
- {
- Interaction.ShouldRunInDesignMode = true;
- }
+ {
+ Interaction.ShouldRunInDesignMode = true;
+ }
[TestCleanup]
public void Teardown()
diff --git a/Test/UnitTests/KeyTriggerTest.cs b/Test/UnitTests/KeyTriggerTest.cs
new file mode 100644
index 0000000..d754912
--- /dev/null
+++ b/Test/UnitTests/KeyTriggerTest.cs
@@ -0,0 +1,181 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.Xaml.Behaviors;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows;
+using Microsoft.Xaml.Behaviors.Input;
+using System.Windows.Interop;
+using System;
+
+namespace Microsoft.Xaml.Interactions.UnitTests
+{
+ [TestClass]
+ public class KeyTriggerTest
+ {
+ #region Setup/teardown
+
+ [TestInitialize]
+ public void Setup()
+ {
+ Interaction.ShouldRunInDesignMode = true;
+ }
+
+ [TestCleanup]
+ public void Teardown()
+ {
+ Interaction.ShouldRunInDesignMode = false;
+ }
+
+ #endregion
+
+ #region Test methods
+
+ [TestMethod]
+ [DataRow(Key.A)]
+ [DataRow(Key.B)]
+ [DataRow(Key.C)]
+ [DataRow(Key.D)]
+ [DataRow(Key.E)]
+ [DataRow(Key.F)]
+ [DataRow(Key.G)]
+ [DataRow(Key.H)]
+ [DataRow(Key.I)]
+ [DataRow(Key.J)]
+ [DataRow(Key.K)]
+ [DataRow(Key.L)]
+ [DataRow(Key.M)]
+ [DataRow(Key.N)]
+ [DataRow(Key.O)]
+ [DataRow(Key.P)]
+ [DataRow(Key.Q)]
+ [DataRow(Key.R)]
+ [DataRow(Key.S)]
+ [DataRow(Key.T)]
+ [DataRow(Key.U)]
+ [DataRow(Key.V)]
+ [DataRow(Key.W)]
+ [DataRow(Key.X)]
+ [DataRow(Key.Y)]
+ [DataRow(Key.Z)]
+ [DataRow(Key.NumPad1)]
+ [DataRow(Key.NumPad2)]
+ [DataRow(Key.NumPad3)]
+ [DataRow(Key.NumPad4)]
+ [DataRow(Key.NumPad5)]
+ [DataRow(Key.NumPad6)]
+ [DataRow(Key.NumPad7)]
+ [DataRow(Key.NumPad8)]
+ [DataRow(Key.NumPad9)]
+ [DataRow(Key.Enter)]
+ [DataRow(Key.Tab)]
+ public void KeyTrigger_InvokesActions_WhenKeyIsPressed(Key key)
+ {
+ var textBox = new TextBox();
+ var keyTrigger = new KeyTrigger { Key = key };
+ var action = new StubAction();
+ keyTrigger.Actions.Add(action);
+ keyTrigger.Attach(textBox);
+
+ Grid grid = new Grid();
+ grid.Children.Add(textBox);
+ using (StubWindow window = new StubWindow(grid))
+ {
+ var inputSource = PresentationSource.FromVisual(textBox) ?? new HwndSource(0, 0, 0, 0, 0, "", IntPtr.Zero);
+ var keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, inputSource, 0, key);
+ keyEventArgs.RoutedEvent = Keyboard.KeyDownEvent;
+ textBox.RaiseEvent(keyEventArgs);
+
+ Assert.AreEqual(1, action.InvokeCount);
+ }
+ }
+
+ [TestMethod]
+ [DataRow(Key.A)]
+ [DataRow(Key.B)]
+ [DataRow(Key.C)]
+ [DataRow(Key.D)]
+ [DataRow(Key.E)]
+ [DataRow(Key.F)]
+ [DataRow(Key.G)]
+ [DataRow(Key.H)]
+ [DataRow(Key.I)]
+ [DataRow(Key.J)]
+ [DataRow(Key.K)]
+ [DataRow(Key.L)]
+ [DataRow(Key.M)]
+ [DataRow(Key.N)]
+ [DataRow(Key.O)]
+ [DataRow(Key.P)]
+ [DataRow(Key.Q)]
+ [DataRow(Key.R)]
+ [DataRow(Key.S)]
+ [DataRow(Key.T)]
+ [DataRow(Key.U)]
+ [DataRow(Key.V)]
+ [DataRow(Key.W)]
+ [DataRow(Key.X)]
+ [DataRow(Key.Y)]
+ [DataRow(Key.Z)]
+ [DataRow(Key.NumPad1)]
+ [DataRow(Key.NumPad2)]
+ [DataRow(Key.NumPad3)]
+ [DataRow(Key.NumPad4)]
+ [DataRow(Key.NumPad5)]
+ [DataRow(Key.NumPad6)]
+ [DataRow(Key.NumPad7)]
+ [DataRow(Key.NumPad8)]
+ [DataRow(Key.NumPad9)]
+ [DataRow(Key.Enter)]
+ [DataRow(Key.Tab)]
+ public void KeyTrigger_InvokesActions_WhenKeyIsReleased(Key key)
+ {
+ var textBox = new TextBox();
+ var keyTrigger = new KeyTrigger { Key = key, FiredOn = KeyTriggerFiredOn.KeyUp };
+ var action = new StubAction();
+ keyTrigger.Actions.Add(action);
+ keyTrigger.Attach(textBox);
+
+ Grid grid = new Grid();
+ grid.Children.Add(textBox);
+ using (StubWindow window = new StubWindow(grid))
+ {
+ var inputSource = PresentationSource.FromVisual(textBox) ?? new HwndSource(0, 0, 0, 0, 0, "", IntPtr.Zero);
+ var keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, inputSource, 0, key);
+ keyEventArgs.RoutedEvent = Keyboard.KeyUpEvent;
+ textBox.RaiseEvent(keyEventArgs);
+
+ Assert.AreEqual(1, action.InvokeCount);
+ }
+ }
+
+
+ [TestMethod]
+ [DataRow(true)]
+ [DataRow(false)]
+ public void KeyTrigger_InvokesActionsOnce_WhenLoadedEventFiredMultipleTimes(bool activeOnFocus)
+ {
+ var textBox = new TextBox();
+ var keyTrigger = new KeyTrigger { ActiveOnFocus = activeOnFocus, Key = Key.Enter };
+ var action = new StubAction();
+ keyTrigger.Actions.Add(action);
+ keyTrigger.Attach(textBox);
+
+ Grid grid = new Grid();
+ grid.Children.Add(textBox);
+ using (StubWindow window = new StubWindow(grid))
+ {
+ //simulate the loaded event being invoked multiple times; for example, when using an element in a tab control
+ textBox.RaiseEvent(new RoutedEventArgs(FrameworkElement.LoadedEvent));
+
+ var inputSource = PresentationSource.FromVisual(textBox) ?? new HwndSource(0, 0, 0, 0, 0, "", IntPtr.Zero);
+ var keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, inputSource, 0, Key.Enter);
+ keyEventArgs.RoutedEvent = Keyboard.KeyDownEvent;
+ textBox.RaiseEvent(keyEventArgs);
+
+ Assert.AreEqual(1, action.InvokeCount);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.Xaml.Behaviors/EventTriggerBase.cs b/src/Microsoft.Xaml.Behaviors/EventTriggerBase.cs
index d62cad0..bfeff3a 100644
--- a/src/Microsoft.Xaml.Behaviors/EventTriggerBase.cs
+++ b/src/Microsoft.Xaml.Behaviors/EventTriggerBase.cs
@@ -408,7 +408,11 @@ private void RegisterLoaded(FrameworkElement associatedElement)
}
}
- private void UnregisterLoaded(FrameworkElement associatedElement)
+ ///
+ /// Removes the event handler from the Loaded event of the associated object.
+ ///
+ /// The associated object
+ protected void UnregisterLoaded(FrameworkElement associatedElement)
{
Debug.Assert(this.eventHandlerMethodInfo == null);
if (this.IsLoadedRegistered && associatedElement != null)
diff --git a/src/Microsoft.Xaml.Behaviors/Input/KeyTrigger.cs b/src/Microsoft.Xaml.Behaviors/Input/KeyTrigger.cs
index 1ddf228..aece7b3 100644
--- a/src/Microsoft.Xaml.Behaviors/Input/KeyTrigger.cs
+++ b/src/Microsoft.Xaml.Behaviors/Input/KeyTrigger.cs
@@ -117,6 +117,10 @@ protected override void OnEvent(EventArgs eventArgs)
{
this.targetElement.KeyUp += this.OnKeyPress;
}
+
+ // Unregister the Loaded event of the Source object to prevent the KeyUp or KeyDown events from being registered multiple times.
+ // this is especially important when the KeyTrigger is used in a TabControl/TabItem.
+ UnregisterLoaded(Source as FrameworkElement);
}
protected override void OnDetaching()