diff --git a/src/Components/Components/src/BindConverter.cs b/src/Components/Components/src/BindConverter.cs index 06a03a05f390..2cae3af98440 100644 --- a/src/Components/Components/src/BindConverter.cs +++ b/src/Components/Components/src/BindConverter.cs @@ -212,7 +212,29 @@ private static string FormatShortValueCore(short value, CultureInfo? culture) /// /// The formatted value. [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] - public static string FormatValue(float value, CultureInfo? culture = null) => FormatFloatValueCore(value, culture); + public static string FormatValue(float value, CultureInfo? culture = null) => FormatFloatValueCore(value, culture, format: null); + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The format to use. Provided to . + /// The formatted value. + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static string FormatValue(float value, [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, CultureInfo? culture = null) => FormatFloatValueCore(value, culture, format); + + private static string FormatFloatValueCore(float value, CultureInfo? culture, string? format) + { + if (format != null) + { + return value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } private static string FormatFloatValueCore(float value, CultureInfo? culture) { @@ -228,7 +250,34 @@ private static string FormatFloatValueCore(float value, CultureInfo? culture) /// /// The formatted value. [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] - public static string? FormatValue(float? value, CultureInfo? culture = null) => FormatNullableFloatValueCore(value, culture); + public static string? FormatValue(float? value, CultureInfo? culture = null) => FormatNullableFloatValueCore(value, culture, format: null); + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The format to use. Provided to . + /// The formatted value. + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static string? FormatValue(float? value, [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, CultureInfo? culture = null) => FormatNullableFloatValueCore(value, culture, format); + + private static string? FormatNullableFloatValueCore(float? value, CultureInfo? culture, string? format) + { + if (value == null) + { + return null; + } + + if (format != null) + { + return value.Value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } private static string? FormatNullableFloatValueCore(float? value, CultureInfo? culture) { @@ -249,7 +298,29 @@ private static string FormatFloatValueCore(float value, CultureInfo? culture) /// /// The formatted value. [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] - public static string? FormatValue(double value, CultureInfo? culture = null) => FormatDoubleValueCore(value, culture); + public static string? FormatValue(double value, CultureInfo? culture = null) => FormatDoubleValueCore(value, culture, format: null); + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The format to use. Provided to . + /// The formatted value. + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static string? FormatValue(double value, [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, CultureInfo? culture = null) => FormatDoubleValueCore(value, culture, format); + + private static string FormatDoubleValueCore(double value, CultureInfo? culture, string? format) + { + if (format != null) + { + return value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } private static string FormatDoubleValueCore(double value, CultureInfo? culture) { @@ -265,7 +336,19 @@ private static string FormatDoubleValueCore(double value, CultureInfo? culture) /// /// The formatted value. [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] - public static string? FormatValue(double? value, CultureInfo? culture = null) => FormatNullableDoubleValueCore(value, culture); + public static string? FormatValue(double? value, CultureInfo? culture = null) => FormatNullableDoubleValueCore(value, culture, format: null); + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The format to use. Provided to . + /// The formatted value. + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static string? FormatValue(double? value, [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, CultureInfo? culture = null) => FormatNullableDoubleValueCore(value, culture, format); private static string? FormatNullableDoubleValueCore(double? value, CultureInfo? culture) { @@ -277,6 +360,21 @@ private static string FormatDoubleValueCore(double value, CultureInfo? culture) return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); } + private static string? FormatNullableDoubleValueCore(double? value, CultureInfo? culture, string? format) + { + if (value == null) + { + return null; + } + + if (format != null) + { + return value.Value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + /// /// Formats the provided for inclusion in an attribute. /// @@ -286,13 +384,35 @@ private static string FormatDoubleValueCore(double value, CultureInfo? culture) /// /// The formatted value. [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] - public static string FormatValue(decimal value, CultureInfo? culture = null) => FormatDecimalValueCore(value, culture); + public static string FormatValue(decimal value, CultureInfo? culture = null) => FormatDecimalValueCore(value, culture, format: null); + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The format to use. Provided to . + /// The formatted value. + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static string FormatValue(decimal value, [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, CultureInfo? culture = null) => FormatDecimalValueCore(value, culture, format); private static string FormatDecimalValueCore(decimal value, CultureInfo? culture) { return value.ToString(culture ?? CultureInfo.CurrentCulture); } + private static string FormatDecimalValueCore(decimal value, CultureInfo? culture, string? format) + { + if (format != null) + { + return value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + /// /// Formats the provided as a . /// @@ -302,7 +422,19 @@ private static string FormatDecimalValueCore(decimal value, CultureInfo? culture /// /// The formatted value. [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] - public static string? FormatValue(decimal? value, CultureInfo? culture = null) => FormatNullableDecimalValueCore(value, culture); + public static string? FormatValue(decimal? value, CultureInfo? culture = null) => FormatNullableDecimalValueCore(value, culture, format: null); + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The format to use. Provided to . + /// The formatted value. + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static string? FormatValue(decimal? value, [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, CultureInfo? culture = null) => FormatNullableDecimalValueCore(value, culture, format); private static string? FormatNullableDecimalValueCore(decimal? value, CultureInfo? culture) { @@ -314,6 +446,21 @@ private static string FormatDecimalValueCore(decimal value, CultureInfo? culture return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); } + private static string? FormatNullableDecimalValueCore(decimal? value, CultureInfo? culture, string? format) + { + if (value == null) + { + return null; + } + + if (format != null) + { + return value.Value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + /// /// Formats the provided as a . /// @@ -969,9 +1116,16 @@ public static bool TryConvertToNullableFloat(object? obj, CultureInfo? culture, } internal static BindParser ConvertToFloat = ConvertToFloatCore; + internal static BindParserWithFormat ConvertToFloatWithFormat = ConvertToFloatCore; internal static BindParser ConvertToNullableFloat = ConvertToNullableFloatCore; + internal static BindParserWithFormat ConvertToNullableFloatWithFormat = ConvertToNullableFloatCore; private static bool ConvertToFloatCore(object? obj, CultureInfo? culture, out float value) + { + return ConvertToFloatCore(obj, culture, format: null, out value); + } + + private static bool ConvertToFloatCore(object? obj, CultureInfo? culture, string? format, out float value) { var text = (string?)obj; if (string.IsNullOrEmpty(text)) @@ -997,6 +1151,11 @@ private static bool ConvertToFloatCore(object? obj, CultureInfo? culture, out fl } private static bool ConvertToNullableFloatCore(object? obj, CultureInfo? culture, out float? value) + { + return ConvertToNullableFloatCore(obj, culture, format: null, out value); + } + + private static bool ConvertToNullableFloatCore(object? obj, CultureInfo? culture, string? format, out float? value) { var text = (string?)obj; if (string.IsNullOrEmpty(text)) @@ -1046,9 +1205,16 @@ public static bool TryConvertToNullableDouble(object? obj, CultureInfo? culture, } internal static BindParser ConvertToDoubleDelegate = ConvertToDoubleCore; + internal static BindParserWithFormat ConvertToDoubleWithFormat = ConvertToDoubleCore; internal static BindParser ConvertToNullableDoubleDelegate = ConvertToNullableDoubleCore; + internal static BindParserWithFormat ConvertToNullableDoubleWithFormat = ConvertToNullableDoubleCore; private static bool ConvertToDoubleCore(object? obj, CultureInfo? culture, out double value) + { + return ConvertToDoubleCore(obj, culture, format: null, out value); + } + + private static bool ConvertToDoubleCore(object? obj, CultureInfo? culture, string? format, out double value) { var text = (string?)obj; if (string.IsNullOrEmpty(text)) @@ -1074,6 +1240,11 @@ private static bool ConvertToDoubleCore(object? obj, CultureInfo? culture, out d } private static bool ConvertToNullableDoubleCore(object? obj, CultureInfo? culture, out double? value) + { + return ConvertToNullableDoubleCore(obj, culture, format: null, out value); + } + + private static bool ConvertToNullableDoubleCore(object? obj, CultureInfo? culture, string? format, out double? value) { var text = (string?)obj; if (string.IsNullOrEmpty(text)) @@ -1123,9 +1294,16 @@ public static bool TryConvertToNullableDecimal(object? obj, CultureInfo? culture } internal static BindParser ConvertToDecimal = ConvertToDecimalCore; + internal static BindParserWithFormat ConvertToDecimalWithFormat = ConvertToDecimalCore; internal static BindParser ConvertToNullableDecimal = ConvertToNullableDecimalCore; + internal static BindParserWithFormat ConvertToNullableDecimalWithFormat = ConvertToNullableDecimalCore; private static bool ConvertToDecimalCore(object? obj, CultureInfo? culture, out decimal value) + { + return ConvertToDecimalCore(obj, culture, format: null, out value); + } + + private static bool ConvertToDecimalCore(object? obj, CultureInfo? culture, string? format, out decimal value) { var text = (string?)obj; if (string.IsNullOrEmpty(text)) @@ -1145,6 +1323,11 @@ private static bool ConvertToDecimalCore(object? obj, CultureInfo? culture, out } private static bool ConvertToNullableDecimalCore(object? obj, CultureInfo? culture, out decimal? value) + { + return ConvertToNullableDecimalCore(obj, culture, format: null, out value); + } + + private static bool ConvertToNullableDecimalCore(object? obj, CultureInfo? culture, string? format, out decimal? value) { var text = (string?)obj; if (string.IsNullOrEmpty(text)) diff --git a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs index 1532ff08b326..e743cce279c2 100644 --- a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs +++ b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs @@ -422,6 +422,28 @@ public static EventCallback CreateBinder( return CreateBinderCoreAsync(factory, receiver, setter, culture, ConvertToFloat); } + /// + /// For internal use only. + /// + /// + /// + /// + /// + /// + /// + /// + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static EventCallback CreateBinder( + this EventCallbackFactory factory, + object receiver, + Action setter, + float existingValue, + string format, + CultureInfo? culture = null) + { + return CreateBinderCore(factory, receiver, setter, culture, format, ConvertToFloatWithFormat); + } + /// /// For internal use only. /// @@ -462,6 +484,28 @@ public static EventCallback CreateBinder( return CreateBinderCoreAsync(factory, receiver, setter, culture, ConvertToNullableFloat); } + /// + /// For internal use only. + /// + /// + /// + /// + /// + /// + /// + /// + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static EventCallback CreateBinder( + this EventCallbackFactory factory, + object receiver, + Action setter, + float? existingValue, + string format, + CultureInfo? culture = null) + { + return CreateBinderCore(factory, receiver, setter, culture, format, ConvertToNullableFloatWithFormat); + } + /// /// For internal use only. /// @@ -502,6 +546,28 @@ public static EventCallback CreateBinder( return CreateBinderCoreAsync(factory, receiver, setter, culture, ConvertToDoubleDelegate); } + /// + /// For internal use only. + /// + /// + /// + /// + /// + /// + /// + /// + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static EventCallback CreateBinder( + this EventCallbackFactory factory, + object receiver, + Action setter, + double existingValue, + string format, + CultureInfo? culture = null) + { + return CreateBinderCore(factory, receiver, setter, culture, format, ConvertToDoubleWithFormat); + } + /// /// For internal use only. /// @@ -542,6 +608,28 @@ public static EventCallback CreateBinder( return CreateBinderCoreAsync(factory, receiver, setter, culture, ConvertToNullableDoubleDelegate); } + /// + /// For internal use only. + /// + /// + /// + /// + /// + /// + /// + /// + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static EventCallback CreateBinder( + this EventCallbackFactory factory, + object receiver, + Action setter, + double? existingValue, + string format, + CultureInfo? culture = null) + { + return CreateBinderCore(factory, receiver, setter, culture, format, ConvertToNullableDoubleWithFormat); + } + /// /// For internal use only. /// @@ -582,6 +670,28 @@ public static EventCallback CreateBinder( return CreateBinderCoreAsync(factory, receiver, setter, culture, ConvertToDecimal); } + /// + /// For internal use only. + /// + /// + /// + /// + /// + /// + /// + /// + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static EventCallback CreateBinder( + this EventCallbackFactory factory, + object receiver, + Action setter, + decimal existingValue, + string format, + CultureInfo? culture = null) + { + return CreateBinderCore(factory, receiver, setter, culture, format, ConvertToDecimalWithFormat); + } + /// /// For internal use only. /// @@ -622,6 +732,28 @@ public static EventCallback CreateBinder( return CreateBinderCoreAsync(factory, receiver, setter, culture, ConvertToNullableDecimal); } + /// + /// For internal use only. + /// + /// + /// + /// + /// + /// + /// + /// + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] + public static EventCallback CreateBinder( + this EventCallbackFactory factory, + object receiver, + Action setter, + decimal? existingValue, + string format, + CultureInfo? culture = null) + { + return CreateBinderCore(factory, receiver, setter, culture, format, ConvertToNullableDecimalWithFormat); + } + /// /// For internal use only. /// diff --git a/src/Components/Components/src/PublicAPI.Unshipped.txt b/src/Components/Components/src/PublicAPI.Unshipped.txt index 07e51aca6bd3..86029676506f 100644 --- a/src/Components/Components/src/PublicAPI.Unshipped.txt +++ b/src/Components/Components/src/PublicAPI.Unshipped.txt @@ -14,6 +14,18 @@ Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateS Microsoft.AspNetCore.Components.SupplyParameterFromPersistentComponentStateAttribute Microsoft.AspNetCore.Components.SupplyParameterFromPersistentComponentStateAttribute.SupplyParameterFromPersistentComponentStateAttribute() -> void Microsoft.Extensions.DependencyInjection.SupplyParameterFromPersistentComponentStateProviderServiceCollectionExtensions +static Microsoft.AspNetCore.Components.BindConverter.FormatValue(decimal value, string? format, System.Globalization.CultureInfo? culture = null) -> string! +static Microsoft.AspNetCore.Components.BindConverter.FormatValue(decimal? value, string? format, System.Globalization.CultureInfo? culture = null) -> string? +static Microsoft.AspNetCore.Components.BindConverter.FormatValue(double value, string? format, System.Globalization.CultureInfo? culture = null) -> string? +static Microsoft.AspNetCore.Components.BindConverter.FormatValue(double? value, string? format, System.Globalization.CultureInfo? culture = null) -> string? +static Microsoft.AspNetCore.Components.BindConverter.FormatValue(float value, string? format, System.Globalization.CultureInfo? culture = null) -> string! +static Microsoft.AspNetCore.Components.BindConverter.FormatValue(float? value, string? format, System.Globalization.CultureInfo? culture = null) -> string? +static Microsoft.AspNetCore.Components.EventCallbackFactoryBinderExtensions.CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! setter, decimal existingValue, string! format, System.Globalization.CultureInfo? culture = null) -> Microsoft.AspNetCore.Components.EventCallback +static Microsoft.AspNetCore.Components.EventCallbackFactoryBinderExtensions.CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! setter, decimal? existingValue, string! format, System.Globalization.CultureInfo? culture = null) -> Microsoft.AspNetCore.Components.EventCallback +static Microsoft.AspNetCore.Components.EventCallbackFactoryBinderExtensions.CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! setter, double existingValue, string! format, System.Globalization.CultureInfo? culture = null) -> Microsoft.AspNetCore.Components.EventCallback +static Microsoft.AspNetCore.Components.EventCallbackFactoryBinderExtensions.CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! setter, double? existingValue, string! format, System.Globalization.CultureInfo? culture = null) -> Microsoft.AspNetCore.Components.EventCallback +static Microsoft.AspNetCore.Components.EventCallbackFactoryBinderExtensions.CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! setter, float existingValue, string! format, System.Globalization.CultureInfo? culture = null) -> Microsoft.AspNetCore.Components.EventCallback +static Microsoft.AspNetCore.Components.EventCallbackFactoryBinderExtensions.CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory! factory, object! receiver, System.Action! setter, float? existingValue, string! format, System.Globalization.CultureInfo? culture = null) -> Microsoft.AspNetCore.Components.EventCallback static Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(Microsoft.Extensions.DependencyInjection.IServiceCollection! services, Microsoft.AspNetCore.Components.IComponentRenderMode! componentRenderMode) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions.AddComponentsMetrics(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.AspNetCore.Components.Infrastructure.ComponentsMetricsServiceCollectionExtensions.AddComponentsTracing(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! diff --git a/src/Components/Components/test/BindConverterTest.cs b/src/Components/Components/test/BindConverterTest.cs index b18b71dc5684..ed10a4040278 100644 --- a/src/Components/Components/test/BindConverterTest.cs +++ b/src/Components/Components/test/BindConverterTest.cs @@ -369,6 +369,75 @@ public void TryConvertTo_NullableGuid__Invalid() Assert.Null(actual); } + [Theory] + [InlineData(1.1, "0.0#", "1.1")] // Single decimal place with optional second + [InlineData(1500, "0.00", "1500.00")] // Force two decimal places + [InlineData(1500, "0.##", "1500")] // Remove unnecessary decimals + [InlineData(0, "0.00", "0.00")] // Zero with fixed decimals + [InlineData(0, "0.##", "0")] // Zero with optional decimals + [InlineData(-1.1, "0.0#", "-1.1")] // Negative number with one decimal place + [InlineData(-1500, "0.00", "-1500.00")] // Negative number with two fixed decimals + [InlineData(1.999, "0.0", "2.0")] // Rounding up + [InlineData(1.111, "0.0", "1.1")] // Rounding down + [InlineData(1234567.89, "N2", "1,234,567.89")] // Large number with thousands separator + [InlineData(1234567.89, "#,##0.00", "1,234,567.89")] // Explicit thousands separator format + [InlineData(0.1234, "0.00%", "12.34%")] // Percentage formatting + [InlineData(0.12, "00.00", "00.12")] // Fixed zero's with fixed decimals + [InlineData(1234567.89, "0.00", "1234567.89")] // Fixed two decimals + public void FormatValue_Double_Format(double value, string format, string expected) + { + // Act + var actual = BindConverter.FormatValue(value, format, CultureInfo.InvariantCulture); + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(1.1, "0.0#", "1.1")] // Single decimal place with optional second + [InlineData(1500, "0.00", "1500.00")] // Force two decimal places + [InlineData(1500, "0.##", "1500")] // Remove unnecessary decimals + [InlineData(0, "0.00", "0.00")] // Zero with fixed decimals + [InlineData(0, "0.##", "0")] // Zero with optional decimals + [InlineData(-1.1, "0.0#", "-1.1")] // Negative number with one decimal place + [InlineData(-1500, "0.00", "-1500.00")] // Negative number with two fixed decimals + [InlineData(1.999, "0.0", "2.0")] // Rounding up + [InlineData(1.111, "0.0", "1.1")] // Rounding down + [InlineData(1234567.89, "N2", "1,234,567.89")] // Large number with thousands separator + [InlineData(1234567.89, "#,##0.00", "1,234,567.89")] // Explicit thousands separator format + [InlineData(0.1234, "0.00%", "12.34%")] // Percentage formatting + [InlineData(0.12, "00.00", "00.12")] // Fixed zero's with fixed decimals + public void FormatValue_Decimal_Format(decimal value, string format, string expected) + { + // Act + var actual = BindConverter.FormatValue(value, format, CultureInfo.InvariantCulture); + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(1.1, "0.0#", "1.1")] // Single decimal place with optional second + [InlineData(1500, "0.00", "1500.00")] // Force two decimal places + [InlineData(1500, "0.##", "1500")] // Remove unnecessary decimals + [InlineData(0, "0.00", "0.00")] // Zero with fixed decimals + [InlineData(0, "0.##", "0")] // Zero with optional decimals + [InlineData(-1.1, "0.0#", "-1.1")] // Negative number with one decimal place + [InlineData(-1500, "0.00", "-1500.00")] // Negative number with two fixed decimals + [InlineData(1.999, "0.0", "2.0")] // Rounding up + [InlineData(1.111, "0.0", "1.1")] // Rounding down + [InlineData(1234567.89, "N2", "1,234,567.88")] // Large number with thousands separator + [InlineData(0.1234, "0.00%", "12.34%")] // Percentage formatting + [InlineData(0.12, "00.00", "00.12")] // Fixed zero's with fixed decimals + public void FormatValue_Float_Format(float value, string format, string expected) + { + // Act + var actual = BindConverter.FormatValue(value, format, CultureInfo.InvariantCulture); + + // Assert + Assert.Equal(expected, actual); + } + private enum SomeLetters { A,