From aa3698fd80a13a63fde1221e5e29b0310c9f5feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 26 Jul 2022 18:55:25 +0200 Subject: [PATCH] [Windows] Fix DatePicker TextColor property (#7995) * Fix Windows DatePicker TextColor * Updated implementation * Added Windows DatePicker DeviceTests * - remove public API changes * - clean up handlers * - fix ext methods * - fix windows attach tests * Update PublicAPI.Unshipped.txt * - remove ToDateTimeOffset ext for now Co-authored-by: Shane Neuville --- .../DatePicker/DatePickerHandler.Android.cs | 3 +- .../DatePicker/DatePickerHandler.Windows.cs | 21 ++++--- .../Handlers/DatePicker/DatePickerHandler.cs | 4 +- .../TimePicker/TimePickerHandler.Windows.cs | 17 ++++-- .../Handlers/TimePicker/TimePickerHandler.cs | 8 +-- .../Platform/Windows/DatePickerExtensions.cs | 58 ++++++++++++++++++- .../src/Platform/Windows/PickerExtensions.cs | 17 +++--- .../Platform/Windows/TimePickerExtensions.cs | 52 ++++++++++++++--- .../tests/DeviceTests/Core.DeviceTests.csproj | 1 + .../DatePickerHandlerTests.Android.cs | 3 - .../DatePickerHandlerTests.Windows.cs | 33 +++++++++++ .../DatePicker/DatePickerHandlerTests.cs | 3 - .../DatePicker/DatePickerHandlerTests.iOS.cs | 3 - .../AssertionExtensions.Windows.cs | 19 +++++- 14 files changed, 191 insertions(+), 51 deletions(-) create mode 100644 src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Windows.cs diff --git a/src/Core/src/Handlers/DatePicker/DatePickerHandler.Android.cs b/src/Core/src/Handlers/DatePicker/DatePickerHandler.Android.cs index 6cc3cceade86..9baf18fc711a 100644 --- a/src/Core/src/Handlers/DatePicker/DatePickerHandler.Android.cs +++ b/src/Core/src/Handlers/DatePicker/DatePickerHandler.Android.cs @@ -80,8 +80,7 @@ protected virtual DatePickerDialog CreateDatePickerDialog(int year, int month, i // This is a Android-specific mapping public static void MapBackground(IDatePickerHandler handler, IDatePicker datePicker) { - if (handler is DatePickerHandler platformHandler) - handler.PlatformView?.UpdateBackground(datePicker); + handler.PlatformView?.UpdateBackground(datePicker); } public static void MapFormat(IDatePickerHandler handler, IDatePicker datePicker) diff --git a/src/Core/src/Handlers/DatePicker/DatePickerHandler.Windows.cs b/src/Core/src/Handlers/DatePicker/DatePickerHandler.Windows.cs index e29a88329376..3c68607d3a07 100644 --- a/src/Core/src/Handlers/DatePicker/DatePickerHandler.Windows.cs +++ b/src/Core/src/Handlers/DatePicker/DatePickerHandler.Windows.cs @@ -20,40 +20,39 @@ protected override void DisconnectHandler(CalendarDatePicker platformView) public static void MapFormat(IDatePickerHandler handler, IDatePicker datePicker) { - handler.PlatformView?.UpdateDate(datePicker); + handler.PlatformView.UpdateDate(datePicker); } public static void MapDate(IDatePickerHandler handler, IDatePicker datePicker) { - handler.PlatformView?.UpdateDate(datePicker); + handler.PlatformView.UpdateDate(datePicker); } public static void MapMinimumDate(IDatePickerHandler handler, IDatePicker datePicker) { - handler.PlatformView?.UpdateMinimumDate(datePicker); + handler.PlatformView.UpdateMinimumDate(datePicker); } public static void MapMaximumDate(IDatePickerHandler handler, IDatePicker datePicker) { - handler.PlatformView?.UpdateMaximumDate(datePicker); + handler.PlatformView.UpdateMaximumDate(datePicker); } public static void MapCharacterSpacing(IDatePickerHandler handler, IDatePicker datePicker) { - handler.PlatformView?.UpdateCharacterSpacing(datePicker); + handler.PlatformView.UpdateCharacterSpacing(datePicker); } public static void MapFont(IDatePickerHandler handler, IDatePicker datePicker) { var fontManager = handler.GetRequiredService(); - handler.PlatformView?.UpdateFont(datePicker, fontManager); + handler.PlatformView.UpdateFont(datePicker, fontManager); } public static void MapTextColor(IDatePickerHandler handler, IDatePicker datePicker) { - if (handler is DatePickerHandler platformHandler) - handler.PlatformView?.UpdateTextColor(datePicker); + handler.PlatformView.UpdateTextColor(datePicker); } private void DateChanged(CalendarDatePicker sender, CalendarDatePickerDateChangedEventArgs args) @@ -81,5 +80,11 @@ private void DateChanged(CalendarDatePicker sender, CalendarDatePickerDateChange VirtualView.Date = args.NewDate.Value.Date; } + + // TODO NET7 add to public API + internal static void MapBackground(IDatePickerHandler handler, IDatePicker datePicker) + { + handler.PlatformView?.UpdateBackground(datePicker); + } } } \ No newline at end of file diff --git a/src/Core/src/Handlers/DatePicker/DatePickerHandler.cs b/src/Core/src/Handlers/DatePicker/DatePickerHandler.cs index dab59d8d0d70..cf1b639cd7bc 100644 --- a/src/Core/src/Handlers/DatePicker/DatePickerHandler.cs +++ b/src/Core/src/Handlers/DatePicker/DatePickerHandler.cs @@ -18,9 +18,9 @@ public partial class DatePickerHandler : IDatePickerHandler { public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper) { -#if __ANDROID__ +#if ANDROID || WINDOWS [nameof(IDatePicker.Background)] = MapBackground, -#elif __IOS__ +#elif IOS [nameof(IDatePicker.FlowDirection)] = MapFlowDirection, #endif [nameof(IDatePicker.CharacterSpacing)] = MapCharacterSpacing, diff --git a/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs b/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs index 30f35e6feb65..edcacd2d8c73 100644 --- a/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs +++ b/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs @@ -20,30 +20,35 @@ protected override void DisconnectHandler(TimePicker platformView) public static void MapFormat(ITimePickerHandler handler, ITimePicker timePicker) { - handler.PlatformView?.UpdateTime(timePicker); + handler.PlatformView.UpdateTime(timePicker); } public static void MapTime(ITimePickerHandler handler, ITimePicker timePicker) { - handler.PlatformView?.UpdateTime(timePicker); + handler.PlatformView.UpdateTime(timePicker); } public static void MapCharacterSpacing(ITimePickerHandler handler, ITimePicker timePicker) { - handler.PlatformView?.UpdateCharacterSpacing(timePicker); + handler.PlatformView.UpdateCharacterSpacing(timePicker); } public static void MapFont(ITimePickerHandler handler, ITimePicker timePicker) { var fontManager = handler.GetRequiredService(); - handler.PlatformView?.UpdateFont(timePicker, fontManager); + handler.PlatformView.UpdateFont(timePicker, fontManager); } public static void MapTextColor(ITimePickerHandler handler, ITimePicker timePicker) { - if (handler is TimePickerHandler platformHandler) - handler.PlatformView?.UpdateTextColor(timePicker); + handler.PlatformView.UpdateTextColor(timePicker); + } + + // TODO NET7 make public + internal static void MapBackground(ITimePickerHandler handler, ITimePicker timePicker) + { + handler.PlatformView?.UpdateBackground(timePicker); } void OnControlTimeChanged(object? sender, TimePickerValueChangedEventArgs e) diff --git a/src/Core/src/Handlers/TimePicker/TimePickerHandler.cs b/src/Core/src/Handlers/TimePicker/TimePickerHandler.cs index 1a5bcf462176..518b30b2fed6 100644 --- a/src/Core/src/Handlers/TimePicker/TimePickerHandler.cs +++ b/src/Core/src/Handlers/TimePicker/TimePickerHandler.cs @@ -1,8 +1,8 @@ -#if __IOS__ && !MACCATALYST +#if IOS && !MACCATALYST using PlatformView = Microsoft.Maui.Platform.MauiTimePicker; #elif MACCATALYST using PlatformView = UIKit.UIDatePicker; -#elif MONOANDROID +#elif ANDROID using PlatformView = Microsoft.Maui.Platform.MauiTimePicker; #elif WINDOWS using PlatformView = Microsoft.UI.Xaml.Controls.TimePicker; @@ -18,9 +18,9 @@ public partial class TimePickerHandler : ITimePickerHandler { public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper) { -#if __ANDROID__ +#if ANDROID || WINDOWS [nameof(ITimePicker.Background)] = MapBackground, -#elif __IOS__ +#elif IOS [nameof(ITimePicker.FlowDirection)] = MapFlowDirection, #endif [nameof(ITimePicker.CharacterSpacing)] = MapCharacterSpacing, diff --git a/src/Core/src/Platform/Windows/DatePickerExtensions.cs b/src/Core/src/Platform/Windows/DatePickerExtensions.cs index d34c62287503..c315cc218858 100644 --- a/src/Core/src/Platform/Windows/DatePickerExtensions.cs +++ b/src/Core/src/Platform/Windows/DatePickerExtensions.cs @@ -1,6 +1,7 @@ using System; using Microsoft.Maui.Graphics; using Microsoft.UI.Xaml.Controls; +using WBrush = Microsoft.UI.Xaml.Media.Brush; namespace Microsoft.Maui.Platform { @@ -16,6 +17,8 @@ public static void UpdateDate(this CalendarDatePicker platformDatePicker, IDateP if (!string.IsNullOrEmpty(dateFormat)) platformDatePicker.DateFormat = dateFormat; + + platformDatePicker.UpdateTextColor(datePicker); } public static void UpdateDate(this CalendarDatePicker platformDatePicker, DateTime dateTime) @@ -45,8 +48,57 @@ public static void UpdateTextColor(this CalendarDatePicker platformDatePicker, I { Color textColor = datePicker.TextColor; - if (textColor != null) - platformDatePicker.Foreground = textColor.ToPlatform(); + WBrush? brush = textColor?.ToPlatform(); + + if (brush is null) + { + platformDatePicker.Resources.RemoveKeys(TextColorResourceKeys); + platformDatePicker.ClearValue(CalendarDatePicker.ForegroundProperty); + } + else + { + platformDatePicker.Resources.SetValueForAllKey(TextColorResourceKeys, brush); + platformDatePicker.Foreground = brush; + } + + platformDatePicker.RefreshThemeResources(); + } + + // ResourceKeys controlling the foreground color of the CalendarDatePicker. + // https://docs.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.calendardatepicker?view=windows-app-sdk-1.1 + static readonly string[] TextColorResourceKeys = + { + "CalendarDatePickerTextForeground", + "CalendarDatePickerTextForegroundDisabled", + "CalendarDatePickerTextForegroundSelected" + }; + + // TODO NET7 add to public API + internal static void UpdateBackground(this CalendarDatePicker platformDatePicker, IDatePicker datePicker) + { + var brush = datePicker?.Background?.ToPlatform(); + + if (brush is null) + { + platformDatePicker.Resources.RemoveKeys(BackgroundColorResourceKeys); + platformDatePicker.ClearValue(CalendarDatePicker.BackgroundProperty); + } + else + { + platformDatePicker.Resources.SetValueForAllKey(BackgroundColorResourceKeys, brush); + platformDatePicker.Background = brush; + } + + platformDatePicker.RefreshThemeResources(); } + + static readonly string[] BackgroundColorResourceKeys = + { + "CalendarDatePickerBackground", + "CalendarDatePickerBackgroundPointerOver", + "CalendarDatePickerBackgroundPressed", + "CalendarDatePickerBackgroundDisabled", + "CalendarDatePickerBackgroundFocused", + }; } -} \ No newline at end of file +} diff --git a/src/Core/src/Platform/Windows/PickerExtensions.cs b/src/Core/src/Platform/Windows/PickerExtensions.cs index 9d7bdb6c3c20..8b78f980d008 100644 --- a/src/Core/src/Platform/Windows/PickerExtensions.cs +++ b/src/Core/src/Platform/Windows/PickerExtensions.cs @@ -1,7 +1,6 @@ #nullable enable using Microsoft.Maui.Graphics; using Microsoft.UI.Xaml.Controls; -using WBrush = Microsoft.UI.Xaml.Media.Brush; namespace Microsoft.Maui.Platform { @@ -23,15 +22,15 @@ public static void UpdateBackground(this ComboBox nativeComboBox, IPicker picker if (platformBrush == null) { - nativeComboBox.Resources.RemoveKeys(_backgroundColorResourceKeys); + nativeComboBox.Resources.RemoveKeys(BackgroundColorResourceKeys); } else { - nativeComboBox.Resources.SetValueForAllKey(_backgroundColorResourceKeys, platformBrush); + nativeComboBox.Resources.SetValueForAllKey(BackgroundColorResourceKeys, platformBrush); } } - static readonly string[] _backgroundColorResourceKeys = + static readonly string[] BackgroundColorResourceKeys = { "ComboBoxBackground", "ComboBoxBackgroundPointerOver", @@ -44,18 +43,22 @@ public static void UpdateBackground(this ComboBox nativeComboBox, IPicker picker public static void UpdateTextColor(this ComboBox nativeComboBox, IPicker picker) { var platformBrush = picker.TextColor?.ToPlatform(); + if (platformBrush == null) { - nativeComboBox.Resources.RemoveKeys(_textColorResourceKeys); + nativeComboBox.Resources.RemoveKeys(TextColorResourceKeys); + nativeComboBox.ClearValue(ComboBox.ForegroundProperty); } else { - nativeComboBox.Resources.SetValueForAllKey(_textColorResourceKeys, platformBrush); + nativeComboBox.Resources.SetValueForAllKey(TextColorResourceKeys, platformBrush); nativeComboBox.Foreground = platformBrush; } } - static readonly string[] _textColorResourceKeys = + // ResourceKeys controlling the foreground color of the ComboBox. + // https://docs.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.combobox?view=windows-app-sdk-1.1 + static readonly string[] TextColorResourceKeys = { "ComboBoxForeground", "ComboBoxForegroundDisabled", diff --git a/src/Core/src/Platform/Windows/TimePickerExtensions.cs b/src/Core/src/Platform/Windows/TimePickerExtensions.cs index 742482bb7793..6844ec715379 100644 --- a/src/Core/src/Platform/Windows/TimePickerExtensions.cs +++ b/src/Core/src/Platform/Windows/TimePickerExtensions.cs @@ -30,24 +30,60 @@ public static void UpdateFont(this TimePicker platformTimePicker, ITimePicker ti public static void UpdateTextColor(this TimePicker platformTimePicker, ITimePicker timePicker) { - var brush = timePicker.TextColor?.ToPlatform(); + Color textColor = timePicker.TextColor; - if (brush is null) + UI.Xaml.Media.Brush? platformBrush = textColor?.ToPlatform(); + + if (platformBrush == null) + { platformTimePicker.Resources.RemoveKeys(TextColorResourceKeys); + platformTimePicker.ClearValue(TimePicker.ForegroundProperty); + } else - platformTimePicker.Resources.SetValueForAllKey(TextColorResourceKeys, brush); - + { + platformTimePicker.Resources.SetValueForAllKey(TextColorResourceKeys, platformBrush); + platformTimePicker.Foreground = platformBrush; + } + platformTimePicker.RefreshThemeResources(); } + // ResourceKeys controlling the foreground color of the TimePicker. + // https://docs.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.timepicker?view=windows-app-sdk-1.1 static readonly string[] TextColorResourceKeys = { "TimePickerButtonForeground", - "TimePickerButtonForegroundDefault", "TimePickerButtonForegroundPointerOver", "TimePickerButtonForegroundPressed", - "TimePickerButtonForegroundDisabled", - "TimePickerButtonForegroundFocused", + "TimePickerButtonForegroundDisabled" + }; + + // TODO NET7 add to public API + internal static void UpdateBackground(this TimePicker platformTimePicker, ITimePicker timePicker) + { + var brush = timePicker?.Background?.ToPlatform(); + + if (brush is null) + { + platformTimePicker.Resources.RemoveKeys(BackgroundColorResourceKeys); + platformTimePicker.ClearValue(TimePicker.BackgroundProperty); + } + else + { + platformTimePicker.Resources.SetValueForAllKey(BackgroundColorResourceKeys, brush); + platformTimePicker.Background = brush; + } + + platformTimePicker.RefreshThemeResources(); + } + + static readonly string[] BackgroundColorResourceKeys = + { + "TimePickerButtonBackground", + "TimePickerButtonBackgroundPointerOver", + "TimePickerButtonBackgroundPressed", + "TimePickerButtonBackgroundDisabled", + "TimePickerButtonBackgroundFocused", }; } -} \ No newline at end of file +} diff --git a/src/Core/tests/DeviceTests/Core.DeviceTests.csproj b/src/Core/tests/DeviceTests/Core.DeviceTests.csproj index 96feae4072fe..4849a5abff81 100644 --- a/src/Core/tests/DeviceTests/Core.DeviceTests.csproj +++ b/src/Core/tests/DeviceTests/Core.DeviceTests.csproj @@ -48,6 +48,7 @@ + diff --git a/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Android.cs index 3d0e1b518bb9..2c1f1a830126 100644 --- a/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Android.cs @@ -1,9 +1,6 @@ using System; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.DeviceTests.Stubs; -using Microsoft.Maui.Graphics; -using Microsoft.Maui.Handlers; using Xunit; using AColor = Android.Graphics.Color; diff --git a/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Windows.cs b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Windows.cs new file mode 100644 index 000000000000..30375d648b25 --- /dev/null +++ b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.Windows.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.Maui.Controls; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.Maui.DeviceTests +{ + public partial class DatePickerHandlerTests + { + CalendarDatePicker GetNativeDatePicker(DatePickerHandler datePickerHandler) => + datePickerHandler.PlatformView; + + DateTime GetNativeDate(DatePickerHandler datePickerHandler) + { + var plaformDatePicker = GetNativeDatePicker(datePickerHandler); + var date = plaformDatePicker.Date; + + if (date.HasValue) + return date.Value.DateTime; + + return DateTime.MinValue; + } + + Color GetNativeTextColor(DatePickerHandler datePickerHandler) + { + var foreground = GetNativeDatePicker(datePickerHandler).Foreground; + + if (foreground is UI.Xaml.Media.SolidColorBrush solidColorBrush) + return solidColorBrush.Color.ToColor(); + + return null; + } + } +} diff --git a/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.cs index 0673fcb24df6..f0876ff3ca69 100644 --- a/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.cs @@ -1,9 +1,6 @@ using System; using System.Threading.Tasks; using Microsoft.Maui.DeviceTests.Stubs; -using Microsoft.Maui.Graphics; -using Microsoft.Maui.Handlers; -using Xunit; namespace Microsoft.Maui.DeviceTests { diff --git a/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.iOS.cs index d6fb6a8d6a51..2b341c0f6734 100644 --- a/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/DatePicker/DatePickerHandlerTests.iOS.cs @@ -1,9 +1,6 @@ using System; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.DeviceTests.Stubs; -using Microsoft.Maui.Graphics; -using Microsoft.Maui.Handlers; using Xunit; namespace Microsoft.Maui.DeviceTests diff --git a/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs b/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs index ebcea5c63312..36c117bd641c 100644 --- a/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs +++ b/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs @@ -70,16 +70,20 @@ public static async Task AttachAndRun(this FrameworkElement view, Func AttachAndRun(this FrameworkElement view, Func ToBitmap(this FrameworkElement view) =>