扩展内置类型的自定义格式功能

Ken*_*art 25 .net c# formatting

我对decimal值有一些相当尴尬的格式要求.简而言之:显示到带有尾随空格的两个小数位,除非第三个小数是5,在这种情况下显示为三个小数位.

这种格式化也需要相当灵活.具体地,不总是期望尾随空间,并且当第三个小数是"5"时,"1/2"可能是优选的.

例子:

  • 1.13将显示为带有空格的"01.13"或没有它的"01.13"
  • 1.315将显示为"01.315"或"01.31½"

我需要在其他不相关的UI部分中一致地使用此逻辑.我暂时将其写为WPF值转换器,但这仅用于演示:

public sealed class PriceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is decimal))
        {
            return DependencyProperty.UnsetValue;
        }

        var decimalValue = (decimal)value;
        var formattedDecimalValue = decimalValue.ToString("#0.000", CultureInfo.InvariantCulture);
        var lastFormattedChar = formattedDecimalValue[formattedDecimalValue.Length - 1];

        switch (lastFormattedChar)
        {
            case '0':
                return formattedDecimalValue.Substring(0, formattedDecimalValue.Length - 1) + " ";
            case '5':
                return formattedDecimalValue.Substring(0, formattedDecimalValue.Length - 1) + "½";
            default:
                return formattedDecimalValue;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
Run Code Online (Sandbox Code Playgroud)

我现在正试图将其提取到一个更基本的构建块中,我可以在整个UI层中使用它.我最初的想法是一个自定义格式提供程序,然后我可以使用它Binding:

<TextBlock Text="{Binding Value, FormatString=WHATEVER}"/>
Run Code Online (Sandbox Code Playgroud)

我们的想法是,格式字符串可能类似于"#0.005",表示只显示第三个小数位,如果它是5,或"#0.00F",它试图将第三个小数表示为分数.但是,我无法从绑定中找到使用特定格式提供程序的方法,这似乎是对我的一个主要限制,但也许我错过了一些东西......?

经过更多的实验和调查,我得出结论,我唯一的选择是定义我自己的类型:

public struct Price : IFormattable
Run Code Online (Sandbox Code Playgroud)

这种类型将封装我需要的额外格式化功能.但是,现在我还有另一个难题:在我的ToString实现中,如何利用现有的格式化功能而decimal.ToString(string, IFormatProvider)不会干扰我自己的格式化?看起来这将是相当混乱的,它导致我倾向于更有限的解决方案,只是定义"G"(两个或三个小数位,没有尾随空格)和"S"(与"G"相同,但如果需要,可以使用尾随空格格式化我的Price结构.

谁能告诉我是否有办法让我做这种自定义格式化功能而不会有太多麻烦?

小智 1

有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.iformatprovider.aspx 。

\n\n
// "01.13 " or "01.13". Standard formatting applied: $123.45\n// "01.315" or "01.31\xc2\xbd". Standard formatting applied: $123.45\n\npublic class Test\n{\n    void Main()\n    {\n        decimal number1 = 1.13M;\n        decimal number2 = 1.315M;\n\n        string output1 = String.Format(new CustomNumberFormat(),\n                                 "\\"{0:G}\\" or \\"{0:S}\\". Standard formatting applied: {1:C2}",\n                                 number1, 123.45);\n        Console.WriteLine(output1);\n\n        string output2 = String.Format(new CustomNumberFormat(),\n                                 "\\"{0:G}\\" or \\"{0:S}\\". Standard formatting applied: {1:C2}",\n                                 number2, 123.45);\n        Console.WriteLine(output2);\n    }\n}\n\npublic class CustomNumberFormat : System.IFormatProvider, System.ICustomFormatter\n{\n    public object GetFormat(Type formatType)\n    {\n        if (formatType == typeof(ICustomFormatter))\n            return this;\n        else\n            return null;\n    }\n\n    public string Format(string fmt, object arg, System.IFormatProvider formatProvider)\n    {\n        // Provide default formatting if arg is not a decimal. \n        if (arg.GetType() != typeof(decimal))\n            try\n            {\n                return HandleOtherFormats(fmt, arg);\n            }\n            catch (FormatException e)\n            {\n                throw new FormatException(String.Format("The format of \'{0}\' is invalid.", fmt), e);\n            }\n\n        // Provide default formatting for unsupported format strings. \n        string ufmt = fmt.ToUpper(System.Globalization.CultureInfo.InvariantCulture);\n        if (!(ufmt == "G" || ufmt == "S"))\n            try\n            {\n                return HandleOtherFormats(fmt, arg);\n            }\n            catch (FormatException e)\n            {\n                throw new FormatException(String.Format("The format of \'{0}\' is invalid.", fmt), e);\n            }\n\n        // Convert argument to a string. \n        string result = ((decimal)arg).ToString("0#.000");\n\n        if (ufmt == "G")\n        {\n            var lastFormattedChar = result[result.Length - 1];\n            switch (lastFormattedChar)\n            {\n                case \'0\':\n                    result = result.Substring(0, result.Length - 1) + " ";\n                    break;\n            }\n\n            return result;\n        }\n        else if (ufmt == "S")\n        {\n            var lastFormattedChar = result[result.Length - 1];\n            switch (lastFormattedChar)\n            {\n                case \'0\':\n                    result = result.Substring(0, result.Length - 1);\n                    break;\n                case \'5\':\n                    result = result.Substring(0, result.Length - 1) + "\xc2\xbd";\n                    break;\n            }\n\n            return result;\n        }\n        else\n        {\n            return result;\n        }\n    }\n\n    private string HandleOtherFormats(string format, object arg)\n    {\n        if (arg is System.IFormattable)\n            return ((System.IFormattable)arg).ToString(format, System.Globalization.CultureInfo.CurrentCulture);\n        else if (arg != null)\n            return arg.ToString();\n        else\n            return String.Empty;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n