diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/App.xaml b/src/modules/keyboardmanager/KeyboardManagerEditorUI/App.xaml
index b5119bcf57..7bb01c0386 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/App.xaml
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/App.xaml
@@ -8,10 +8,13 @@
-
-
+
+
+
+
+ 960
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/InputControl.xaml
similarity index 95%
rename from src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml
rename to src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/InputControl.xaml
index 4fb953de49..22f8f6ab64 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/InputControl.xaml
@@ -1,12 +1,12 @@
@@ -43,7 +43,7 @@
-
+
@@ -79,7 +79,7 @@
-
+
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/InputControl.xaml.cs
similarity index 99%
rename from src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml.cs
rename to src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/InputControl.xaml.cs
index 1c7760d4fa..46b3a873ed 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml.cs
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/InputControl.xaml.cs
@@ -14,7 +14,7 @@ using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Windows.System;
-namespace KeyboardManagerEditorUI.Styles
+namespace KeyboardManagerEditorUI.Controls
{
public sealed partial class InputControl : UserControl, IDisposable, IKeyboardHookTarget
{
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyCharPresenter.xaml b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyCharPresenter.xaml
new file mode 100644
index 0000000000..2c6bf72292
--- /dev/null
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyCharPresenter.xaml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyCharPresenter.xaml.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyCharPresenter.xaml.cs
new file mode 100644
index 0000000000..0457e715ee
--- /dev/null
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyCharPresenter.xaml.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Documents;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+
+namespace KeyboardManagerEditorUI.Controls;
+
+public sealed partial class KeyCharPresenter : Control
+{
+ public KeyCharPresenter()
+ {
+ DefaultStyleKey = typeof(KeyCharPresenter);
+ }
+
+ public object Content
+ {
+ get => (object)GetValue(ContentProperty);
+ set => SetValue(ContentProperty, value);
+ }
+
+ public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(object), typeof(KeyCharPresenter), new PropertyMetadata(default(string)));
+}
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyVisual.xaml b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyVisual.xaml
new file mode 100644
index 0000000000..9eed022582
--- /dev/null
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyVisual.xaml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyVisual.xaml.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyVisual.xaml.cs
new file mode 100644
index 0000000000..258b221107
--- /dev/null
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/KeyVisual/KeyVisual.xaml.cs
@@ -0,0 +1,168 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Windows.System;
+
+namespace KeyboardManagerEditorUI.Controls
+{
+ [TemplatePart(Name = KeyPresenter, Type = typeof(KeyCharPresenter))]
+ [TemplateVisualState(Name = NormalState, GroupName = "CommonStates")]
+ [TemplateVisualState(Name = DisabledState, GroupName = "CommonStates")]
+ [TemplateVisualState(Name = InvalidState, GroupName = "CommonStates")]
+ public sealed partial class KeyVisual : Control
+ {
+ private const string KeyPresenter = "KeyPresenter";
+ private const string NormalState = "Normal";
+ private const string DisabledState = "Disabled";
+ private const string InvalidState = "Invalid";
+ private KeyCharPresenter _keyPresenter;
+
+ public object Content
+ {
+ get => (object)GetValue(ContentProperty);
+ set => SetValue(ContentProperty, value);
+ }
+
+ public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(object), typeof(KeyVisual), new PropertyMetadata(default(string), OnContentChanged));
+
+ public bool IsInvalid
+ {
+ get => (bool)GetValue(IsInvalidProperty);
+ set => SetValue(IsInvalidProperty, value);
+ }
+
+ public static readonly DependencyProperty IsInvalidProperty = DependencyProperty.Register(nameof(IsInvalid), typeof(bool), typeof(KeyVisual), new PropertyMetadata(false, OnIsInvalidChanged));
+
+ public bool RenderKeyAsGlyph
+ {
+ get => (bool)GetValue(RenderKeyAsGlyphProperty);
+ set => SetValue(RenderKeyAsGlyphProperty, value);
+ }
+
+ public static readonly DependencyProperty RenderKeyAsGlyphProperty = DependencyProperty.Register(nameof(RenderKeyAsGlyph), typeof(bool), typeof(KeyVisual), new PropertyMetadata(false, OnContentChanged));
+
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
+ public KeyVisual()
+#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
+ {
+ this.DefaultStyleKey = typeof(KeyVisual);
+ }
+
+ protected override void OnApplyTemplate()
+ {
+ IsEnabledChanged -= KeyVisual_IsEnabledChanged;
+ _keyPresenter = (KeyCharPresenter)this.GetTemplateChild(KeyPresenter);
+ Update();
+ SetVisualStates();
+ IsEnabledChanged += KeyVisual_IsEnabledChanged;
+ base.OnApplyTemplate();
+ }
+
+ private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((KeyVisual)d).SetVisualStates();
+ }
+
+ private static void OnIsInvalidChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((KeyVisual)d).SetVisualStates();
+ }
+
+ private void SetVisualStates()
+ {
+ if (this != null)
+ {
+ if (IsInvalid)
+ {
+ VisualStateManager.GoToState(this, InvalidState, true);
+ }
+ else if (!IsEnabled)
+ {
+ VisualStateManager.GoToState(this, DisabledState, true);
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, NormalState, true);
+ }
+ }
+ }
+
+ private void Update()
+ {
+ if (Content == null)
+ {
+ return;
+ }
+
+ if (Content is string)
+ {
+ _keyPresenter.Style = (Style)Application.Current.Resources["DefaultKeyCharPresenterStyle"];
+ return;
+ }
+
+ if (Content is int keyCode)
+ {
+ VirtualKey virtualKey = (VirtualKey)keyCode;
+ switch (virtualKey)
+ {
+ case VirtualKey.Enter:
+ SetGlyphOrText("\uE751", virtualKey);
+ break;
+
+ case VirtualKey.Back:
+ SetGlyphOrText("\uE750", virtualKey);
+ break;
+
+ case VirtualKey.Shift:
+ case (VirtualKey)160: // Left Shift
+ case (VirtualKey)161: // Right Shift
+ SetGlyphOrText("\uE752", virtualKey);
+ break;
+
+ case VirtualKey.Up:
+ _keyPresenter.Content = "\uE0E4";
+ break;
+
+ case VirtualKey.Down:
+ _keyPresenter.Content = "\uE0E5";
+ break;
+
+ case VirtualKey.Left:
+ _keyPresenter.Content = "\uE0E2";
+ break;
+
+ case VirtualKey.Right:
+ _keyPresenter.Content = "\uE0E3";
+ break;
+
+ case VirtualKey.LeftWindows:
+ case VirtualKey.RightWindows:
+ _keyPresenter.Style = (Style)Application.Current.Resources["WindowsKeyCharPresenterStyle"];
+ break;
+ }
+ }
+ }
+
+ private void SetGlyphOrText(string glyph, VirtualKey key)
+ {
+ if (RenderKeyAsGlyph)
+ {
+ _keyPresenter.Content = glyph;
+ _keyPresenter.Style = (Style)Application.Current.Resources["GlyphKeyCharPresenterStyle"];
+ }
+ else
+ {
+ _keyPresenter.Content = key.ToString();
+ _keyPresenter.Style = (Style)Application.Current.Resources["DefaultKeyCharPresenterStyle"];
+ }
+ }
+
+ private void KeyVisual_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ SetVisualStates();
+ }
+ }
+}
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/TextPageInputControl.xaml b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/TextPageInputControl.xaml
similarity index 93%
rename from src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/TextPageInputControl.xaml
rename to src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/TextPageInputControl.xaml
index 35c25fdcf2..9f77680a96 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/TextPageInputControl.xaml
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/TextPageInputControl.xaml
@@ -1,12 +1,12 @@
@@ -19,7 +19,6 @@
-
+
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/TextPageInputControl.xaml.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/TextPageInputControl.xaml.cs
similarity index 99%
rename from src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/TextPageInputControl.xaml.cs
rename to src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/TextPageInputControl.xaml.cs
index 1d2a28c8d7..7fe19b86d5 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/TextPageInputControl.xaml.cs
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/TextPageInputControl.xaml.cs
@@ -10,7 +10,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.System;
-namespace KeyboardManagerEditorUI.Styles
+namespace KeyboardManagerEditorUI.Controls
{
public sealed partial class TextPageInputControl : UserControl, IKeyboardHookTarget
{
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/KeyboardManagerEditorUI.csproj b/src/modules/keyboardmanager/KeyboardManagerEditorUI/KeyboardManagerEditorUI.csproj
index 58c0332b5b..ee67217975 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/KeyboardManagerEditorUI.csproj
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/KeyboardManagerEditorUI.csproj
@@ -27,10 +27,6 @@
-
-
-
-
@@ -59,12 +55,7 @@
-
- MSBuild:Compile
-
-
-
-
+
MSBuild:Compile
@@ -89,14 +80,7 @@
-
- MSBuild:Compile
-
-
-
-
- MSBuild:Compile
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-