.Net 中的 Excel 样式条件数字格式

发布于 2024-10-31 07:24:32 字数 315 浏览 2 评论 0原文

我需要使用 Excel 条件格式字符串来格式化 .Net 应用程序中的数字。对于那些不熟悉它们的人来说,Excel 格式字符串如下所示:

[>=2]#,##0.0;[<=-2]-#,##0.0;#,##0.00

...应解释为“对于大于 2 的数字使用第一种格式,对于小于 -2 的数字使用第二种格式,对于其他所有数字使用第三种格式”。

在我继续构建自己的自定义格式字符串解析器之前,有谁知道 .Net 中是否有类似的东西可供我使用?我知道格式字符串有 ; 分隔符,但据我所知,它似乎无法考虑“负/正/零”之外的条件。

I need to use Excel conditional format strings to format numbers in a .Net application. For those unfamiliar with them, the Excel format strings look like this:

[>=2]#,##0.0;[<=-2]-#,##0.0;#,##0.00

...which should be interpreted as "use the first format for numbers greater than two, the second for numbers lower than -2 and the third format for everything else".

Before I go ahead and build my own custom format string parser, does anyone know if there is anything similar to this in .Net that I could use? I'm aware there's the ;-separator for format strings, but it doesn't seem be able to take conditions beyond "is negative/positive/zero" into account as far as I can tell.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

镜花水月 2024-11-07 07:24:33

您必须自己实现格式化,但您可以使用接口 IFormatProviderICustomFormatter

下面是一个关于如何做到这一点的示例。创建一个由多个 ConditionalFormat 对象组成的 ConditionalFormatter 类。 ConditionalFormat 类具有一个 Predicate 和一个 FormatConditionalFormatter 将按顺序搜索所有 ConditionalFormat 对象,找到第一个 Predicate 为 true 的对象,并使用关联的 Format。格式化程序使用字母“Z”作为格式字符串。

class ConditionalFormat<T> where T : IFormattable {
  public Func<T, Boolean> Predicate { get; set; }
  public String Format { get; set; }
  public static readonly Func<T, Boolean> Tautology = _ => true;
}

class ConditionalFormatter<T> : Collection<ConditionalFormat<T>>, IFormatProvider, ICustomFormatter
  where T : IFormattable {

  public const String FormatString = "Z";

  readonly CultureInfo cultureInfo;

  public ConditionalFormatter(IEnumerable<ConditionalFormat<T>> conditionalFormats)
    : this(conditionalFormats, null) { }

  public ConditionalFormatter(IEnumerable<ConditionalFormat<T>> conditionalFormats, CultureInfo cultureInfo)
    : base(conditionalFormats.ToList()) {
    this.cultureInfo = cultureInfo;
  }

  public Object GetFormat(Type formatType) {
    return formatType == typeof(ICustomFormatter) ? this : null;
  }

  public String Format(String format, Object arg, IFormatProvider formatProvider) {
    if (arg.GetType() != typeof(T))
      return HandleOtherFormats(format, arg);
    var formatUpperCase = format.ToUpperInvariant();
    if (formatUpperCase != FormatString)
      return HandleOtherFormats(format, arg);

    var value = (T) arg;
    foreach (var conditionalFormat in this)
      if (conditionalFormat.Predicate(value))
        return ((IFormattable) value).ToString(conditionalFormat.Format, cultureInfo);

    throw new InvalidOperationException(String.Format("No format matching value {0}.", value));
  }

  String HandleOtherFormats(String format, Object arg) {
    var formattable = arg as IFormattable;
    if (formattable != null)
      return formattable.ToString(format, this.cultureInfo);
    else if (arg != null)
      return arg.ToString();
    else
      return String.Empty;
  }

}

该类是通用的,您必须创建一个与您想要格式化的类型相匹配的实例。以下是使用 Double 的示例:

var conditionalFormatter = new ConditionalFormatter<Double>(
  new[] {
    new ConditionalFormat<Double> {
      Predicate = d => -2 < d && d < 2,
      Format = "#,##0.00"
    },
    new ConditionalFormat<Double> {
      Predicate = ConditionalFormat<Double>.Tautology,
      Format = "#,##0.0"
    },
  }
);
var value = 1234.5678;
var formattedValue = String.Format(conditionalFormatter, "Value is {0:Z}", value);

You will have to implement the formatting yourself but you can hook into .NET's existing formatting framework by using the interfaces IFormatProvider and ICustomFormatter.

Here is one example on how to do that. A ConditionalFormatter class is created that consists of several ConditionalFormat objects. The ConditionalFormat class has a Predicate and a Format. The ConditionalFormatter will search all ConditionalFormat objects in sequence finding the first where Predicate is true and use the associated Format. The formatter uses the letter 'Z' as the format string.

class ConditionalFormat<T> where T : IFormattable {
  public Func<T, Boolean> Predicate { get; set; }
  public String Format { get; set; }
  public static readonly Func<T, Boolean> Tautology = _ => true;
}

class ConditionalFormatter<T> : Collection<ConditionalFormat<T>>, IFormatProvider, ICustomFormatter
  where T : IFormattable {

  public const String FormatString = "Z";

  readonly CultureInfo cultureInfo;

  public ConditionalFormatter(IEnumerable<ConditionalFormat<T>> conditionalFormats)
    : this(conditionalFormats, null) { }

  public ConditionalFormatter(IEnumerable<ConditionalFormat<T>> conditionalFormats, CultureInfo cultureInfo)
    : base(conditionalFormats.ToList()) {
    this.cultureInfo = cultureInfo;
  }

  public Object GetFormat(Type formatType) {
    return formatType == typeof(ICustomFormatter) ? this : null;
  }

  public String Format(String format, Object arg, IFormatProvider formatProvider) {
    if (arg.GetType() != typeof(T))
      return HandleOtherFormats(format, arg);
    var formatUpperCase = format.ToUpperInvariant();
    if (formatUpperCase != FormatString)
      return HandleOtherFormats(format, arg);

    var value = (T) arg;
    foreach (var conditionalFormat in this)
      if (conditionalFormat.Predicate(value))
        return ((IFormattable) value).ToString(conditionalFormat.Format, cultureInfo);

    throw new InvalidOperationException(String.Format("No format matching value {0}.", value));
  }

  String HandleOtherFormats(String format, Object arg) {
    var formattable = arg as IFormattable;
    if (formattable != null)
      return formattable.ToString(format, this.cultureInfo);
    else if (arg != null)
      return arg.ToString();
    else
      return String.Empty;
  }

}

The class is generic and you will have to create an instance that matches the type you want to format. Here is an example using Double:

var conditionalFormatter = new ConditionalFormatter<Double>(
  new[] {
    new ConditionalFormat<Double> {
      Predicate = d => -2 < d && d < 2,
      Format = "#,##0.00"
    },
    new ConditionalFormat<Double> {
      Predicate = ConditionalFormat<Double>.Tautology,
      Format = "#,##0.0"
    },
  }
);
var value = 1234.5678;
var formattedValue = String.Format(conditionalFormatter, "Value is {0:Z}", value);
奢华的一滴泪 2024-11-07 07:24:33

另一个有趣的解决方案:有一个名为 SmartFormat 的库,其中包含条件格式化程序。

您的示例的语法为:

var output = Smart.Format("{0:>=2?{0:#,##0.0}|<=-2?{0:-#,##0.0}|{0:#,##0.00}}", value);

有关语法的说明,请参阅 ConditionalFormatter。

但是,如果您想支持上面提到的确切 Excel 语法,您可以下载 SmartFormat 源并修改 ConditionalFormatter 以支持您的语法。它使用正则表达式来解析每个(条件),因此很容易修改。

Another interesting solution: there is a library called SmartFormat that includes a conditional formatter.

The syntax for your example would be:

var output = Smart.Format("{0:>=2?{0:#,##0.0}|<=-2?{0:-#,##0.0}|{0:#,##0.00}}", value);

For an explanation of the syntax, see ConditionalFormatter.

However, if you wanted to support the exact Excel syntax you mentioned above, you could download the SmartFormat source and modify the ConditionalFormatter to support your syntax. It uses a Regex to parse each (condition), so that would be easy to modify.

哭了丶谁疼 2024-11-07 07:24:33

你需要编写 if/else if 语句。没有直接的方法

string numberToFormat = //state your number here
string FormattedString = null;

if(numberToFormat >2)
{
    //Format 1
    //e.g. FormattedString = String.Format("{0:0.00}", numberToFormat);

}

else if(numberToFormat < -2)
{
     //Format 2
}

else 
{
     // Format Default     
}

这是最短的方法。但当然,您可以获得自己的条件格式化程序。

you need to write if/else if statements. There is no direct way

string numberToFormat = //state your number here
string FormattedString = null;

if(numberToFormat >2)
{
    //Format 1
    //e.g. FormattedString = String.Format("{0:0.00}", numberToFormat);

}

else if(numberToFormat < -2)
{
     //Format 2
}

else 
{
     // Format Default     
}

This is the shortest way. But of course you can get your own conditional formatter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文