MS documentation says it this is not possible... Hoping I missed something in the docs.
want to format decimal number as: -9,999.000
using StringFormat={}{0:#,0.000}
works, but this "rounds" the input where as I want to "truncate" the input.
To test, in WPF form applied this string format to a TextBox bound to a decimal property.
When I enter test values, the actual formatted value doesnt match the expected formatted value as the format string automatically "rounds" the decimal values.
Enter Expected Actual
1111.999 1,111.999 1,111.999
1111.9999 1,111.999 1,112,000
MS documentation clearly states that "0" placeholders will always round. great, but I dont want that behavior.
Question: is there a format string to show 3 decimal places (with trailing zeros) without using the "0"? or is there a format string that truncates at 3 decimal places instead of rounding at 3 decimal places?
I did try StringFormat={}{0:#,0.###}
but that removes trailing zeros. (and also StringFormat={}{F3}
).
Enter Expected Actual
1111.100 1,111.100 1,111.1
closest solution I could find has been [https://stackoverflow.com/questions/16914224/wpf-textbox-to-enter-decimal-values] but none of the answers really deal with the rounding versus truncate.
UPDATE
xaml code where StringFormat is used.
<TextBox Text="{Binding
TemplateNumber,
StringFormat={}{0:F3},
ValidatesOnDataErrors=True,
NotifyOnValidationError=True,
UpdateSourceTrigger=PropertyChanged,
Delay=500}"
/>
UPDATE 2
the IValueConverter Convert function to truncate instead of round...
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return string.Empty;
int decimalPlaces = 3; // HACK: hardcoded, needs to be parameter
// decimal decimalShift = (decimal)Math.Pow(10, decimalPlaces);
decimal decimalValue = (decimal)value;
// decimal decimalWhole = Math.Truncate(decimalValue);
// decimal decimalFraction = Math.Truncate((decimalValue - decimalWhole) * decimalShift);
string format;
string result;
// simpler truncation approach
decimal d = Math.Round(decimalValue, decimalPlaces, MidpointRounding.ToZero);
format = "{0:N" + decimalPlaces.ToString() + "}";
result = string.Format(format, d);
/*
if (decimalPlaces > 0)
{
format = "{0:N0}.{1:" + new string('0', decimalPlaces) + "}";
result = string.Format(format, decimalWhole, decimalFraction);
}
else // whole number only
{
format = "{0:N0}";
result = string.Format(format, decimalWhole);
}
*/
return result;
}
UPDATE 3
After further testing... the "displayed value" is now correct, but the "bound property" is not.
the property TemplateNumber
in the xaml code below has the value entered into the text box, not the value displayed by the text box (as modified by the IValueConverter.
<TextBox
Text="{Binding TemplateNumber,
Converter={converters:DecimalConverter Precision=4},
ValidatesOnDataErrors=True,
NotifyOnValidationError=True,
UpdateSourceTrigger=PropertyChanged,
Delay=500}"
/>
that is:
need to fix the rounding on the convert back too....
public object ConvertBack(object value, Type targetType, object
parameter, CultureInfo culture)
{
// note: remove any extra white space
if (decimal.TryParse(value?.ToString().Replace(" ",""), out decimal d))
{
int decimalPlaces = Precision ?? 0;
decimal result = Math.Round(d, decimalPlaces, MidpointRounding.ToZero);
return result;
}
return null; // type is decimal? so null is legit
}
UPDATE 4
added Precision as a property to remove hard coded value. used MarkupExtension approach. this is really simple to add. cound not figure out how to add multiple properties (xaml barfs: future issue to resolve).
FYI the MarkupExtension to add Precision.
public class DecimalConverter : MarkupExtension, IValueConverter
{
#region Markup Extension
public int? Precision { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
#endregion // markup extension
...
the xaml is as per update 3.
You can use IValueConverter for example:
class DecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.Format("{0:F3}", value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (decimal.TryParse(value?.ToString().Replace(" ", ""), out decimal num1))
return num1;
return null;
}
}
Add it to your control
<UserControl.Resources>
<local:DecimalConverter x:Key="MyDecimalConverter"/>
</UserControl.Resources>
Use in textbox
<TextBox Text={Binding ...., Converter={StaticResource MyDecimalConverter}}/>