From f1cf27c82fee0c41a66249cc126a759faed8c9fb Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 10 Jan 2025 20:32:21 +0100 Subject: [PATCH 1/9] Simplify CanConvertFrom --- .../Windows/Input/Command/KeyGestureConverter.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index a092fbb637a..9f8ff886c0b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -30,17 +30,9 @@ public class KeyGestureConverter : TypeConverter public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { // We can only handle string. - if (sourceType == typeof(string)) - { - return true; - } - else - { - return false; - } + return sourceType == typeof(string); } - /// ///TypeConverter method override. /// From 3534cfb6be5846fcf8daca09944ed5d9674dabf3 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 10 Jan 2025 20:39:48 +0100 Subject: [PATCH 2/9] Simplify CanConvertTo --- .../Input/Command/KeyGestureConverter.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index 9f8ff886c0b..f674dfaecb4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -42,20 +42,14 @@ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceT public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { // We can convert to an InstanceDescriptor or to a string. - if (destinationType == typeof(string)) - { - // When invoked by the serialization engine we can convert to string only for known type - if (context != null && context.Instance != null) - { - KeyGesture keyGesture = context.Instance as KeyGesture; - if (keyGesture != null) - { - return (ModifierKeysConverter.IsDefinedModifierKeys(keyGesture.Modifiers) - && IsDefinedKey(keyGesture.Key)); - } - } - } - return false; + if (destinationType != typeof(string)) + return false; + + // When invoked by the serialization engine we can convert to string only for known type + if (context?.Instance is not KeyGesture keyGesture) + return false; + + return ModifierKeysConverter.IsDefinedModifierKeys(keyGesture.Modifiers) && IsDefinedKey(keyGesture.Key); } /// @@ -166,10 +160,14 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul throw GetConvertToException(value,destinationType); } - // Check for Valid enum, as any int can be casted to the enum. + /// + /// Helper function similar to , just lighter and faster. + /// + /// The value to test against. + /// if falls in enumeration range, otherwise. internal static bool IsDefinedKey(Key key) { - return (key >= Key.None && key <= Key.OemClear); + return key >= Key.None && key <= Key.OemClear; } private static KeyConverter keyConverter = new KeyConverter(); From f3362892e41ca91b67560e1fa33dd9f442253643 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 10 Jan 2025 22:45:01 +0100 Subject: [PATCH 3/9] Optimize ConvertTo; small opt in ModifierKeysConverter --- .../Input/Command/KeyGestureConverter.cs | 62 +++++++++---------- .../Windows/Input/ModifierKeysConverter.cs | 34 ++++++++-- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index f674dfaecb4..c3bf279dab1 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -123,41 +123,37 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul { ArgumentNullException.ThrowIfNull(destinationType); - if (destinationType == typeof(string)) + if (destinationType != typeof(string)) + throw GetConvertToException(value, destinationType); + + // Following checks are here to match the previous behavior + if (value is null) + return string.Empty; + + if (value is not KeyGesture keyGesture) + throw GetConvertToException(value, destinationType); + + // If the key is None, nothing else matters + if (keyGesture.Key is Key.None) + return string.Empty; + + // You will only get string.Empty from KeyConverter for Key.None and we've checked that above + string strKey = (string)keyConverter.ConvertTo(context, culture, keyGesture.Key, destinationType); + + // Prepend modifiers if there are any (TODO: ConvertMultipleModifiers is gonna crumble on no matching mods) + ReadOnlySpan modifierSpan = ModifierKeysConverter.ConvertMultipleModifiers(keyGesture.Modifiers, stackalloc char[22]); + if (modifierSpan.IsEmpty) { - if (value != null) - { - KeyGesture keyGesture = value as KeyGesture; - if (keyGesture != null) - { - if (keyGesture.Key == Key.None) - return String.Empty; - - string strBinding = "" ; - string strKey = (string)keyConverter.ConvertTo(context, culture, keyGesture.Key, destinationType) as string; - if (strKey != String.Empty) - { - strBinding += modifierKeysConverter.ConvertTo(context, culture, keyGesture.Modifiers, destinationType) as string; - if (strBinding != String.Empty) - { - strBinding += MODIFIERS_DELIMITER; - } - strBinding += strKey; - - if (!String.IsNullOrEmpty(keyGesture.DisplayString)) - { - strBinding += DISPLAYSTRING_SEPARATOR + keyGesture.DisplayString; - } - } - return strBinding; - } - } - else - { - return String.Empty; - } + // No modifiers, just binding (possibly with with display string) + return string.IsNullOrEmpty(keyGesture.DisplayString) ? strKey : $"{strKey},{keyGesture.DisplayString}"; } - throw GetConvertToException(value,destinationType); + + // Append display string if there's any, like "Ctrl+A,Description" + if (!string.IsNullOrEmpty(keyGesture.DisplayString)) + return string.Create(CultureInfo.InvariantCulture, stackalloc char[168], $"{modifierSpan}+{strKey},{keyGesture.DisplayString}"); + + // We just put together modifiers and key, like "Ctrl+A" + return string.Create(CultureInfo.InvariantCulture, stackalloc char[50], $"{modifierSpan}+{strKey}"); } /// diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs index 3d2789703f0..1d63a435e3f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -123,10 +123,32 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul }; } - private static string ConvertMultipleModifiers(ModifierKeys modifiers) + /// + /// This is a proxy function for + /// so we do not initialize a stack-allocated buffer when we do not need to. + /// + /// The modifiers bits to format. + /// Formatted in [modifier_1]+[modifier_2] format. + [MethodImpl(MethodImplOptions.NoInlining)] + internal static string ConvertMultipleModifiers(ModifierKeys modifiers) + { + return new string(ConvertMultipleModifiers(modifiers, stackalloc char[22])); + } + + /// + /// Format multiple into -provided buffer. + /// + /// The modifiers bits to format. + /// The buffer to write formatted modifiers into. + /// Formatted in [modifier_1]+[modifier_2] format. + /// + /// Make sure the is at least 22 chars long, otherwise conversion might fail. + /// + internal static ReadOnlySpan ConvertMultipleModifiers(ModifierKeys modifiers, Span modifierSpan) { // Ctrl+Alt+Windows+Shift is the maximum char length, though the composition of such value is improbable - Span modifierSpan = stackalloc char[22]; + Debug.Assert(modifierSpan.Length > 21); + int totalLength = 0; if (modifiers.HasFlag(ModifierKeys.Control)) @@ -141,14 +163,14 @@ private static string ConvertMultipleModifiers(ModifierKeys modifiers) if (modifiers.HasFlag(ModifierKeys.Shift)) AppendWithDelimiter("Shift", ref totalLength, ref modifierSpan); - //Helper function to concatenate modifiers + // Helper function to concatenate modifiers [MethodImpl(MethodImplOptions.AggressiveInlining)] static void AppendWithDelimiter(string literal, ref int totalLength, ref Span modifierSpan) { // If this is not the first modifier in the span, we prepend a delimiter (e.g. Ctrl -> Ctrl+Alt) if (totalLength > 0) { - "+".CopyTo(modifierSpan.Slice(totalLength)); + modifierSpan[totalLength] = '+'; totalLength++; } @@ -156,7 +178,7 @@ static void AppendWithDelimiter(string literal, ref int totalLength, ref Span From 447f2415e5d2b263fb369d61a3374631a940f599 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 11 Jan 2025 11:56:41 +0100 Subject: [PATCH 4/9] Optimize ConvertFrom --- .../Windows/Input/Command/KeyGesture.cs | 7 +- .../Input/Command/KeyGestureConverter.cs | 86 ++++++++----------- .../System/Windows/Input/KeyConverter.cs | 2 +- .../Windows/Input/ModifierKeysConverter.cs | 12 +++ 4 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGesture.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGesture.cs index f3a4bf70072..b90f78d2658 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGesture.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGesture.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -287,9 +287,9 @@ internal static KeyGesture CreateFromResourceStrings(string keyGestureToken, str { // combine the gesture and the display string, producing a string // that the type converter will recognize - if (!String.IsNullOrEmpty(keyDisplayString)) + if (!string.IsNullOrEmpty(keyDisplayString)) { - keyGestureToken += KeyGestureConverter.DISPLAYSTRING_SEPARATOR + keyDisplayString; + keyGestureToken += DisplayStringSeparator + keyDisplayString; } return _keyGestureConverter.ConvertFromInvariantString(keyGestureToken) as KeyGesture; @@ -307,6 +307,7 @@ internal static KeyGesture CreateFromResourceStrings(string keyGestureToken, str private Key _key = Key.None; private string _displayString; private const char MULTIPLEGESTURE_DELIMITER = ';'; + private const char DisplayStringSeparator = ','; private static TypeConverter _keyGestureConverter = new KeyGestureConverter(); //private static bool _classRegistered = false; #endregion Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index c3bf279dab1..234196b205e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -18,9 +18,6 @@ namespace System.Windows.Input /// public class KeyGestureConverter : TypeConverter { - private const char MODIFIERS_DELIMITER = '+' ; - internal const char DISPLAYSTRING_SEPARATOR = ',' ; - /// ///CanConvertFrom() /// @@ -61,54 +58,43 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object source) { - if (source != null && source is string) + if (source is not string sourceString) + throw GetConvertFromException(source); + + ReadOnlySpan trimmedSource = sourceString.AsSpan().Trim(); + if (trimmedSource.IsEmpty) + return new KeyGesture(Key.None); + + ReadOnlySpan keyToken; + ReadOnlySpan modifiersToken; + string displayString; + + // Break apart display string + int index = trimmedSource.IndexOf(','); + if (index >= 0) { - string fullName = ((string)source).Trim(); - if (fullName.Length == 0) - return new KeyGesture(Key.None); - - string keyToken; - string modifiersToken; - string displayString; - - // break apart display string - int index = fullName.IndexOf(DISPLAYSTRING_SEPARATOR); - if (index >= 0) - { - displayString = fullName.Substring(index + 1).Trim(); - fullName = fullName.Substring(0, index).Trim(); - } - else - { - displayString = String.Empty; - } - - // break apart key and modifiers - index = fullName.LastIndexOf(MODIFIERS_DELIMITER); - if (index >= 0) - { // modifiers exists - modifiersToken = fullName.Substring(0, index); - keyToken = fullName.Substring(index + 1); - } - else - { - modifiersToken = String.Empty; - keyToken = fullName; - } - - ModifierKeys modifiers = ModifierKeys.None; - object resultkey = keyConverter.ConvertFrom(context, culture, keyToken); - if (resultkey != null) - { - object temp = modifierKeysConverter.ConvertFrom(context, culture, modifiersToken); - if (temp != null) - { - modifiers = (ModifierKeys)temp; - } - return new KeyGesture((Key)resultkey, modifiers, displayString); - } + displayString = trimmedSource.Slice(index + 1).Trim().ToString(); + trimmedSource = trimmedSource.Slice(0, index).Trim(); } - throw GetConvertFromException(source); + else + { + displayString = string.Empty; + } + + // Break apart key and modifiers + index = trimmedSource.LastIndexOf('+'); + if (index >= 0) + { + modifiersToken = trimmedSource.Slice(0, index); + keyToken = trimmedSource.Slice(index + 1).Trim(); + } + else + { + modifiersToken = ReadOnlySpan.Empty; + keyToken = trimmedSource; + } + + return new KeyGesture(KeyConverter.GetKeyFromString(keyToken), ModifierKeysConverter.ConvertFromImpl(modifiersToken), displayString); } /// @@ -167,7 +153,7 @@ internal static bool IsDefinedKey(Key key) } private static KeyConverter keyConverter = new KeyConverter(); - private static ModifierKeysConverter modifierKeysConverter = new ModifierKeysConverter(); + } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs index 1deae00b596..30eefe15b9c 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs @@ -101,7 +101,7 @@ _ when IsDefinedKey(key) => key.ToString(), /// /// The string to convert from. /// A value corresponding to the specified string, if was empty. - private static Key GetKeyFromString(ReadOnlySpan keyToken) + internal static Key GetKeyFromString(ReadOnlySpan keyToken) { // If the token is empty, we presume "None" as our value but it is a success if (keyToken.IsEmpty) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs index 1d63a435e3f..28b18c0ed01 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs @@ -58,6 +58,18 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c ReadOnlySpan modifiersToken = stringSource.AsSpan().Trim(); + return ConvertFromImpl(modifiersToken); + } + + /// + /// Converts to its represensation. + /// + /// + /// is expected to have separated with '+', with any amount of whitespace. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ModifierKeys ConvertFromImpl(ReadOnlySpan modifiersToken) + { // Empty token means there were no modifiers, exit early if (modifiersToken.IsEmpty) return ModifierKeys.None; From 321dcb79e3155c37bb310794e84f6d6569d3de87 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 11 Jan 2025 11:59:09 +0100 Subject: [PATCH 5/9] Fix license header/summary --- .../Windows/Input/Command/KeyGestureConverter.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index 234196b205e..c4bb69c9e29 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -1,20 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -// -// -// Description: KeyGestureConverter - Converts a KeyGesture string -// to the *Type* that the string represents -// + using System.ComponentModel; // for TypeConverter using System.Globalization; // for CultureInfo namespace System.Windows.Input { /// - /// KeyGesture - Converter class for converting between a string and the Type of a KeyGesture + /// Converter class for converting between a and . /// public class KeyGestureConverter : TypeConverter { From 09bafcb75bf5dfa545cc57a49aec6b5e18cf4d20 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 11 Jan 2025 13:40:48 +0100 Subject: [PATCH 6/9] Mark s_keyConverter as readonly --- .../Windows/Input/Command/KeyGestureConverter.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index c4bb69c9e29..9145378b305 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -11,6 +11,11 @@ namespace System.Windows.Input /// public class KeyGestureConverter : TypeConverter { + /// + /// To aid with conversion from to . + /// + private static readonly KeyConverter s_keyConverter = new(); + /// ///CanConvertFrom() /// @@ -117,7 +122,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul return string.Empty; // You will only get string.Empty from KeyConverter for Key.None and we've checked that above - string strKey = (string)keyConverter.ConvertTo(context, culture, keyGesture.Key, destinationType); + string strKey = (string)s_keyConverter.ConvertTo(context, culture, keyGesture.Key, destinationType); // Prepend modifiers if there are any (TODO: ConvertMultipleModifiers is gonna crumble on no matching mods) ReadOnlySpan modifierSpan = ModifierKeysConverter.ConvertMultipleModifiers(keyGesture.Modifiers, stackalloc char[22]); @@ -144,9 +149,6 @@ internal static bool IsDefinedKey(Key key) { return key >= Key.None && key <= Key.OemClear; } - - private static KeyConverter keyConverter = new KeyConverter(); - } } From 7b817397a8a0f7269fd4c6af5a624db3aa93c85a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 11 Jan 2025 14:04:13 +0100 Subject: [PATCH 7/9] Update docs --- .../Input/Command/KeyGestureConverter.cs | 60 ++++++++++++------- .../System/Windows/Input/KeyConverter.cs | 20 ++++--- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index 9145378b305..ac428e24b8a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -16,24 +16,28 @@ public class KeyGestureConverter : TypeConverter /// private static readonly KeyConverter s_keyConverter = new(); - /// - ///CanConvertFrom() - /// - ///ITypeDescriptorContext - ///type to convert from - ///true if the given type can be converted, false otherwise + /// + /// Returns whether or not this class can convert from a given . + /// + /// The for this call. + /// The being queried for support. + /// + /// if the given can be converted, otherwise. + /// public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { // We can only handle string. return sourceType == typeof(string); } - /// - ///TypeConverter method override. - /// - ///ITypeDescriptorContext - ///Type to convert to - ///true if conversion is possible + /// + /// Returns whether or not this class can convert to a given . + /// + /// The for this call. + /// The being queried for support. + /// + /// if this class can convert to , otherwise. + /// public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { // We can convert to an InstanceDescriptor or to a string. @@ -48,12 +52,15 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati } /// - /// ConvertFrom() + /// Converts of type to its represensation. /// - /// - /// - /// - /// + /// This parameter is ignored during the call. + /// This parameter is ignored during the call. + /// The object to convert to a . + /// + /// A new instance of class representing the data contained in . + /// + /// Thrown in case the was not a . public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object source) { if (source is not string sourceString) @@ -96,13 +103,20 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c } /// - /// ConvertTo() + /// Attempt to convert a class to the . /// - /// - /// - /// - /// - /// + /// This parameter is ignored during the call. + /// This parameter is ignored during the call. + /// The object to convert to a . + /// The to convert to. + /// + /// The formatted to its representation. + /// + /// Thrown in case was . + /// + /// Thrown in case the was not a + /// or was not a . + /// public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { ArgumentNullException.ThrowIfNull(destinationType); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs index 30eefe15b9c..c26971f9607 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/KeyConverter.cs @@ -8,12 +8,12 @@ namespace System.Windows.Input { /// - /// Converter class for converting between a and . + /// Converter class for converting between a and . /// public class KeyConverter : TypeConverter { /// - /// Used to check whether we can convert a into a . + /// Used to check whether we can convert a into a . /// ///ITypeDescriptorContext ///type to convert from @@ -25,11 +25,11 @@ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceT } /// - /// Used to check whether we can convert specified value to . + /// Used to check whether we can convert specified value to . /// /// ITypeDescriptorContext /// Type to convert to - /// if conversion to is possible, otherwise. + /// if conversion to is possible, otherwise. public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { // We can convert to a string @@ -44,12 +44,12 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati } /// - /// Converts of type to its representation. + /// Converts of type to its representation. /// /// Parser Context /// Culture Info /// Key String - /// A representing the specified by . + /// A representing the specified by . public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object source) { if (source is not string stringSource) @@ -60,13 +60,13 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c } /// - /// Converts a of type to its representation. + /// Converts a of type to its representation. /// /// Serialization Context /// Culture Info /// Key value /// Type to Convert - /// A representing the specified by . + /// A representing the specified by . public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { ArgumentNullException.ThrowIfNull(destinationType); @@ -100,7 +100,9 @@ _ when IsDefinedKey(key) => key.ToString(), /// Helper function that performs the conversion of to the enum. /// /// The string to convert from. - /// A value corresponding to the specified string, if was empty. + /// + /// A value corresponding to the specified string, if was empty. + /// internal static Key GetKeyFromString(ReadOnlySpan keyToken) { // If the token is empty, we presume "None" as our value but it is a success From e7b6abbd0f6d7e69be4135c91e334c7f2904a44d Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 11 Jan 2025 14:36:04 +0100 Subject: [PATCH 8/9] One branch for the times when someone uses gesture without modifiers --- .../Windows/Input/Command/KeyGestureConverter.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index ac428e24b8a..dc0fd60958c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -138,13 +138,12 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul // You will only get string.Empty from KeyConverter for Key.None and we've checked that above string strKey = (string)s_keyConverter.ConvertTo(context, culture, keyGesture.Key, destinationType); - // Prepend modifiers if there are any (TODO: ConvertMultipleModifiers is gonna crumble on no matching mods) - ReadOnlySpan modifierSpan = ModifierKeysConverter.ConvertMultipleModifiers(keyGesture.Modifiers, stackalloc char[22]); - if (modifierSpan.IsEmpty) - { - // No modifiers, just binding (possibly with with display string) + // No modifiers, just binding (possibly with with display string) + if (keyGesture.Modifiers is ModifierKeys.None) return string.IsNullOrEmpty(keyGesture.DisplayString) ? strKey : $"{strKey},{keyGesture.DisplayString}"; - } + + // Prepend modifiers + ReadOnlySpan modifierSpan = ModifierKeysConverter.ConvertMultipleModifiers(keyGesture.Modifiers, stackalloc char[22]); // Append display string if there's any, like "Ctrl+A,Description" if (!string.IsNullOrEmpty(keyGesture.DisplayString)) From 6a96a7bb110386a50df2f462f8a1e2cfc1b0075a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 11 Jan 2025 15:37:19 +0100 Subject: [PATCH 9/9] Irrelevant comment removal --- .../System/Windows/Input/Command/KeyGestureConverter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs index dc0fd60958c..99124293916 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/KeyGestureConverter.cs @@ -138,11 +138,10 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul // You will only get string.Empty from KeyConverter for Key.None and we've checked that above string strKey = (string)s_keyConverter.ConvertTo(context, culture, keyGesture.Key, destinationType); - // No modifiers, just binding (possibly with with display string) + // No modifiers, just binding (possibly with with display string) -> "F5,Refresh" if (keyGesture.Modifiers is ModifierKeys.None) return string.IsNullOrEmpty(keyGesture.DisplayString) ? strKey : $"{strKey},{keyGesture.DisplayString}"; - // Prepend modifiers ReadOnlySpan modifierSpan = ModifierKeysConverter.ConvertMultipleModifiers(keyGesture.Modifiers, stackalloc char[22]); // Append display string if there's any, like "Ctrl+A,Description"