C# 扩展内置类型的自定义格式功能
我对C# 扩展内置类型的自定义格式功能,c#,.net,formatting,C#,.net,Formatting,我对decimal值有一些相当棘手的格式要求。简而言之:除非第三个小数点是5,否则显示到小数点后两位,并带有尾随空格,在这种情况下,显示到小数点后三位 这种格式也需要相当灵活。具体而言,尾随空格并不总是需要的,当第三个小数点为“5”时,最好使用“½” 示例: 1.13将显示为带空格的“01.13”,或不带空格的“01.13” 1.315将显示为“01.315”或“01.31½” 我需要在其他不相关的UI中一致地使用此逻辑。我暂时将其编写为WPF值转换器,但这只是为了演示: public se
decimal
值有一些相当棘手的格式要求。简而言之:除非第三个小数点是5,否则显示到小数点后两位,并带有尾随空格,在这种情况下,显示到小数点后三位
这种格式也需要相当灵活。具体而言,尾随空格并不总是需要的,当第三个小数点为“5”时,最好使用“½”
示例:
- 1.13将显示为带空格的“01.13”,或不带空格的“01.13”
- 1.315将显示为“01.315”或“01.31½”
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层中使用它。我最初的想法是一个自定义格式提供程序,然后可以从绑定中使用它:
<TextBlock Text="{Binding Value, FormatString=WHATEVER}"/>
这种类型将封装我需要的额外格式化功能。然而,现在我又遇到了另一个难题:在我的ToString
实现中,如何利用现有的decimal.ToString(string,IFormatProvider)
格式功能而不干扰我自己的?这看起来会非常混乱,这让我倾向于一个更有限的解决方案,只定义我的Price
结构的“G”(两个或三个小数位,没有尾随空格)和“s”(与“G”相同,但必要时有尾随空格)
有没有人能告诉我,是否有一种方法可以让我不费吹灰之力地实现这种自定义格式功能?有关详细信息,请参阅
// "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
实现:
<TextBlock Text="{Binding Value, Mode=OneWay, Converter={StaticResource PriceConverter}, ConverterParameter=#0.00F"/>
设置字符串格式的唯一前缀是否可以区分自定义格式和默认格式?像自定义的“.00”和默认的“#0.00”(如果第一个字符是特殊的-使用自定义…)@Alexei:只有当我想完全拥有格式化代码时,这才有效,但我更愿意委托给内置格式化,然后用我自己的零碎补充。否则,我将不得不重新实现大量内置功能。试试这个@amald:谢谢,但这对我的场景没有帮助。我不能只删除部分格式字符串,因为它们对我的特定格式规则很重要。我甚至不能假设某些字符是我的指示符,因为自定义格式允许文本字符串出现在格式字符串中。因此,我需要解析格式字符串,除非有某种方式获得它的解析表达式。请避免使用“密封”或“最终”,认为您的源代码,迟早可以按层次扩展,谢谢,但我想您没有注意到我的问题:)我的想法围绕<代码>“g”/代码> /代码> s。
是一个后备计划—我采用了一个比您建议的简单得多的实现(IFormattable
是关键,并确保它也可以从绑定中工作,而您的建议没有转换器是不行的)。我真正想要的是支持自定义格式,而不是标准格式。请参阅我问题前面的示例,如“#0.005”
。当您尝试用自己的格式补充内置的自定义格式时,您最终不得不重新实现许多您更愿意委托的内容。
<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
}
}