簡體   English   中英

擴展內置類型的自定義格式功能

[英]Extending the custom formatting capabilities of built-in types

我對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();
    }
}

我現在正試圖將其提取到一個更基本的構建塊中,我可以在整個UI層中使用它。 我最初的想法是一個自定義格式提供程序,然后我可以從Binding使用它:

<TextBlock Text="{Binding Value, FormatString=WHATEVER}"/>

我們的想法是,格式字符串可能類似於“#0.005”,表示只顯示第三個小數位,如果它是5,或“#0.00F”,它試圖將第三個小數表示為分數。 但是,我無法從綁定中找到使用特定格式提供程序的方法,這似乎是對我的一個主要限制,但也許我錯過了什么......?

經過更多的實驗和調查,我得出結論,我唯一的選擇是定義我自己的類型:

public struct Price : IFormattable

這種類型將封裝我需要的額外格式化功能。 但是,現在我有另一個難題:在我的ToString實現中,如何利用decimal.ToString(string, IFormatProvider)的現有格式化功能而不干擾我自己的? 看起來這將是相當混亂的,它導致我傾向於更有限的解決方案,只是定義“G”(兩個或三個小數位,沒有尾隨空格)和“S”(與“G”相同,但我的Price結構的格式。如果需要,有尾隨空格)格式。

誰能告訴我是否有辦法讓我做這種自定義格式化功能而不會有太多麻煩?

有關詳細信息,請參閱http://msdn.microsoft.com/en-us/library/system.iformatprovider.aspx

// "01.13 " or "01.13". Standard formatting applied: $123.45
// "01.315" or "01.31½". Standard formatting applied: $123.45

public class Test
{
    void Main()
    {
        decimal number1 = 1.13M;
        decimal number2 = 1.315M;

        string output1 = String.Format(new CustomNumberFormat(),
                                 "\"{0:G}\" or \"{0:S}\". Standard formatting applied: {1:C2}",
                                 number1, 123.45);
        Console.WriteLine(output1);

        string output2 = String.Format(new CustomNumberFormat(),
                                 "\"{0:G}\" or \"{0:S}\". Standard formatting applied: {1:C2}",
                                 number2, 123.45);
        Console.WriteLine(output2);
    }
}

public class CustomNumberFormat : System.IFormatProvider, System.ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }

    public string Format(string fmt, object arg, System.IFormatProvider formatProvider)
    {
        // Provide default formatting if arg is not a decimal. 
        if (arg.GetType() != typeof(decimal))
            try
            {
                return HandleOtherFormats(fmt, arg);
            }
            catch (FormatException e)
            {
                throw new FormatException(String.Format("The format of '{0}' is invalid.", fmt), e);
            }

        // Provide default formatting for unsupported format strings. 
        string ufmt = fmt.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
        if (!(ufmt == "G" || ufmt == "S"))
            try
            {
                return HandleOtherFormats(fmt, arg);
            }
            catch (FormatException e)
            {
                throw new FormatException(String.Format("The format of '{0}' is invalid.", fmt), e);
            }

        // Convert argument to a string. 
        string result = ((decimal)arg).ToString("0#.000");

        if (ufmt == "G")
        {
            var lastFormattedChar = result[result.Length - 1];
            switch (lastFormattedChar)
            {
                case '0':
                    result = result.Substring(0, result.Length - 1) + " ";
                    break;
            }

            return result;
        }
        else if (ufmt == "S")
        {
            var lastFormattedChar = result[result.Length - 1];
            switch (lastFormattedChar)
            {
                case '0':
                    result = result.Substring(0, result.Length - 1);
                    break;
                case '5':
                    result = result.Substring(0, result.Length - 1) + "½";
                    break;
            }

            return result;
        }
        else
        {
            return result;
        }
    }

    private string HandleOtherFormats(string format, object arg)
    {
        if (arg is System.IFormattable)
            return ((System.IFormattable)arg).ToString(format, System.Globalization.CultureInfo.CurrentCulture);
        else if (arg != null)
            return arg.ToString();
        else
            return String.Empty;
    }
}

嘗試傳遞格式提供程序作為IValueConverter.Convert實現中的parameter參數:

<TextBlock Text="{Binding Value, Mode=OneWay, Converter={StaticResource PriceConverter}, ConverterParameter=#0.00F"/>

然后,在您的轉換器內:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    string formatString = parameter as string;

    if(formatString != null)
    {
        // Your code here
    }
    else
    {
        // Whatever you want to do here
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM