diff --git a/src/System.Windows.Forms.DataVisualization/Utilities/ValueConverter.cs b/src/System.Windows.Forms.DataVisualization/Utilities/ValueConverter.cs index 08de8112..1266632b 100644 --- a/src/System.Windows.Forms.DataVisualization/Utilities/ValueConverter.cs +++ b/src/System.Windows.Forms.DataVisualization/Utilities/ValueConverter.cs @@ -12,157 +12,160 @@ using System.Globalization; -namespace System.Windows.Forms.DataVisualization.Charting.Utilities +namespace System.Windows.Forms.DataVisualization.Charting.Utilities; + +/// +/// ValueConverter class is used when numeric or DateTime +/// value needs to be converted to a string using specified format. +/// +internal static class ValueConverter { + #region Methods + /// - /// ValueConverter class is used when numeric or DateTime - /// value needs to be converted to a string using specified format. + /// Converts value to string using specified format. /// - internal static class ValueConverter - { - #region Methods - - /// - /// Converts value to string using specified format. - /// - /// Reference to the chart object. - /// Reference to the object being formatted. - /// Additional object tag. - /// Value converted to string. - /// Format string. - /// Value type. - /// Chart element type being formatted. - public static string FormatValue( - Chart chart, - object obj, - object objTag, - double value, - string format, - ChartValueType valueType, - ChartElementType elementType) - { - format ??= string.Empty; - string convertionFormat = format; - string result = string.Empty; - - // Make sure value index is part of the format - if(convertionFormat != null && convertionFormat.Length > 0) - { - int bracketIndex = convertionFormat.IndexOf('{', 0); - if(bracketIndex >= 0) - { - while(bracketIndex >= 0) - { - // If format is not followed by the value index - if(!convertionFormat[bracketIndex..].StartsWith("{0:", StringComparison.Ordinal)) - { - // Check character prior to the bracket - if(bracketIndex >= 1 && convertionFormat.Substring(bracketIndex - 1, 1) == "{") - { - continue; - } - else - { - // Insert value index in format - convertionFormat = convertionFormat.Insert(bracketIndex + 1, "0:"); - } - } - - bracketIndex = convertionFormat.IndexOf('{', bracketIndex + 1); - } - } - else - { - convertionFormat = "{0:" + convertionFormat + "}"; - } - } - - // Date/time formating - if (valueType == ChartValueType.DateTime || - valueType == ChartValueType.DateTimeOffset || - valueType == ChartValueType.Date) - { - // Set default format - if(convertionFormat.Length == 0) - { - convertionFormat = "{0:d}"; - if (valueType == ChartValueType.DateTimeOffset) - convertionFormat += " +0"; - } - - // Convert date to string - result = String.Format(CultureInfo.CurrentCulture, convertionFormat, DateTime.FromOADate(value)); - } - else if(valueType == ChartValueType.Time) - { - // Set default format - if(convertionFormat.Length == 0) - { - convertionFormat = "{0:t}"; - } - - // Convert date to string - result = String.Format(CultureInfo.CurrentCulture, convertionFormat, DateTime.FromOADate(value)); - } - else - { - bool failedFlag = false; - - // Set default format - if(convertionFormat.Length == 0) - { - convertionFormat = "{0:G}"; - } - - try - { - // Numeric value formatting - result = String.Format(CultureInfo.CurrentCulture,convertionFormat, value); - } - catch(FormatException) - { - failedFlag = true; - } - - // If numeric formatting failed try to format using decimal number - if(failedFlag) - { - failedFlag = false; - try - { - // Decimal value formatting - result = String.Format(CultureInfo.CurrentCulture, convertionFormat, (long)value); - } - catch (ArgumentNullException) - { - failedFlag = true; - } - catch (FormatException) + /// Reference to the chart object. + /// Reference to the object being formatted. + /// Additional object tag. + /// Value converted to string. + /// Format string. + /// Value type. + /// Chart element type being formatted. + public static string FormatValue( + Chart chart, + object obj, + object objTag, + double value, + string format, + ChartValueType valueType, + ChartElementType elementType) + { + format ??= string.Empty; + string convertionFormat = format; + string result = string.Empty; + + // Make sure value index is part of the format + if (convertionFormat != null && convertionFormat.Length > 0) + { + int bracketIndex = convertionFormat.IndexOf('{', 0); + if (bracketIndex >= 0) + { + while (bracketIndex >= 0) + { + // If format is not followed by the value index + if (!convertionFormat[bracketIndex..].StartsWith("{0:", StringComparison.Ordinal)) { - failedFlag = true; + // Check character prior to the bracket + if (bracketIndex >= 1 && convertionFormat.Substring(bracketIndex - 1, 1) == "{") + { + continue; + } + else + { + // Insert value index in format + convertionFormat = convertionFormat.Insert(bracketIndex + 1, "0:"); + } } - } - - // Return format string as result (literal) if all formatting methods failed - if(failedFlag) - { - result = format; - } - } - - // For the Reporting Services chart a special number formatting - // handler may be set and used for all formatting needs. - if (chart != null) + + bracketIndex = convertionFormat.IndexOf('{', bracketIndex + 1); + } + } + else + { + convertionFormat = "{0:" + convertionFormat + "}"; + } + } + + // Date/time formating + if (valueType == ChartValueType.DateTime || + valueType == ChartValueType.DateTimeOffset || + valueType == ChartValueType.Date) + { + // Set default format + if (convertionFormat.Length == 0) + { + convertionFormat = "{0:d}"; + if (valueType == ChartValueType.DateTimeOffset) + convertionFormat += " +0"; + } + + // Convert date to string + result = string.Format(CultureInfo.CurrentCulture, convertionFormat, DateTime.FromOADate(value)); + } + else if (valueType == ChartValueType.Time) + { + // Set default format + if (convertionFormat.Length == 0) { - // Call number formatter - FormatNumberEventArgs eventArguments = new FormatNumberEventArgs(value, format, valueType, result, objTag, elementType); - chart.CallOnFormatNumber(obj, eventArguments); - result = eventArguments.LocalizedValue; + convertionFormat = "{0:t}"; } - return result; - } - - #endregion - } + // Convert date to string + result = string.Format(CultureInfo.CurrentCulture, convertionFormat, DateTime.FromOADate(value)); + } + else + { + bool failedFlag = false; + + // Set default format + if (convertionFormat.Length == 0) + { + convertionFormat = "{0:G}"; + } + + try + { + // Numeric value formatting + // Workaround for "-0" label due to Floating-Point parsing changes in .NET Core 3. See https://devblogs.microsoft.com/dotnet/floating-point-parsing-and-formatting-improvements-in-net-core-3-0/ + if (value == 0d && double.IsNegative(value)) + result = string.Format(CultureInfo.CurrentCulture, convertionFormat, 0d); + else + result = string.Format(CultureInfo.CurrentCulture, convertionFormat, value); + } + catch (FormatException) + { + failedFlag = true; + } + + // If numeric formatting failed try to format using decimal number + if (failedFlag) + { + failedFlag = false; + try + { + // Decimal value formatting + result = string.Format(CultureInfo.CurrentCulture, convertionFormat, (long)value); + } + catch (ArgumentNullException) + { + failedFlag = true; + } + catch (FormatException) + { + failedFlag = true; + } + } + + // Return format string as result (literal) if all formatting methods failed + if (failedFlag) + { + result = format; + } + } + + // For the Reporting Services chart a special number formatting + // handler may be set and used for all formatting needs. + if (chart != null) + { + // Call number formatter + FormatNumberEventArgs eventArguments = new FormatNumberEventArgs(value, format, valueType, result, objTag, elementType); + chart.CallOnFormatNumber(obj, eventArguments); + result = eventArguments.LocalizedValue; + } + + return result; + } + + #endregion }