From 8d41acba67c9ccc9ec7b2030dbf3d9a5b487cd6d Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 25 Jun 2019 21:31:09 -0700 Subject: [PATCH] Add support for passing CultureInfo to binders This change allows the binding infrastructure to accept a CultureInfo everywhere we create a binder. The next step will be to add support to the binding directive attribute to pass a culture. --- ...ft.AspNetCore.Components.netstandard2.0.cs | 34 ++-- .../EventCallbackFactoryBinderExtensions.cs | 175 +++++++++++------- ...ventCallbackFactoryBinderExtensionsTest.cs | 40 ++++ 3 files changed, 162 insertions(+), 87 deletions(-) diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs index be05804ba7..417e114224 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs @@ -148,23 +148,23 @@ namespace Microsoft.AspNetCore.Components } public static partial class EventCallbackFactoryBinderExtensions { - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, bool existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, System.DateTime existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, System.DateTime existingValue, string format) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, decimal existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, double existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, int existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, long existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, bool? existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, System.DateTime? existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, decimal? existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, double? existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, int? existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, long? existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, float? existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, float existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, string existingValue) { throw null; } - public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, T existingValue) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, bool existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, System.DateTime existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, System.DateTime existingValue, string format, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, decimal existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, double existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, int existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, long existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, bool? existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, System.DateTime? existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, decimal? existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, double? existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, int? existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, long? existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, float? existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, float existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, string existingValue, System.Globalization.CultureInfo culture = null) { throw null; } + public static Microsoft.AspNetCore.Components.EventCallback CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action setter, T existingValue, System.Globalization.CultureInfo culture = null) { throw null; } } public static partial class EventCallbackFactoryUIEventArgsExtensions { diff --git a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs index 9e5458780f..c8c8d9a0ec 100644 --- a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs +++ b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs @@ -25,13 +25,13 @@ namespace Microsoft.AspNetCore.Components // For now we're not necessarily handling this correctly since we parse the same way for number and text. public static class EventCallbackFactoryBinderExtensions { - private delegate bool BindConverter(object obj, out T value); + private delegate bool BindConverter(object obj, CultureInfo culture, out T value); // Perf: conversion delegates are written as static funcs so we can prevent // allocations for these simple cases. private readonly static BindConverter ConvertToString = ConvertToStringCore; - private static bool ConvertToStringCore(object obj, out string value) + private static bool ConvertToStringCore(object obj, CultureInfo culture, out string value) { // We expect the input to already be a string. value = (string)obj; @@ -41,14 +41,14 @@ namespace Microsoft.AspNetCore.Components private static BindConverter ConvertToBool = ConvertToBoolCore; private static BindConverter ConvertToNullableBool = ConvertToNullableBoolCore; - private static bool ConvertToBoolCore(object obj, out bool value) + private static bool ConvertToBoolCore(object obj, CultureInfo culture, out bool value) { // We expect the input to already be a bool. value = (bool)obj; return true; } - private static bool ConvertToNullableBoolCore(object obj, out bool? value) + private static bool ConvertToNullableBoolCore(object obj, CultureInfo culture, out bool? value) { // We expect the input to already be a bool. value = (bool?)obj; @@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Components private static BindConverter ConvertToInt = ConvertToIntCore; private static BindConverter ConvertToNullableInt = ConvertToNullableIntCore; - private static bool ConvertToIntCore(object obj, out int value) + private static bool ConvertToIntCore(object obj, CultureInfo culture, out int value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Components return false; } - if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted)) + if (!int.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToNullableIntCore(object obj, out int? value) + private static bool ConvertToNullableIntCore(object obj, CultureInfo culture, out int? value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Components return true; } - if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted)) + if (!int.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Components private static BindConverter ConvertToLong = ConvertToLongCore; private static BindConverter ConvertToNullableLong = ConvertToNullableLongCore; - private static bool ConvertToLongCore(object obj, out long value) + private static bool ConvertToLongCore(object obj, CultureInfo culture, out long value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Components return false; } - if (!long.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted)) + if (!long.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToNullableLongCore(object obj, out long? value) + private static bool ConvertToNullableLongCore(object obj, CultureInfo culture, out long? value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -127,7 +127,7 @@ namespace Microsoft.AspNetCore.Components return true; } - if (!long.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted)) + if (!long.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -140,7 +140,7 @@ namespace Microsoft.AspNetCore.Components private static BindConverter ConvertToFloat = ConvertToFloatCore; private static BindConverter ConvertToNullableFloat = ConvertToNullableFloatCore; - private static bool ConvertToFloatCore(object obj, out float value) + private static bool ConvertToFloatCore(object obj, CultureInfo culture, out float value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Components return false; } - if (!float.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted)) + if (!float.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToNullableFloatCore(object obj, out float? value) + private static bool ConvertToNullableFloatCore(object obj, CultureInfo culture, out float? value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -168,7 +168,7 @@ namespace Microsoft.AspNetCore.Components return true; } - if (!float.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted)) + if (!float.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -181,7 +181,7 @@ namespace Microsoft.AspNetCore.Components private static BindConverter ConvertToDouble = ConvertToDoubleCore; private static BindConverter ConvertToNullableDouble = ConvertToNullableDoubleCore; - private static bool ConvertToDoubleCore(object obj, out double value) + private static bool ConvertToDoubleCore(object obj, CultureInfo culture, out double value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -190,7 +190,7 @@ namespace Microsoft.AspNetCore.Components return false; } - if (!double.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted)) + if (!double.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToNullableDoubleCore(object obj, out double? value) + private static bool ConvertToNullableDoubleCore(object obj, CultureInfo culture, out double? value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -209,7 +209,7 @@ namespace Microsoft.AspNetCore.Components return true; } - if (!double.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted)) + if (!double.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Components private static BindConverter ConvertToDecimal = ConvertToDecimalCore; private static BindConverter ConvertToNullableDecimal = ConvertToNullableDecimalCore; - private static bool ConvertToDecimalCore(object obj, out decimal value) + private static bool ConvertToDecimalCore(object obj, CultureInfo culture, out decimal value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -231,7 +231,7 @@ namespace Microsoft.AspNetCore.Components return false; } - if (!decimal.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted)) + if (!decimal.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -241,7 +241,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToNullableDecimalCore(object obj, out decimal? value) + private static bool ConvertToNullableDecimalCore(object obj, CultureInfo culture, out decimal? value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -250,7 +250,7 @@ namespace Microsoft.AspNetCore.Components return true; } - if (!decimal.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted)) + if (!decimal.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) { value = default; return false; @@ -263,7 +263,7 @@ namespace Microsoft.AspNetCore.Components private static BindConverter ConvertToDateTime = ConvertToDateTimeCore; private static BindConverter ConvertToNullableDateTime = ConvertToNullableDateTimeCore; - private static bool ConvertToDateTimeCore(object obj, out DateTime value) + private static bool ConvertToDateTimeCore(object obj, CultureInfo culture, out DateTime value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -272,7 +272,7 @@ namespace Microsoft.AspNetCore.Components return false; } - if (!DateTime.TryParse(text, CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) + if (!DateTime.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) { value = default; return false; @@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToNullableDateTimeCore(object obj, out DateTime? value) + private static bool ConvertToNullableDateTimeCore(object obj, CultureInfo culture, out DateTime? value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -291,7 +291,7 @@ namespace Microsoft.AspNetCore.Components return true; } - if (!DateTime.TryParse(text, CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) + if (!DateTime.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) { value = default; return false; @@ -301,7 +301,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToEnum(object obj, out T value) where T : struct, Enum + private static bool ConvertToEnum(object obj, CultureInfo culture, out T value) where T : struct, Enum { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Components return true; } - private static bool ConvertToNullableEnum(object obj, out Nullable value) where T : struct, Enum + private static bool ConvertToNullableEnum(object obj, CultureInfo culture, out T? value) where T : struct, Enum { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -346,14 +346,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - string existingValue) + string existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToString); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToString); } /// @@ -363,14 +365,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - bool existingValue) + bool existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToBool); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToBool); } /// @@ -380,14 +384,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - bool? existingValue) + bool? existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToNullableBool); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableBool); } /// @@ -397,14 +403,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - int existingValue) + int existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToInt); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToInt); } /// @@ -414,14 +422,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - int? existingValue) + int? existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToNullableInt); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableInt); } /// @@ -431,14 +441,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - long existingValue) + long existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToLong); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToLong); } /// @@ -448,14 +460,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - long? existingValue) + long? existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToNullableLong); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableLong); } /// @@ -465,14 +479,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - float existingValue) + float existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToFloat); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToFloat); } /// @@ -482,14 +498,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - float? existingValue) + float? existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToNullableFloat); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableFloat); } /// @@ -499,14 +517,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - double existingValue) + double existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToDouble); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToDouble); } /// @@ -516,14 +536,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - double? existingValue) + double? existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToNullableDouble); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableDouble); } /// @@ -533,14 +555,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - decimal existingValue) + decimal existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToDecimal); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToDecimal); } /// @@ -550,14 +574,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - decimal? existingValue) + decimal? existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToNullableDecimal); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableDecimal); } /// @@ -567,14 +593,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - DateTime existingValue) + DateTime existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToDateTime); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToDateTime); } /// @@ -584,14 +612,16 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - DateTime? existingValue) + DateTime? existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, ConvertToNullableDateTime); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableDateTime); } /// @@ -602,13 +632,15 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, DateTime existingValue, - string format) + string format, + CultureInfo culture = null) { // Avoiding CreateBinderCore so we can avoid an extra allocating lambda // when a format is used. @@ -618,7 +650,7 @@ namespace Microsoft.AspNetCore.Components var converted = false; try { - value = ConvertDateTime(e.Value, format); + value = ConvertDateTime(e.Value, culture, format); converted = true; } catch @@ -633,20 +665,20 @@ namespace Microsoft.AspNetCore.Components }; return factory.Create(receiver, callback); - static DateTime ConvertDateTime(object obj, string format) + static DateTime ConvertDateTime(object obj, CultureInfo culture, string format) { var text = (string)obj; if (string.IsNullOrEmpty(text)) { return default; } - else if (format != null && DateTime.TryParseExact(text, format, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var value)) + else if (format != null && DateTime.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.RoundtripKind, out var value)) { return value; } else { - return DateTime.Parse(text); + return DateTime.Parse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.RoundtripKind); } } } @@ -659,20 +691,23 @@ namespace Microsoft.AspNetCore.Components /// /// /// + /// /// public static EventCallback CreateBinder( this EventCallbackFactory factory, object receiver, Action setter, - T existingValue) + T existingValue, + CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, BinderConverterCache.Get()); + return CreateBinderCore(factory, receiver, setter, culture, BinderConverterCache.Get()); } private static EventCallback CreateBinderCore( this EventCallbackFactory factory, object receiver, Action setter, + CultureInfo culture, BindConverter converter) { Action callback = e => @@ -681,7 +716,7 @@ namespace Microsoft.AspNetCore.Components var converted = false; try { - converted = converter(e.Value, out value); + converted = converter(e.Value, culture, out value); } catch { @@ -822,7 +857,7 @@ namespace Microsoft.AspNetCore.Components return ConvertWithTypeConverter; - bool ConvertWithTypeConverter(object obj, out T value) + bool ConvertWithTypeConverter(object obj, CultureInfo culture, out T value) { var text = (string)obj; if (string.IsNullOrEmpty(text)) @@ -832,7 +867,7 @@ namespace Microsoft.AspNetCore.Components } // We intentionally close-over the TypeConverter to cache it. The TypeDescriptor infrastructure is slow. - var converted = typeConverter.ConvertFromString(context: null, CultureInfo.CurrentCulture, text); + var converted = typeConverter.ConvertFromString(context: null, culture ?? CultureInfo.CurrentCulture, text); if (converted == null) { value = default; diff --git a/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs b/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs index 831734f460..6136b5b439 100644 --- a/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs +++ b/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.Globalization; using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Components @@ -515,6 +516,45 @@ namespace Microsoft.AspNetCore.Components ex.Message); } + [Fact] + [ReplaceCulture("fr-FR", "fr-FR")] + public async Task CreateBinder_NumericType_WithCurrentCulture() + { + // Arrange + var value = 17_000; + var component = new EventCountingComponent(); + Action setter = (_) => value = _; + + var binder = EventCallback.Factory.CreateBinder(component, setter, value, culture: null); + + var expectedValue = 42_000; + + // Act + await binder.InvokeAsync(new UIChangeEventArgs() { Value = "42 000,00", }); + + Assert.Equal(expectedValue, value); + Assert.Equal(1, component.Count); + } + + [Fact] + public async Task CreateBinder_NumericType_WithInvariantCulture() + { + // Arrange + var value = 17_000; + var component = new EventCountingComponent(); + Action setter = (_) => value = _; + + var binder = EventCallback.Factory.CreateBinder(component, setter, value, CultureInfo.InvariantCulture); + + var expectedValue = 42_000; + + // Act + await binder.InvokeAsync(new UIChangeEventArgs() { Value = "42,000.00", }); + + Assert.Equal(expectedValue, value); + Assert.Equal(1, component.Count); + } + private class EventCountingComponent : IComponent, IHandleEvent { public int Count;