Assuming we have a viewmodel property MyMoney
. How can I format it in its view xaml as a currency with no trailing zeroes?
For example:
MyMoney = 1; //$1
MyMoney = 1.2 //$1.2
I've tried the following in xaml (e.g. <TextBox Text="{Binding MyMoney, StringFormat=..."/>
) but it doesn't satisfy all the conditions:
StringFormat=C
shows currency but also trailing zeroes.StringFormat=C0
shows currency but shows only the whole number.StringFormat={}{0:0.##}
does not show trailing zeroes but not as currency.StringFormat={}{0:$0.##}
does not show trailing zeroes but hard-coded $
. We should be able to cater current locale/culture's currency.
I think you pretty much need to use a converter here because of the requirement to remove trailing zeroes. This probably needs a bit more work but:
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace wpf_99
{
public class CurrencyFormatConverter : MarkupExtension, IValueConverter
{
// public double Multiplier { get; set; } You could pass parameters to properties.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol + System.Convert.ToDecimal(value).ToString("0.##");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string input = value.ToString();
if(!char.IsDigit(input[0]))
{
input= input.Substring(1);
}
if(input.Length == 0)
{
return 0;
}
return Decimal.Parse(input);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}
Usage
<TextBox Text="{Binding Money,UpdateSourceTrigger=PropertyChanged, Converter={local:CurrencyFormatConverter}}"/>
Probably you would be knowing that the "C" in string format works according to the locale/culture set in your local machine.However to answer your question,I would suggest to put the trailing zeroes removal code at property level and keep the string formatting at xaml level as simple as possible.for e.g
At xaml level :
<TextBox x:Name="TextBox_Curr" Height="50" Text="{Binding Money,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay,StringFormat={}{0:C}}" Margin="120,134,156,135"/>
At Model level (just a raw code):
private decimal _money;
public decimal Money
{
get { return _money; }
set {
_money = value;
_money.ToString("0.##");
NotifyPropertyChanged("Money"); }
}
It worked for me while running the sample code.
Also if you do not wish to change the system settings you could probably go for a forced culture setting.
public static class Cultures
{
public static readonly CultureInfo UnitedKingdom =
CultureInfo.GetCultureInfo("en-GB");
}
Then:
Money.ToString("C", Cultures.UnitedKingdom)
First, I would like to give credit to @Andy for his answer which led me to use IValueConverter
.
I'm posting my solution which offers the following benefits:
Leverage C#'s "C" format specifier
a. Consider negative values (e.g.
-1 --> ($1)
)b. Cater varying current locale/culture
Bind to multiple data types (
decimal
,double
,int
, etc).Return
DependencyProperty.UnsetValue
whenConvertBack
is unable to produce a value.
All of the above follow how StringFormat=c
will behave in wpf (e.g. in a TextBox
) except the converter removes trailing zeroes as desired.
public class CurrencyFormatConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
System.Convert.ToDecimal(value).ToCurrency(culture);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
switch (Type.GetTypeCode(targetType))
{
case TypeCode.Decimal:
return Decimal.TryParse(value.ToString(), NumberStyles.Currency, culture, out var @decimal)
? @decimal
: DependencyProperty.UnsetValue;
case TypeCode.Double:
return Double.TryParse(value.ToString(), NumberStyles.Currency, culture, out var @double)
? @double
: DependencyProperty.UnsetValue;
case TypeCode.Int16:
return Int16.TryParse(value.ToString(), NumberStyles.Currency, culture, out var int16)
? int16
: DependencyProperty.UnsetValue;
case TypeCode.Int32:
return Int32.TryParse(value.ToString(), NumberStyles.Currency, culture, out var int32)
? int32
: DependencyProperty.UnsetValue;
case TypeCode.Int64:
return Int64.TryParse(value.ToString(), NumberStyles.Currency, culture, out var int64)
? int64
: DependencyProperty.UnsetValue;
case TypeCode.Single:
return Single.TryParse(value.ToString(), NumberStyles.Currency, culture, out var single)
? single
: DependencyProperty.UnsetValue;
case TypeCode.UInt16:
return UInt16.TryParse(value.ToString(), NumberStyles.Currency, culture, out var uint16)
? uint16
: DependencyProperty.UnsetValue;
case TypeCode.UInt32:
return UInt32.TryParse(value.ToString(), NumberStyles.Currency, culture, out var uint32)
? uint32
: DependencyProperty.UnsetValue;
case TypeCode.UInt64:
return UInt64.TryParse(value.ToString(), NumberStyles.Currency, culture, out var uint64)
? uint64
: DependencyProperty.UnsetValue;
default:
throw new NotSupportedException($"Converting currency string to target type {targetType} is not supported.");
}
}
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
public static class DecimalExtensions
{
/// <summary>
/// Converts a numeric value to its equivalent currency string representation using the specified culture-specific format information.
/// </summary>
/// <param name="value">The value to be converted.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <returns>The currency string representation of the value as specified by <paramref name="provider" />.</returns>
public static string ToCurrency(this decimal value, IFormatProvider provider) =>
/// Use "1" (or "-1" if value is negative)
/// as a placeholder for the actual value.
(value < 0 ? -1 : 1)
/// Format as a currency using the "C" format specifier.
.ToString("C0", provider)
/// Convert the absolute value to its string representation
/// then replace the placeholder "1".
/// We used absolute value since the negative sign
/// is already converted to its string representation
/// using the "C" format specifier.
.Replace("1", Math.Abs(value).ToString("#,0.############################", provider));
}
来源:https://stackoverflow.com/questions/53783899/format-as-currency-with-no-trailing-zeroes-in-wpf-xaml