Browse Source

Use KeySymbol for AccessKey (#20662)

* Change AccessText.AccessKey to a string

* Use KeySymbol for AccessKey

* Update API suppressions

* Fix tests
pull/20687/head
Julien Lebosquain 1 month ago
committed by GitHub
parent
commit
2185ce04e7
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      api/Avalonia.nupkg.xml
  2. 24
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  3. 2
      src/Avalonia.Base/Input/IAccessKeyHandler.cs
  4. 2
      src/Avalonia.Controls/Automation/Peers/MenuItemAutomationPeer.cs
  5. 14
      src/Avalonia.Controls/Primitives/AccessText.cs
  6. 69
      tests/Avalonia.Base.UnitTests/Input/AccessKeyHandlerTests.cs
  7. 21
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  8. 18
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs

12
api/Avalonia.nupkg.xml

@ -1219,6 +1219,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.AccessText.get_AccessKey</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect})</Target> <Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect})</Target>
@ -2131,6 +2137,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.AccessText.get_AccessKey</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect})</Target> <Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect})</Target>

24
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -122,9 +122,11 @@ namespace Avalonia.Input
/// </summary> /// </summary>
/// <param name="accessKey">The access key.</param> /// <param name="accessKey">The access key.</param>
/// <param name="element">The input element.</param> /// <param name="element">The input element.</param>
public void Register(char accessKey, IInputElement element) public void Register(string accessKey, IInputElement element)
{ {
var key = NormalizeKey(accessKey.ToString()); ArgumentException.ThrowIfNullOrEmpty(accessKey);
var key = NormalizeKey(accessKey);
// remove dead elements with matching key // remove dead elements with matching key
for (var i = _registrations.Count - 1; i >= 0; i--) for (var i = _registrations.Count - 1; i >= 0; i--)
@ -224,7 +226,7 @@ namespace Avalonia.Input
MainMenu?.IsOpen != true) MainMenu?.IsOpen != true)
return; return;
e.Handled = ProcessKey(e.Key.ToString(), e.Source as IInputElement); e.Handled = ProcessKey(e.KeySymbol, e.Source as IInputElement);
} }
@ -287,8 +289,11 @@ namespace Avalonia.Input
/// <param name="key">The access key to process.</param> /// <param name="key">The access key to process.</param>
/// <param name="element">The element to get the targets which are in scope.</param> /// <param name="element">The element to get the targets which are in scope.</param>
/// <returns>If there matches <c>true</c>, otherwise <c>false</c>.</returns> /// <returns>If there matches <c>true</c>, otherwise <c>false</c>.</returns>
protected bool ProcessKey(string key, IInputElement? element) protected bool ProcessKey(string? key, IInputElement? element)
{ {
if (string.IsNullOrEmpty(key))
return false;
key = NormalizeKey(key); key = NormalizeKey(key);
var senderInfo = GetTargetForElement(element, key); var senderInfo = GetTargetForElement(element, key);
// Find the possible targets matching the access key // Find the possible targets matching the access key
@ -503,20 +508,11 @@ namespace Avalonia.Input
/// </summary> /// </summary>
internal class AccessKeyPressedEventArgs : RoutedEventArgs internal class AccessKeyPressedEventArgs : RoutedEventArgs
{ {
/// <summary>
/// The constructor for AccessKeyPressed event args
/// </summary>
public AccessKeyPressedEventArgs()
{
RoutedEvent = AccessKeyHandler.AccessKeyPressedEvent;
Key = null;
}
/// <summary> /// <summary>
/// Constructor for AccessKeyPressed event args /// Constructor for AccessKeyPressed event args
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
public AccessKeyPressedEventArgs(string key) : this() public AccessKeyPressedEventArgs(string key)
{ {
RoutedEvent = AccessKeyHandler.AccessKeyPressedEvent; RoutedEvent = AccessKeyHandler.AccessKeyPressedEvent;
Key = key; Key = key;

2
src/Avalonia.Base/Input/IAccessKeyHandler.cs

@ -26,7 +26,7 @@ namespace Avalonia.Input
/// </summary> /// </summary>
/// <param name="accessKey">The access key.</param> /// <param name="accessKey">The access key.</param>
/// <param name="element">The input element.</param> /// <param name="element">The input element.</param>
void Register(char accessKey, IInputElement element); void Register(string accessKey, IInputElement element);
/// <summary> /// <summary>
/// Unregisters the access keys associated with the input element. /// Unregisters the access keys associated with the input element.

2
src/Avalonia.Controls/Automation/Peers/MenuItemAutomationPeer.cs

@ -20,7 +20,7 @@ namespace Avalonia.Automation.Peers
{ {
if (Owner.HeaderPresenter?.Child is AccessText accessText) if (Owner.HeaderPresenter?.Child is AccessText accessText)
{ {
result = accessText.AccessKey.ToString(); result = accessText.AccessKey;
} }
} }

14
src/Avalonia.Controls/Primitives/AccessText.cs

@ -4,6 +4,7 @@ using Avalonia.Reactive;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting;
using System; using System;
using System.Text;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
@ -42,7 +43,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Gets the access key. /// Gets the access key.
/// </summary> /// </summary>
public char AccessKey public string? AccessKey
{ {
get; get;
private set; private set;
@ -93,7 +94,7 @@ namespace Avalonia.Controls.Primitives
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
_accessKeys = (e.Root as TopLevel)?.AccessKeyHandler; _accessKeys = (e.Root as TopLevel)?.AccessKeyHandler;
if (_accessKeys != null && AccessKey != 0) if (_accessKeys != null && !string.IsNullOrEmpty(AccessKey))
{ {
_accessKeys.Register(AccessKey, this); _accessKeys.Register(AccessKey, this);
} }
@ -104,7 +105,7 @@ namespace Avalonia.Controls.Primitives
{ {
base.OnDetachedFromVisualTree(e); base.OnDetachedFromVisualTree(e);
if (_accessKeys != null && AccessKey != 0) if (_accessKeys != null && !string.IsNullOrEmpty(AccessKey))
{ {
_accessKeys.Unregister(this); _accessKeys.Unregister(this);
_accessKeys = null; _accessKeys = null;
@ -153,7 +154,7 @@ namespace Avalonia.Controls.Primitives
/// <param name="text">The new text.</param> /// <param name="text">The new text.</param>
private void TextChanged(string? text) private void TextChanged(string? text)
{ {
var key = (char)0; string? key = null;
if (text != null) if (text != null)
{ {
@ -161,13 +162,14 @@ namespace Avalonia.Controls.Primitives
if (underscore != -1 && underscore < text.Length - 1) if (underscore != -1 && underscore < text.Length - 1)
{ {
key = text[underscore + 1]; var rune = Rune.GetRuneAt(text, underscore + 1);
key = rune.ToString();
} }
} }
AccessKey = key; AccessKey = key;
if (_accessKeys != null && AccessKey != 0) if (_accessKeys != null && !string.IsNullOrEmpty(AccessKey))
{ {
_accessKeys.Register(AccessKey, this); _accessKeys.Register(AccessKey, this);
} }

69
tests/Avalonia.Base.UnitTests/Input/AccessKeyHandlerTests.cs

@ -21,8 +21,8 @@ namespace Avalonia.Base.UnitTests.Input
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}"); root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt); KeyDown(root, Key.LeftAlt);
KeyDown(root, Key.A, KeyModifiers.Alt); KeyDown(root, Key.A, "a", KeyModifiers.Alt);
KeyUp(root, Key.A, KeyModifiers.Alt); KeyUp(root, Key.A, "a", KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt); KeyUp(root, Key.LeftAlt);
Assert.Equal(new[] Assert.Equal(new[]
@ -48,8 +48,8 @@ namespace Avalonia.Base.UnitTests.Input
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}"); root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt); KeyDown(root, Key.LeftAlt);
KeyDown(root, Key.A, KeyModifiers.Alt); KeyDown(root, Key.A, "a", KeyModifiers.Alt);
KeyUp(root, Key.A, KeyModifiers.Alt); KeyUp(root, Key.A, "a", KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt); KeyUp(root, Key.LeftAlt);
Assert.Equal(new[] Assert.Equal(new[]
@ -122,13 +122,13 @@ namespace Avalonia.Base.UnitTests.Input
var events = new List<string>(); var events = new List<string>();
target.SetOwner(root); target.SetOwner(root);
target.Register('A', button); target.Register("A", button);
root.KeyDown += (s, e) => events.Add($"KeyDown {e.Key}"); root.KeyDown += (s, e) => events.Add($"KeyDown {e.Key}");
root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}"); root.KeyUp += (s, e) => events.Add($"KeyUp {e.Key}");
KeyDown(root, Key.LeftAlt); KeyDown(root, Key.LeftAlt);
KeyDown(root, Key.A, KeyModifiers.Alt); KeyDown(root, Key.A, "a", KeyModifiers.Alt);
KeyUp(root, Key.A, KeyModifiers.Alt); KeyUp(root, Key.A, "a", KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt); KeyUp(root, Key.LeftAlt);
// This differs from WPF which doesn't raise the `A` key event, but matches UWP. // This differs from WPF which doesn't raise the `A` key event, but matches UWP.
@ -141,8 +141,12 @@ namespace Avalonia.Base.UnitTests.Input
}, events); }, events);
} }
[Fact] [Theory]
public void Should_Raise_AccessKey_For_Registered_Access_Key() [InlineData("A", Key.A, "a")]
[InlineData("A", Key.Q, "a")]
[InlineData("é", Key.D2, "é")]
[InlineData("2", Key.D2, "2")]
public void Should_Raise_AccessKey_For_Registered_Access_Key_Matching_KeySymbol(string registered, Key key, string keySymbol)
{ {
using (UnitTestApplication.Start(TestServices.RealFocus)) using (UnitTestApplication.Start(TestServices.RealFocus))
{ {
@ -154,22 +158,51 @@ namespace Avalonia.Base.UnitTests.Input
KeyboardDevice.Instance?.SetFocusedElement(button, NavigationMethod.Unspecified, KeyModifiers.None); KeyboardDevice.Instance?.SetFocusedElement(button, NavigationMethod.Unspecified, KeyModifiers.None);
target.SetOwner(root); target.SetOwner(root);
target.Register('A', button); target.Register(registered, button);
button.AddHandler(AccessKeyHandler.AccessKeyEvent, (s, e) => ++raised); button.AddHandler(AccessKeyHandler.AccessKeyEvent, (s, e) => ++raised);
KeyDown(root, Key.LeftAlt); KeyDown(root, Key.LeftAlt);
Assert.Equal(0, raised); Assert.Equal(0, raised);
KeyDown(root, Key.A, KeyModifiers.Alt); KeyDown(root, key, keySymbol, KeyModifiers.Alt);
Assert.Equal(1, raised); Assert.Equal(1, raised);
KeyUp(root, Key.A, KeyModifiers.Alt); KeyUp(root, key, keySymbol, KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt); KeyUp(root, Key.LeftAlt);
Assert.Equal(1, raised); Assert.Equal(1, raised);
} }
} }
[Fact]
public void Should_Not_Raise_AccessKey_For_Registered_Access_Key_Not_Matching_KeySymbol()
{
using (UnitTestApplication.Start(TestServices.RealFocus))
{
var button = new Button();
var root = new TestRoot(button);
var target = new AccessKeyHandler();
var raised = 0;
KeyboardDevice.Instance?.SetFocusedElement(button, NavigationMethod.Unspecified, KeyModifiers.None);
target.SetOwner(root);
target.Register("A", button);
button.AddHandler(AccessKeyHandler.AccessKeyEvent, (_, _) => ++raised);
KeyDown(root, Key.LeftAlt);
Assert.Equal(0, raised);
KeyDown(root, Key.A, "q", KeyModifiers.Alt);
Assert.Equal(0, raised);
KeyUp(root, Key.A, "q", KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt);
Assert.Equal(0, raised);
}
}
[Theory] [Theory]
[InlineData(false, 0)] [InlineData(false, 0)]
[InlineData(true, 1)] [InlineData(true, 1)]
@ -185,16 +218,16 @@ namespace Avalonia.Base.UnitTests.Input
KeyboardDevice.Instance?.SetFocusedElement(button, NavigationMethod.Unspecified, KeyModifiers.None); KeyboardDevice.Instance?.SetFocusedElement(button, NavigationMethod.Unspecified, KeyModifiers.None);
target.SetOwner(root); target.SetOwner(root);
target.Register('A', button); target.Register("A", button);
button.AddHandler(AccessKeyHandler.AccessKeyEvent, (s, e) => ++raised); button.AddHandler(AccessKeyHandler.AccessKeyEvent, (s, e) => ++raised);
KeyDown(root, Key.LeftAlt); KeyDown(root, Key.LeftAlt);
Assert.Equal(0, raised); Assert.Equal(0, raised);
KeyDown(root, Key.A, KeyModifiers.Alt); KeyDown(root, Key.A, "a", KeyModifiers.Alt);
Assert.Equal(expected, raised); Assert.Equal(expected, raised);
KeyUp(root, Key.A, KeyModifiers.Alt); KeyUp(root, Key.A, "a", KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt); KeyUp(root, Key.LeftAlt);
Assert.Equal(expected, raised); Assert.Equal(expected, raised);
} }
@ -223,22 +256,24 @@ namespace Avalonia.Base.UnitTests.Input
} }
} }
private static void KeyDown(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None) private static void KeyDown(IInputElement target, Key key, string? keySymbol = null, KeyModifiers modifiers = KeyModifiers.None)
{ {
target.RaiseEvent(new KeyEventArgs target.RaiseEvent(new KeyEventArgs
{ {
RoutedEvent = InputElement.KeyDownEvent, RoutedEvent = InputElement.KeyDownEvent,
Key = key, Key = key,
KeySymbol = keySymbol,
KeyModifiers = modifiers, KeyModifiers = modifiers,
}); });
} }
private static void KeyUp(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None) private static void KeyUp(IInputElement target, Key key, string? keySymbol = null, KeyModifiers modifiers = KeyModifiers.None)
{ {
target.RaiseEvent(new KeyEventArgs target.RaiseEvent(new KeyEventArgs
{ {
RoutedEvent = InputElement.KeyUpEvent, RoutedEvent = InputElement.KeyUpEvent,
Key = key, Key = key,
KeySymbol = keySymbol,
KeyModifiers = modifiers, KeyModifiers = modifiers,
}); });
} }

21
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@ -345,16 +345,17 @@ namespace Avalonia.Controls.UnitTests
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded, TestContext.Current.CancellationToken); Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded, TestContext.Current.CancellationToken);
var accessKey = Key.A; const Key accessKey = Key.A;
const string accessKeySymbol = "a";
target.CommandParameter = true; target.CommandParameter = true;
RaiseAccessKey(root, accessKey); RaiseAccessKey(root, accessKey, accessKeySymbol);
Assert.Equal(1, raised); Assert.Equal(1, raised);
target.CommandParameter = false; target.CommandParameter = false;
RaiseAccessKey(root, accessKey); RaiseAccessKey(root, accessKey, accessKeySymbol);
Assert.Equal(1, raised); Assert.Equal(1, raised);
@ -379,30 +380,32 @@ namespace Avalonia.Controls.UnitTests
return topLevel; return topLevel;
} }
static void RaiseAccessKey(IInputElement target, Key accessKey) static void RaiseAccessKey(IInputElement target, Key accessKey, string keySymbol)
{ {
KeyDown(target, Key.LeftAlt); KeyDown(target, Key.LeftAlt);
KeyDown(target, accessKey, KeyModifiers.Alt); KeyDown(target, accessKey, keySymbol, KeyModifiers.Alt);
KeyUp(target, accessKey, KeyModifiers.Alt); KeyUp(target, accessKey, keySymbol, KeyModifiers.Alt);
KeyUp(target, Key.LeftAlt); KeyUp(target, Key.LeftAlt, null);
} }
static void KeyDown(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None) static void KeyDown(IInputElement target, Key key, string? keySymbol = null, KeyModifiers modifiers = KeyModifiers.None)
{ {
target.RaiseEvent(new KeyEventArgs target.RaiseEvent(new KeyEventArgs
{ {
RoutedEvent = InputElement.KeyDownEvent, RoutedEvent = InputElement.KeyDownEvent,
Key = key, Key = key,
KeySymbol = keySymbol,
KeyModifiers = modifiers, KeyModifiers = modifiers,
}); });
} }
static void KeyUp(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None) static void KeyUp(IInputElement target, Key key, string? keySymbol = null, KeyModifiers modifiers = KeyModifiers.None)
{ {
target.RaiseEvent(new KeyEventArgs target.RaiseEvent(new KeyEventArgs
{ {
RoutedEvent = InputElement.KeyUpEvent, RoutedEvent = InputElement.KeyUpEvent,
Key = key, Key = key,
KeySymbol = keySymbol,
KeyModifiers = modifiers, KeyModifiers = modifiers,
}); });
} }

18
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -718,10 +718,10 @@ namespace Avalonia.Controls.UnitTests
} }
[Theory] [Theory]
[InlineData(Key.A, 1)] [InlineData(Key.A, "a", 1)]
[InlineData(Key.L, 2)] [InlineData(Key.L, "l", 2)]
[InlineData(Key.D, 0)] [InlineData(Key.D, "d", 0)]
public void Should_TabControl_Recognizes_AccessKey(Key accessKey, int selectedTabIndex) public void Should_TabControl_Recognizes_AccessKey(Key accessKey, string accessKeySymbol, int selectedTabIndex)
{ {
var ah = new AccessKeyHandler(); var ah = new AccessKeyHandler();
var kd = new KeyboardDevice(); var kd = new KeyboardDevice();
@ -760,8 +760,8 @@ namespace Avalonia.Controls.UnitTests
ApplyTemplate(tabControl); ApplyTemplate(tabControl);
KeyDown(root, Key.LeftAlt); KeyDown(root, Key.LeftAlt);
KeyDown(root, accessKey, KeyModifiers.Alt); KeyDown(root, accessKey, accessKeySymbol, KeyModifiers.Alt);
KeyUp(root, accessKey, KeyModifiers.Alt); KeyUp(root, accessKey, accessKeySymbol, KeyModifiers.Alt);
KeyUp(root, Key.LeftAlt); KeyUp(root, Key.LeftAlt);
Assert.Equal(selectedTabIndex, tabControl.SelectedIndex); Assert.Equal(selectedTabIndex, tabControl.SelectedIndex);
@ -788,22 +788,24 @@ namespace Avalonia.Controls.UnitTests
return topLevel; return topLevel;
} }
static void KeyDown(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None) static void KeyDown(IInputElement target, Key key, string? keySymbol = null, KeyModifiers modifiers = KeyModifiers.None)
{ {
target.RaiseEvent(new KeyEventArgs target.RaiseEvent(new KeyEventArgs
{ {
RoutedEvent = InputElement.KeyDownEvent, RoutedEvent = InputElement.KeyDownEvent,
Key = key, Key = key,
KeySymbol = keySymbol,
KeyModifiers = modifiers, KeyModifiers = modifiers,
}); });
} }
static void KeyUp(IInputElement target, Key key, KeyModifiers modifiers = KeyModifiers.None) static void KeyUp(IInputElement target, Key key, string? keySymbol = null, KeyModifiers modifiers = KeyModifiers.None)
{ {
target.RaiseEvent(new KeyEventArgs target.RaiseEvent(new KeyEventArgs
{ {
RoutedEvent = InputElement.KeyUpEvent, RoutedEvent = InputElement.KeyUpEvent,
Key = key, Key = key,
KeySymbol = keySymbol,
KeyModifiers = modifiers, KeyModifiers = modifiers,
}); });
} }

Loading…
Cancel
Save