From 288b8b804d7b53f6f8f0ee3f0cd5f6b0fe3582e2 Mon Sep 17 00:00:00 2001
From: Mohamed Hazem <72768725+mou-haz@users.noreply.github.com>
Date: Fri, 11 Jul 2025 04:33:52 +0300
Subject: [PATCH 1/5] Improved focus visuals
timepicker focus visual
improved calender view focus visuals
assign focus visual target
focus visual target for:
hyperlink button
radio button
button
command bar
menu items
toggle switch
improve focus visuals
toggle switch
command bar
updated corner radius
fix button focus visual
Fix FocusVisual clipped by scrollviewer
fix settingscard clipped
tree view focus visuals
improved focus visuals
radio menu items focus visuals
menu items focus visuals
tabitem focus visuals fix
treeview focus visuals
generalizing AdornerDecorator
context menu item focus visuals
combobox focus visuals
---
.../SettingsCard/SettingsCard.xaml | 7 +-
.../SettingsExpander/SettingsExpander.xaml | 7 +-
.../Windows/CommandBar/CommandBar.xaml | 8 +-
.../HyperlinkButton/HyperlinkButton.xaml | 1 +
.../Windows/RadioMenuItem/RadioMenuItem.xaml | 1 +
.../Windows/TimePicker/TimePicker.xaml | 6 +-
.../Windows/ToggleSwitch/ToggleSwitch.xaml | 4 +-
.../Controls/ControlExample.xaml | 1 +
.../Controls/Helpers/FocusVisualHelper.cs | 80 ++++++++++++++++++-
.../Themes/Controls/Calendar.xaml | 60 +++++++++++---
.../Themes/Controls/ComboBox.xaml | 2 +-
.../Themes/Controls/CommandBar.xaml | 1 +
.../Themes/Controls/Expander.xaml | 3 +
.../Themes/Controls/MenuItem.xaml | 1 +
.../Themes/Controls/NavigationBackButton.xaml | 1 +
.../Themes/Controls/NavigationView.xaml | 1 +
.../Themes/Controls/RadioButton.xaml | 1 +
.../Themes/Controls/ScrollViewer.xaml | 50 +++++++-----
.../Themes/Controls/TreeView.xaml | 6 ++
.../Themes/Schemes/Dark.xaml | 11 ++-
.../Themes/Schemes/HighContrast.xaml | 15 ++--
.../Themes/Schemes/Light.xaml | 11 ++-
.../Themes/ThemeResources.xaml | 1 +
23 files changed, 215 insertions(+), 64 deletions(-)
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsCard/SettingsCard.xaml b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsCard/SettingsCard.xaml
index f37718d0..ee325ca3 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsCard/SettingsCard.xaml
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsCard/SettingsCard.xaml
@@ -78,7 +78,7 @@
-
+ CornerRadius="{TemplateBinding ui:ControlHelper.CornerRadius}"
+ ui:FocusVisualHelper.IsTemplateFocusTarget="True">
@@ -382,7 +383,7 @@
ToolTipService.ToolTip="{TemplateBinding ActionIconToolTip}" />
-
+
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml
index 7de30115..7f57e51a 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml
@@ -53,13 +53,14 @@
-
+ SnapsToDevicePixels="True"
+ ui:FocusVisualHelper.IsTemplateFocusTarget="True">
-
+
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/CommandBar/CommandBar.xaml b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/CommandBar/CommandBar.xaml
index 7a21957e..be234527 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/CommandBar/CommandBar.xaml
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/CommandBar/CommandBar.xaml
@@ -48,6 +48,7 @@
-->
+ Glyph="" />
+
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/TimePicker/TimePicker.xaml b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/TimePicker/TimePicker.xaml
index e5050ad5..42f580fb 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/TimePicker/TimePicker.xaml
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Windows/TimePicker/TimePicker.xaml
@@ -80,9 +80,9 @@
-
+
@@ -99,7 +99,7 @@
Focusable="false"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Hidden">
-
+
+
@@ -123,6 +124,8 @@
Visibility="Collapsed" />
@@ -144,7 +147,6 @@
Grid.RowSpan="3"
Grid.ColumnSpan="5"
Margin="0,5"
- ui:FocusVisualHelper.IsTemplateFocusTarget="True"
Background="{DynamicResource ToggleSwitchContainerBackground}"
CornerRadius="{TemplateBinding CornerRadius}" />
1000
+
-
+
diff --git a/source/iNKORE.UI.WPF.Modern/Themes/Schemes/HighContrast.xaml b/source/iNKORE.UI.WPF.Modern/Themes/Schemes/HighContrast.xaml
index d1999fa8..033f5985 100644
--- a/source/iNKORE.UI.WPF.Modern/Themes/Schemes/HighContrast.xaml
+++ b/source/iNKORE.UI.WPF.Modern/Themes/Schemes/HighContrast.xaml
@@ -2563,7 +2563,7 @@
1
4,2
- 0,3,0,3
+ 0,5,0,5
1
28
28
@@ -2643,16 +2643,19 @@
-
+ BorderBrush="{TemplateBinding chelper:FocusVisualHelper.FocusVisualPrimaryBrush}"
+ BorderThickness="{TemplateBinding chelper:FocusVisualHelper.FocusVisualPrimaryThickness}"
+ SnapsToDevicePixels="True"
+ CornerRadius="{TemplateBinding chelper:FocusVisualHelper.FocusVisualPrimaryCornerRadius}">
+
-
+
\ No newline at end of file
diff --git a/source/iNKORE.UI.WPF.Modern/Themes/Schemes/Light.xaml b/source/iNKORE.UI.WPF.Modern/Themes/Schemes/Light.xaml
index 9927217e..bff6a903 100644
--- a/source/iNKORE.UI.WPF.Modern/Themes/Schemes/Light.xaml
+++ b/source/iNKORE.UI.WPF.Modern/Themes/Schemes/Light.xaml
@@ -2682,7 +2682,7 @@
0
4,2
- 0,3,0,3
+ 0,5,0,5
0
28
28
@@ -2763,15 +2763,18 @@
-
+ SnapsToDevicePixels="True"
+ CornerRadius="{TemplateBinding chelper:FocusVisualHelper.FocusVisualPrimaryCornerRadius}">
+
-
+
#947EEC
diff --git a/source/iNKORE.UI.WPF.Modern/Themes/ThemeResources.xaml b/source/iNKORE.UI.WPF.Modern/Themes/ThemeResources.xaml
index f0fd67cf..75e00474 100644
--- a/source/iNKORE.UI.WPF.Modern/Themes/ThemeResources.xaml
+++ b/source/iNKORE.UI.WPF.Modern/Themes/ThemeResources.xaml
@@ -549,6 +549,7 @@
34
0.875
+ 354
4,2,4,2
0
From 96b10186b22cf7d3e81a27b9ace98c885c11657b Mon Sep 17 00:00:00 2001
From: Mohamed Hazem <72768725+mou-haz@users.noreply.github.com>
Date: Thu, 18 Sep 2025 19:27:11 +0300
Subject: [PATCH 2/5] calendarview tab focus visuals
---
.../Themes/Controls/Calendar.xaml | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml b/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml
index 3032a79a..4a205944 100644
--- a/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml
+++ b/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml
@@ -464,6 +464,9 @@
+
+
+
@@ -536,6 +539,9 @@
+
+
+
@@ -628,7 +634,7 @@
-
+
@@ -687,6 +693,8 @@
+
+
@@ -739,6 +747,10 @@
+
+
+
+
@@ -857,6 +869,8 @@
+
+
From 0f39d5130cdb2d40d8bdd424d7a3c299501cdb67 Mon Sep 17 00:00:00 2001
From: Mohamed Hazem <72768725+mou-haz@users.noreply.github.com>
Date: Fri, 19 Sep 2025 01:52:58 +0300
Subject: [PATCH 3/5] CalendarView keyboard focus behavior
---
.../Controls/Helpers/CalendarHelper.cs | 212 +++++++++++++++++-
.../Themes/Controls/Calendar.xaml | 23 +-
2 files changed, 229 insertions(+), 6 deletions(-)
diff --git a/source/iNKORE.UI.WPF.Modern/Controls/Helpers/CalendarHelper.cs b/source/iNKORE.UI.WPF.Modern/Controls/Helpers/CalendarHelper.cs
index 49ba66db..43d77046 100644
--- a/source/iNKORE.UI.WPF.Modern/Controls/Helpers/CalendarHelper.cs
+++ b/source/iNKORE.UI.WPF.Modern/Controls/Helpers/CalendarHelper.cs
@@ -1,7 +1,12 @@
-using System.Windows;
+using System;
+using System.Globalization;
+using System.Reflection;
+using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
+using System.Windows.Data;
using System.Windows.Input;
+using Calendar = System.Windows.Controls.Calendar;
namespace iNKORE.UI.WPF.Modern.Controls.Helpers
{
@@ -52,5 +57,210 @@ private static void OnCalendarGotMouseCapture(object sender, MouseEventArgs e)
}
}
}
+
+ #region CalendarKeyboardBehaviorOverride
+
+ public static bool GetKeyboardBehaviorOverride(Calendar calendar) =>
+ (bool)calendar.GetValue(KeyboardBehaviorOverrideProperty);
+
+ public static void SetKeyboardBehaviorOverride(Calendar calendar, bool value) =>
+ calendar.SetValue(KeyboardBehaviorOverrideProperty, value);
+
+ public static readonly DependencyProperty KeyboardBehaviorOverrideProperty =
+ DependencyProperty.RegisterAttached(
+ "KeyboardBehaviorOverride",
+ typeof(bool),
+ typeof(CalendarHelper),
+ new PropertyMetadata(OnKeyboardBehaviorOverrideChanged));
+
+ private static void OnKeyboardBehaviorOverrideChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is not Calendar calendar)
+ {
+ return;
+ }
+
+ if (e.NewValue is true)
+ {
+ calendar.PreviewKeyDown += OnHandleChangeFocusByKeys;
+ }
+ else
+ {
+ calendar.PreviewKeyDown -= OnHandleChangeFocusByKeys;
+ }
+ }
+
+ private static void OnHandleChangeFocusByKeys(object sender, KeyEventArgs e)
+ {
+ if (sender is not Calendar calendar ||
+ (Keyboard.Modifiers & ModifierKeys.Control) is ModifierKeys.Control)
+ {
+ return;
+ }
+
+ if ((Keyboard.Modifiers & ModifierKeys.Shift) is ModifierKeys.Shift && e.Key is not Key.Tab)
+ {
+ e.Handled = true;
+ return;
+ }
+
+ DateTime? focusTarget;
+ switch (calendar.DisplayMode)
+ {
+ case CalendarMode.Month:
+ int? change = e.Key switch
+ {
+ Key.Up => -7,
+ Key.Down => 7,
+ Key.Left => -1,
+ Key.Right => 1,
+ _ => null
+ };
+
+ if (change is null)
+ {
+ return;
+ }
+
+ var currentDate = typeof(Calendar).GetProperty("CurrentDate",
+ BindingFlags.NonPublic | BindingFlags.Instance)!
+ .GetValue(calendar)
+ as DateTime?;
+
+ focusTarget = GetNonBlackoutTarget(calendar, currentDate?.AddDays(change.Value));
+
+ break;
+
+ case CalendarMode.Year:
+ if (GetChangeForYearOrDecadeView(e.Key) is not { } monthChange)
+ {
+ return;
+ }
+
+ focusTarget = calendar.DisplayDate.AddMonths(monthChange);
+ break;
+ case CalendarMode.Decade:
+ if (GetChangeForYearOrDecadeView(e.Key) is not { } yearChange)
+ {
+ return;
+ }
+
+ focusTarget = calendar.DisplayDate.AddYears(yearChange);
+ break;
+ default:
+ return;
+ }
+
+ typeof(Calendar).GetMethod("MoveDisplayTo", BindingFlags.NonPublic | BindingFlags.Instance)!
+ .Invoke(calendar,
+ [focusTarget]);
+
+ e.Handled = true;
+
+ static int? GetChangeForYearOrDecadeView(Key key) => key switch
+ {
+ Key.Up => -4,
+ Key.Down => 4,
+ Key.Left => -1,
+ Key.Right => 1,
+ _ => null
+ };
+
+ static DateTime? GetNonBlackoutTarget(Calendar calendar, DateTime? targetFocus)
+ {
+ var blackoutDates =
+ typeof(Calendar).GetField("_blackoutDates", BindingFlags.NonPublic | BindingFlags.Instance)!
+ .GetValue(calendar)
+ as CalendarBlackoutDatesCollection;
+
+ var toSelectDate =
+ typeof(CalendarBlackoutDatesCollection).GetMethod("GetNonBlackoutDate",
+ BindingFlags.NonPublic | BindingFlags.Instance)!
+ .Invoke(blackoutDates, [targetFocus, -1]) as DateTime?;
+ return toSelectDate;
+ }
+ }
+
+ private static DateTime? GetCurrentFocusedDate() =>
+ (Keyboard.FocusedElement as FrameworkElement)?.DataContext as DateTime?;
+
+ #endregion
+
+ #region ContainsToday
+
+ public static CalendarMode? GetCalendarDisplayMode(DependencyObject obj) => (CalendarMode?)obj.GetValue(CalendarDisplayModeProperty);
+
+ public static void SetCalendarDisplayMode(DependencyObject obj, CalendarMode? value) => obj.SetValue(CalendarDisplayModeProperty, value);
+
+ public static readonly DependencyProperty CalendarDisplayModeProperty =
+ DependencyProperty.RegisterAttached(
+ "CalendarDisplayMode",
+ typeof(CalendarMode?),
+ typeof(CalendarHelper),
+ new PropertyMetadata(null, OnContainsTodayNeedsUpdate));
+
+ public static object GetContextDate(DependencyObject obj) => obj.GetValue(ContextDateProperty);
+
+ public static void SetContextDate(DependencyObject obj, object value) => obj.SetValue(ContextDateProperty, value);
+
+ public static readonly DependencyProperty ContextDateProperty =
+ DependencyProperty.RegisterAttached(
+ "ContextDate",
+ typeof(object),
+ typeof(CalendarHelper),
+ new PropertyMetadata(null, OnContainsTodayNeedsUpdate));
+
+ public static bool GetIsContainsTodayActive(DependencyObject obj) => (bool)obj.GetValue(IsContainsTodayActiveProperty);
+
+ public static void SetIsContainsTodayActive(DependencyObject obj, bool value) => obj.SetValue(IsContainsTodayActiveProperty, value);
+
+ public static readonly DependencyProperty IsContainsTodayActiveProperty =
+ DependencyProperty.RegisterAttached(
+ "IsContainsTodayActive",
+ typeof(bool),
+ typeof(CalendarHelper),
+ new PropertyMetadata(true, OnContainsTodayNeedsUpdate));
+
+ private static void OnContainsTodayNeedsUpdate(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ UpdateContainsTodayActive(d);
+ }
+
+ private static void UpdateContainsTodayActive(DependencyObject d)
+ {
+ var mode = GetCalendarDisplayMode(d);
+ var contextDate = GetContextDate(d) as DateTime?;
+
+ if (!mode.HasValue || !contextDate.HasValue)
+ {
+ SetContainsToday(d, false);
+ return;
+ }
+
+ var containsTodayActive = mode.Value switch
+ {
+ CalendarMode.Year => contextDate.Value.Year == DateTime.Today.Year &&
+ contextDate.Value.Month == DateTime.Today.Month,
+ CalendarMode.Decade => contextDate.Value.Year == DateTime.Today.Year,
+ _ => false
+ };
+
+ SetContainsToday(d, containsTodayActive);
+ }
+
+ public static bool GetContainsToday(DependencyObject obj) =>
+ (bool)obj.GetValue(ContainsTodayProperty);
+
+ public static void SetContainsToday(DependencyObject obj, bool value) =>
+ obj.SetValue(ContainsTodayProperty, value);
+
+ public static readonly DependencyProperty ContainsTodayProperty =
+ DependencyProperty.RegisterAttached(
+ "ContainsToday",
+ typeof(bool),
+ typeof(CalendarHelper),
+ new PropertyMetadata(false));
+
+ #endregion
}
}
diff --git a/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml b/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml
index 4a205944..2afbaab4 100644
--- a/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml
+++ b/source/iNKORE.UI.WPF.Modern/Themes/Controls/Calendar.xaml
@@ -818,6 +818,9 @@
+
+
+
@@ -868,30 +871,39 @@
-
+
+
+
+
+
+
+
-
+
-
+
+
-
+
+
-
+
+
@@ -937,6 +949,7 @@
+
From 4a2943569ac1bddd34b9769b9d89e7f99215c078 Mon Sep 17 00:00:00 2001
From: Mohamed Hazem <72768725+mou-haz@users.noreply.github.com>
Date: Tue, 23 Sep 2025 15:54:40 +0300
Subject: [PATCH 4/5] update ErrorTextBlock to match winui
fixes the problem that the hyperlink (even when textblock is hidden) was stealing the focus, giving unexpected focus switch results.
---
source/iNKORE.UI.WPF.Modern.Gallery/Controls/ControlExample.xaml | 1 -
1 file changed, 1 deletion(-)
diff --git a/source/iNKORE.UI.WPF.Modern.Gallery/Controls/ControlExample.xaml b/source/iNKORE.UI.WPF.Modern.Gallery/Controls/ControlExample.xaml
index 0258b181..6ae737a9 100644
--- a/source/iNKORE.UI.WPF.Modern.Gallery/Controls/ControlExample.xaml
+++ b/source/iNKORE.UI.WPF.Modern.Gallery/Controls/ControlExample.xaml
@@ -38,7 +38,6 @@
This sample requires a later version of Windows to be fully functional. Learn more about version adaptive apps:
- https://docs.microsoft.com/windows/uwp/debug-test-perf/version-adaptive-apps
From 40c9a03be4b1056cc5b4dd946d1aa42e2e012efc Mon Sep 17 00:00:00 2001
From: Mohamed Hazem <72768725+mou-haz@users.noreply.github.com>
Date: Tue, 4 Nov 2025 15:27:34 +0200
Subject: [PATCH 5/5] fix: revert settingsexpander to using ElevationBorder
---
.../SettingsControls/SettingsExpander/SettingsExpander.xaml | 4 ++--
source/iNKORE.UI.WPF.Modern/Themes/Generic.xaml | 2 ++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml
index 7f57e51a..64b4440b 100644
--- a/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml
+++ b/source/iNKORE.UI.WPF.Modern.Controls/Controls/Community/SettingsControls/SettingsExpander/SettingsExpander.xaml
@@ -53,7 +53,7 @@
-
-
+
diff --git a/source/iNKORE.UI.WPF.Modern/Themes/Generic.xaml b/source/iNKORE.UI.WPF.Modern/Themes/Generic.xaml
index d0b3f35e..09fd36e4 100644
--- a/source/iNKORE.UI.WPF.Modern/Themes/Generic.xaml
+++ b/source/iNKORE.UI.WPF.Modern/Themes/Generic.xaml
@@ -4,6 +4,7 @@
xmlns:common="clr-namespace:iNKORE.UI.WPF.Modern.Common"
xmlns:converters="clr-namespace:iNKORE.UI.WPF.Modern.Common.Converters"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
+ xmlns:helpers="clr-namespace:iNKORE.UI.WPF.Modern.Controls.Helpers"
xmlns:primitives="clr-namespace:iNKORE.UI.WPF.Modern.Controls.Primitives">
@@ -27,6 +28,7 @@