diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs
index 62f3ad65dc8..9b4863c11d6 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs
@@ -1,19 +1,19 @@
-// 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.
+using System.Runtime.CompilerServices;
using System.ComponentModel;
using System.Globalization;
namespace System.Windows.Input
{
///
- /// Converter class for converting between a and .
+ /// Converter class for converting between a and .
///
public class MouseActionConverter : 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 an InstanceDescriptor or to a string
@@ -45,41 +45,48 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati
}
///
- /// Converts of type to its represensation.
+ /// Converts of type to its represensation.
///
/// Parser Context
/// Culture Info
/// MouseAction 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 mouseAction)
throw GetConvertFromException(source);
ReadOnlySpan mouseActionToken = mouseAction.AsSpan().Trim();
- return mouseActionToken switch
- {
- _ when mouseActionToken.IsEmpty => MouseAction.None, // Special casing as produced by "ConvertTo"
- _ when mouseActionToken.Equals("None", StringComparison.OrdinalIgnoreCase) => MouseAction.None,
- _ when mouseActionToken.Equals("LeftClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftClick,
- _ when mouseActionToken.Equals("RightClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightClick,
- _ when mouseActionToken.Equals("MiddleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleClick,
- _ when mouseActionToken.Equals("WheelClick", StringComparison.OrdinalIgnoreCase) => MouseAction.WheelClick,
- _ when mouseActionToken.Equals("LeftDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftDoubleClick,
- _ when mouseActionToken.Equals("RightDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightDoubleClick,
- _ when mouseActionToken.Equals("MiddleDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleDoubleClick,
- _ => throw new NotSupportedException(SR.Format(SR.Unsupported_MouseAction, mouseActionToken.ToString()))
- };
+
+ return ConvertFromImpl(mouseActionToken);
}
///
- /// Converts a of to its represensation.
+ /// Converts to its represensation.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static MouseAction ConvertFromImpl(ReadOnlySpan mouseActionToken) => mouseActionToken switch
+ {
+ _ when mouseActionToken.IsEmpty => MouseAction.None, // Special casing as produced by "ConvertTo"
+ _ when mouseActionToken.Equals("None", StringComparison.OrdinalIgnoreCase) => MouseAction.None,
+ _ when mouseActionToken.Equals("LeftClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftClick,
+ _ when mouseActionToken.Equals("RightClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightClick,
+ _ when mouseActionToken.Equals("MiddleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleClick,
+ _ when mouseActionToken.Equals("WheelClick", StringComparison.OrdinalIgnoreCase) => MouseAction.WheelClick,
+ _ when mouseActionToken.Equals("LeftDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftDoubleClick,
+ _ when mouseActionToken.Equals("RightDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightDoubleClick,
+ _ when mouseActionToken.Equals("MiddleDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleDoubleClick,
+ _ => throw new NotSupportedException(SR.Format(SR.Unsupported_MouseAction, mouseActionToken.ToString()))
+ };
+
+ ///
+ /// Converts a of to its represensation.
///
/// Serialization Context
/// Culture Info
/// MouseAction 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);
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGesture.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGesture.cs
index 9320b104a99..31ec452f878 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGesture.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGesture.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.
@@ -53,13 +53,13 @@ public MouseGesture(MouseAction mouseAction): this(mouseAction, ModifierKeys.Non
///
/// Mouse Action
/// Modifiers
- public MouseGesture( MouseAction mouseAction,ModifierKeys modifiers) // acclerator action
+ public MouseGesture(MouseAction mouseAction, ModifierKeys modifiers) // acclerator action
{
- if (!MouseGesture.IsDefinedMouseAction(mouseAction))
- throw new InvalidEnumArgumentException("mouseAction", (int)mouseAction, typeof(MouseAction));
+ if (!MouseActionConverter.IsDefinedMouseAction(mouseAction))
+ throw new InvalidEnumArgumentException(nameof(mouseAction), (int)mouseAction, typeof(MouseAction));
if (!ModifierKeysConverter.IsDefinedModifierKeys(modifiers))
- throw new InvalidEnumArgumentException("modifiers", (int)modifiers, typeof(ModifierKeys));
+ throw new InvalidEnumArgumentException(nameof(modifiers), (int)modifiers, typeof(ModifierKeys));
_modifiers = modifiers;
_mouseAction = mouseAction;
@@ -86,12 +86,13 @@ public MouseAction MouseAction
}
set
{
- if (!MouseGesture.IsDefinedMouseAction((MouseAction)value))
- throw new InvalidEnumArgumentException("value", (int)value, typeof(MouseAction));
+ if (!MouseActionConverter.IsDefinedMouseAction(value))
+ throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(MouseAction));
+
if (_mouseAction != value)
{
- _mouseAction = (MouseAction)value;
- OnPropertyChanged("MouseAction");
+ _mouseAction = value;
+ OnPropertyChanged(nameof(MouseAction));
}
}
}
@@ -107,13 +108,13 @@ public ModifierKeys Modifiers
}
set
{
- if (!ModifierKeysConverter.IsDefinedModifierKeys((ModifierKeys)value))
- throw new InvalidEnumArgumentException("value", (int)value, typeof(ModifierKeys));
+ if (!ModifierKeysConverter.IsDefinedModifierKeys(value))
+ throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(ModifierKeys));
if (_modifiers != value)
{
- _modifiers = (ModifierKeys)value;
- OnPropertyChanged("Modifiers");
+ _modifiers = value;
+ OnPropertyChanged(nameof(Modifiers));
}
}
}
@@ -135,12 +136,6 @@ public override bool Matches(object targetElement, InputEventArgs inputEventArgs
return false;
}
-
- // Helper like Enum.IsDefined, for MouseAction.
- internal static bool IsDefinedMouseAction(MouseAction mouseAction)
- {
- return (mouseAction >= MouseAction.None && mouseAction <= MouseAction.MiddleDoubleClick);
- }
#endregion Public Methods
#region Internal NotifyProperty changed
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGestureConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGestureConverter.cs
index a864915b898..1a8cb27861b 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGestureConverter.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseGestureConverter.cs
@@ -1,14 +1,5 @@
-// 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.
-
-//
-//
-//
-// Description: MouseGestureConverter - Converts a MouseGesture string
-// to the *Type* that the string represents
-//
-//
using System.ComponentModel; // for TypeConverter
using System.Globalization; // for CultureInfo
@@ -16,161 +7,120 @@
namespace System.Windows.Input
{
///
- /// MouseGesture - Converter class for converting between a string and the Type of a MouseGesture
+ /// Converter class for converting between a and .
///
public class MouseGestureConverter : TypeConverter
{
- private const char MODIFIERS_DELIMITER = '+' ;
-
- ///
- /// CanConvertFrom()
- ///
- ///ITypeDescriptorContext
- ///type to convert from
- ///true if the given type can be converted, false otherwise
+ ///
+ /// To aid with conversion from to .
+ ///
+ private static readonly MouseActionConverter s_mouseActionConverter = new();
+
+ ///
+ /// 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.
- if (sourceType == typeof(string))
- {
- return true;
- }
- else
- {
- return false;
- }
+ return sourceType == typeof(string);
}
///
- /// ConvertFrom
+ /// Returns whether or not this class can convert to a given .
///
- /// Parser Context
- /// Culture Info
- /// MouseGesture String
- ///
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object source)
+ /// The for this call.
+ /// The being queried for support.
+ ///
+ /// if this class can convert to , otherwise.
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
- if (source is string && source != null)
- {
- string fullName = ((string)source).Trim();
- string mouseActionToken;
- string modifiersToken;
-
- if (fullName.Length == 0)
- return new MouseGesture(MouseAction.None, ModifierKeys.None);
-
- // break apart LocalName and Prefix
- int Offset = fullName.LastIndexOf(MODIFIERS_DELIMITER);
- if (Offset >= 0)
- { // modifiers exists
- modifiersToken = fullName.Substring(0,Offset);
- mouseActionToken = fullName.Substring(Offset + 1);
- }
- else
- {
- modifiersToken = String.Empty;
- mouseActionToken = fullName;
- }
-
- TypeConverter mouseActionConverter = TypeDescriptor.GetConverter(typeof(System.Windows.Input.MouseAction));
- if (null != mouseActionConverter )
- {
- object mouseAction = mouseActionConverter.ConvertFrom(context, culture, mouseActionToken);
- // mouseAction Converter will throw Exception, if it fails,
- // so we don't need to check once more for bogus
- // MouseAction values
- if (mouseAction != null)
- {
- if (modifiersToken != String.Empty)
- {
- TypeConverter modifierKeysConverter = TypeDescriptor.GetConverter(typeof(System.Windows.Input.ModifierKeys));
- if (null != modifierKeysConverter)
- {
- object modifierKeys = modifierKeysConverter.ConvertFrom(context, culture, modifiersToken);
-
- if (modifierKeys != null && modifierKeys is ModifierKeys)
- {
- return new MouseGesture((MouseAction)mouseAction, (ModifierKeys)modifierKeys);
- }
- }
- }
- else
- {
- return new MouseGesture((MouseAction)mouseAction);
- }
- }
- }
- }
- throw GetConvertFromException(source);
+ // We can convert to an InstanceDescriptor or to a string.
+ 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 MouseGesture mouseGesture)
+ return false;
+
+ return ModifierKeysConverter.IsDefinedModifierKeys(mouseGesture.Modifiers) &&
+ MouseActionConverter.IsDefinedMouseAction(mouseGesture.MouseAction);
}
- ///
- ///TypeConverter method override.
- ///
- ///ITypeDescriptorContext
- ///Type to convert to
- ///true if conversion is possible
- public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ ///
+ /// 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)
{
- // We can convert to an InstanceDescriptor or to a string.
- if (destinationType == typeof(string))
+ if (source is not string sourceString)
+ throw GetConvertFromException(source);
+
+ ReadOnlySpan trimmedSource = sourceString.AsSpan().Trim();
+
+ // Break apart MouseAction and ModifierKeys
+ int index = trimmedSource.LastIndexOf('+');
+ if (index >= 0)
{
- // When invoked by the serialization engine we can convert to string only for known type
- if (context != null && context.Instance != null)
- {
- MouseGesture mouseGesture = context.Instance as MouseGesture;
- if (mouseGesture != null)
- {
- return (ModifierKeysConverter.IsDefinedModifierKeys(mouseGesture.Modifiers)
- && MouseActionConverter.IsDefinedMouseAction(mouseGesture.MouseAction));
- }
- }
+ ReadOnlySpan mouseActionToken = trimmedSource.Slice(index + 1).Trim();
+ ReadOnlySpan modifiersToken = trimmedSource.Slice(0, index).Trim();
+
+ return new MouseGesture(MouseActionConverter.ConvertFromImpl(mouseActionToken), ModifierKeysConverter.ConvertFromImpl(modifiersToken));
}
- return false;
+
+ return new MouseGesture(MouseActionConverter.ConvertFromImpl(trimmedSource), ModifierKeys.None);
}
///
- /// ConvertTo()
+ /// Attempts to convert a object to the .
///
- /// Serialization Context
- /// Culture Info
- /// MouseGesture value
- /// Type to Convert
- /// string if parameter is a MouseGesture
+ /// 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);
- if (destinationType == typeof(string))
- {
- if (value == null)
- return String.Empty;
-
- MouseGesture mouseGesture = value as MouseGesture;
- if (mouseGesture != null)
- {
- string strGesture = "";
-
- TypeConverter modifierKeysConverter = TypeDescriptor.GetConverter(typeof(System.Windows.Input.ModifierKeys));
- if (null != modifierKeysConverter)
- {
- strGesture += modifierKeysConverter.ConvertTo(context, culture, mouseGesture.Modifiers, destinationType) as string;
- if (strGesture != String.Empty)
- {
- strGesture += MODIFIERS_DELIMITER ;
- }
- }
- TypeConverter mouseActionConverter = TypeDescriptor.GetConverter(typeof(System.Windows.Input.MouseAction));
- if (null != mouseActionConverter)
- {
- strGesture += mouseActionConverter.ConvertTo(context, culture, mouseGesture.MouseAction, destinationType) as string;
- }
- return strGesture;
- }
- }
- throw GetConvertToException(value,destinationType);
+ 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 MouseGesture mouseGesture)
+ throw GetConvertToException(value, destinationType);
+
+ string mouseAction = (string)s_mouseActionConverter.ConvertTo(context, culture, mouseGesture.MouseAction, destinationType);
+
+ // This returns the converted MouseAction only
+ if (mouseGesture.Modifiers is ModifierKeys.None)
+ return mouseAction;
+
+ ReadOnlySpan modifierSpan = ModifierKeysConverter.ConvertMultipleModifiers(mouseGesture.Modifiers, stackalloc char[22]);
+
+ // This will result in "Ctrl+" in case MouseAction was None but that's fine
+ return string.Create(CultureInfo.InvariantCulture, stackalloc char[42], $"{modifierSpan}+{mouseAction}");
}
}
}
-
-
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..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
@@ -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.
@@ -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;
@@ -123,10 +135,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 +175,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 +190,7 @@ static void AppendWithDelimiter(string literal, ref int totalLength, ref Span