diff --git a/docs/input/widgets/breakdownchart.md b/docs/input/widgets/breakdownchart.md
index f00ec0f..a30fd54 100644
--- a/docs/input/widgets/breakdownchart.md
+++ b/docs/input/widgets/breakdownchart.md
@@ -132,3 +132,13 @@ AnsiConsole.Write(new BreakdownChart()
.AddItem(new Fruit("Mango", 3, Color.Orange4))
.AddItems(items));
```
+
+### Add value formatter to chart numbers
+
+```csharp
+var chart = new BreakdownChart();
+chart.UseValueFormater(value => value.ToString("N0"));
+
+// This can be simplified as extension methods are chainable.
+var chart = new BreakdownChart().UseValueFormatter(v => v.ToString("N0"));
+```
diff --git a/src/Spectre.Console/Extensions/BarChartExtensions.cs b/src/Spectre.Console/Extensions/BarChartExtensions.cs
index 484f81b..8029981 100644
--- a/src/Spectre.Console/Extensions/BarChartExtensions.cs
+++ b/src/Spectre.Console/Extensions/BarChartExtensions.cs
@@ -116,6 +116,43 @@ public static class BarChartExtensions
return chart;
}
+ ///
+ /// Sets the value formatter for the bar chart using culture info.
+ ///
+ /// The bar chart.
+ /// The value formatter function with culture info.
+ /// The same instance so that multiple calls can be chained.
+ public static BarChart UseValueFormatter(this BarChart chart, Func? func)
+ {
+ if (chart is null)
+ {
+ throw new ArgumentNullException(nameof(chart));
+ }
+
+ chart.ValueFormatter = func;
+ return chart;
+ }
+
+ ///
+ /// Sets the value formatter for the bar chart.
+ ///
+ /// The bar chart.
+ /// The value formatter to use.
+ /// The same instance so that multiple calls can be chained.
+ public static BarChart UseValueFormatter(this BarChart chart, Func? func)
+ {
+ if (chart is null)
+ {
+ throw new ArgumentNullException(nameof(chart));
+ }
+
+ chart.ValueFormatter = func != null
+ ? (value, _) => func(value)
+ : null;
+
+ return chart;
+ }
+
///
/// Sets the width of the bar chart.
///
diff --git a/src/Spectre.Console/Widgets/Charts/BarChart.cs b/src/Spectre.Console/Widgets/Charts/BarChart.cs
index b6367b7..c09a997 100644
--- a/src/Spectre.Console/Widgets/Charts/BarChart.cs
+++ b/src/Spectre.Console/Widgets/Charts/BarChart.cs
@@ -43,6 +43,11 @@ public sealed class BarChart : Renderable, IHasCulture
/// Defaults to null, which corresponds to largest value in chart.
public double? MaxValue { get; set; }
+ ///
+ /// Gets or sets the function used to format the values of the bar chart.
+ ///
+ public Func? ValueFormatter { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -90,6 +95,7 @@ public sealed class BarChart : Renderable, IHasCulture
AsciiBar = '█',
ShowValue = ShowValues,
Culture = Culture,
+ ValueFormatter = ValueFormatter,
});
}
diff --git a/src/Spectre.Console/Widgets/ProgressBar.cs b/src/Spectre.Console/Widgets/ProgressBar.cs
index 2e6ea54..3587eb5 100644
--- a/src/Spectre.Console/Widgets/ProgressBar.cs
+++ b/src/Spectre.Console/Widgets/ProgressBar.cs
@@ -15,6 +15,7 @@ internal sealed class ProgressBar : Renderable, IHasCulture
public bool ShowValue { get; set; }
public bool IsIndeterminate { get; set; }
public CultureInfo? Culture { get; set; }
+ public Func? ValueFormatter { get; set; }
public Style CompletedStyle { get; set; } = Color.Yellow;
public Style FinishedStyle { get; set; } = Color.Green;
@@ -50,7 +51,7 @@ internal sealed class ProgressBar : Renderable, IHasCulture
var barCount = Math.Max(0, (int)(width * (completedBarCount / MaxValue)));
// Show value?
- var value = completedBarCount.ToString(Culture ?? CultureInfo.InvariantCulture);
+ var value = ValueFormatter != null ? ValueFormatter(completedBarCount, Culture ?? CultureInfo.InvariantCulture) : completedBarCount.ToString(Culture ?? CultureInfo.InvariantCulture);
if (ShowValue)
{
barCount = barCount - value.Length - 1;
diff --git a/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Formatted.Output.verified.txt b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Formatted.Output.verified.txt
new file mode 100644
index 0000000..de94de7
--- /dev/null
+++ b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Formatted.Output.verified.txt
@@ -0,0 +1 @@
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9,000
diff --git a/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Render.Output.verified.txt b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Render.Output.verified.txt
new file mode 100644
index 0000000..d572628
--- /dev/null
+++ b/test/Spectre.Console.Tests/Expectations/Widgets/ProgressBar/Render.Output.verified.txt
@@ -0,0 +1 @@
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9000
\ No newline at end of file
diff --git a/test/Spectre.Console.Tests/Unit/Widgets/ProgressBarTests.cs b/test/Spectre.Console.Tests/Unit/Widgets/ProgressBarTests.cs
new file mode 100644
index 0000000..7decab6
--- /dev/null
+++ b/test/Spectre.Console.Tests/Unit/Widgets/ProgressBarTests.cs
@@ -0,0 +1,51 @@
+namespace Spectre.Console.Tests.Unit;
+
+[UsesVerify]
+[ExpectationPath("Widgets/ProgressBar")]
+public class ProgressBarTests
+{
+ [Fact]
+ [Expectation("Render")]
+ public async Task Should_Render_Correctly()
+ {
+ // Given
+ var console = new TestConsole();
+
+ var bar = new ProgressBar()
+ {
+ Width = 60,
+ Value = 9000,
+ MaxValue = 9000,
+ ShowValue = true,
+ };
+
+ // When
+ console.Write(bar);
+
+ // Then
+ await Verifier.Verify(console.Output);
+ }
+
+ [Fact]
+ [Expectation("Formatted")]
+ public async Task Should_Render_ValueFormatted()
+ {
+ // Given
+ var console = new TestConsole();
+
+ var bar = new ProgressBar()
+ {
+ Width = 60,
+ Value = 9000,
+ MaxValue = 9000,
+ ShowValue = true,
+ ValueFormatter = (value, _) => value.ToString("N0"),
+ };
+
+ // When
+ console.Write(bar);
+
+ // Then
+ await Verifier.Verify(console.Output);
+ }
+}
\ No newline at end of file