diff --git a/src/Components/Blazor/Build/src/targets/BuiltInBclLinkerDescriptor.xml b/src/Components/Blazor/Build/src/targets/BuiltInBclLinkerDescriptor.xml
index 4b442b1bb87e..32533df8ca3c 100644
--- a/src/Components/Blazor/Build/src/targets/BuiltInBclLinkerDescriptor.xml
+++ b/src/Components/Blazor/Build/src/targets/BuiltInBclLinkerDescriptor.xml
@@ -13,5 +13,9 @@
+
+
+
+
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 382346efce24..7bdacfbc4b0d 100644
--- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
+++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs
@@ -155,6 +155,7 @@ public static partial class EventCallbackFactoryBinderExtensions
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; }
@@ -162,7 +163,7 @@ public static partial class EventCallbackFactoryBinderExtensions
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) where T : struct, System.Enum { 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 partial class EventCallbackFactoryUIEventArgsExtensions
{
diff --git a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs
index ba0a23dd0a96..16fe53ad2614 100644
--- a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs
+++ b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs
@@ -2,13 +2,27 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Concurrent;
+using System.ComponentModel;
+using System.Diagnostics;
using System.Globalization;
+using System.Reflection;
namespace Microsoft.AspNetCore.Components
{
///
/// Contains extension methods for two-way binding using . For internal use only.
///
+ //
+ // NOTE: for number parsing, the HTML5 spec dictates that the DOM will represent
+ // number values as floating point numbers using `.` as the period separator. This is NOT culture senstive.
+ // Put another way, the user might see `,` as their decimal separator, but the value available in events
+ // to JS code is always simpilar to what .NET parses with InvariantCulture.
+ //
+ // See: https://www.w3.org/TR/html5/sec-forms.html#number-state-typenumber
+ // See: https://www.w3.org/TR/html5/infrastructure.html#valid-floating-point-number
+ //
+ // 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);
@@ -53,7 +67,7 @@ private static bool ConvertToIntCore(object obj, out int value)
return false;
}
- if (!int.TryParse(text, out var converted))
+ if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -72,7 +86,7 @@ private static bool ConvertToNullableIntCore(object obj, out int? value)
return true;
}
- if (!int.TryParse(text, out var converted))
+ if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -94,7 +108,7 @@ private static bool ConvertToLongCore(object obj, out long value)
return false;
}
- if (!long.TryParse(text, out var converted))
+ if (!long.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -113,7 +127,7 @@ private static bool ConvertToNullableLongCore(object obj, out long? value)
return true;
}
- if (!long.TryParse(text, out var converted))
+ if (!long.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -135,7 +149,7 @@ private static bool ConvertToFloatCore(object obj, out float value)
return false;
}
- if (!float.TryParse(text, out var converted))
+ if (!float.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -154,7 +168,7 @@ private static bool ConvertToNullableFloatCore(object obj, out float? value)
return true;
}
- if (!float.TryParse(text, out var converted))
+ if (!float.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -176,7 +190,7 @@ private static bool ConvertToDoubleCore(object obj, out double value)
return false;
}
- if (!double.TryParse(text, out var converted))
+ if (!double.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -195,7 +209,7 @@ private static bool ConvertToNullableDoubleCore(object obj, out double? value)
return true;
}
- if (!double.TryParse(text, out var converted))
+ if (!double.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -217,7 +231,7 @@ private static bool ConvertToDecimalCore(object obj, out decimal value)
return false;
}
- if (!decimal.TryParse(text, out var converted))
+ if (!decimal.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -236,7 +250,7 @@ private static bool ConvertToNullableDecimalCore(object obj, out decimal? value)
return true;
}
- if (!decimal.TryParse(text, out var converted))
+ if (!decimal.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out var converted))
{
value = default;
return false;
@@ -246,28 +260,83 @@ private static bool ConvertToNullableDecimalCore(object obj, out decimal? value)
return true;
}
- private static class EnumConverter where T : struct, Enum
+ private static BindConverter ConvertToDateTime = ConvertToDateTimeCore;
+ private static BindConverter ConvertToNullableDateTime = ConvertToNullableDateTimeCore;
+
+ private static bool ConvertToDateTimeCore(object obj, out DateTime value)
{
- public static readonly BindConverter Convert = ConvertCore;
+ var text = (string)obj;
+ if (string.IsNullOrEmpty(text))
+ {
+ value = default;
+ return false;
+ }
- public static bool ConvertCore(object obj, out T value)
+ if (!DateTime.TryParse(text, CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted))
{
- var text = (string)obj;
- if (string.IsNullOrEmpty(text))
- {
- value = default;
- return true;
- }
+ value = default;
+ return false;
+ }
- if (!Enum.TryParse(text, out var converted))
- {
- value = default;
- return false;
- }
+ value = converted;
+ return true;
+ }
- value = converted;
+ private static bool ConvertToNullableDateTimeCore(object obj, out DateTime? value)
+ {
+ var text = (string)obj;
+ if (string.IsNullOrEmpty(text))
+ {
+ value = default;
return true;
}
+
+ if (!DateTime.TryParse(text, CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted))
+ {
+ value = default;
+ return false;
+ }
+
+ value = converted;
+ return true;
+ }
+
+ private static bool ConvertToEnum(object obj, out T value) where T : struct, Enum
+ {
+ var text = (string)obj;
+ if (string.IsNullOrEmpty(text))
+ {
+ value = default;
+ return true;
+ }
+
+ if (!Enum.TryParse(text, out var converted))
+ {
+ value = default;
+ return false;
+ }
+
+ value = converted;
+ return true;
+ }
+
+ private static bool ConvertToNullableEnum(object obj, out Nullable value) where T : struct, Enum
+ {
+ var text = (string)obj;
+ if (string.IsNullOrEmpty(text))
+ {
+ value = default;
+ return true;
+ }
+
+ if (!Enum.TryParse(text, out var converted))
+ {
+ value = default;
+ return false;
+ }
+
+ value = converted;
+ return true;
}
///
@@ -284,7 +353,6 @@ public static EventCallback CreateBinder(
Action setter,
string existingValue)
{
- ;
return CreateBinderCore(factory, receiver, setter, ConvertToString);
}
@@ -489,15 +557,6 @@ public static EventCallback CreateBinder(
Action setter,
decimal? existingValue)
{
- Func