I'm trying to put value (double type) in textbox and i would like to get a custom format.
To explain, i put 10001.1 or 10001,1 and i would like to have 10 001,1 in my texblock.
At the moment my result is 10,001.00 i don't know how can i change the format properly.
My textblock is bind on this :
private double? _Total = null;
public double? Total
{
get
{
if (FiltreAffaire != null)
_Total = ListeAfterFiltre.Sum(CF => CF.CommandeFournisseurLignes.Where(CFL => CFL.Affaire.Numero == FiltreAffaire.Split('-').First().Trim()).Sum(CFL => CFL.Total_HT));
else
return _ListeAfterFiltre.Sum(CF => CF.Montant_HT);
return _Total;
}
set
{
_Total = value;
}
}
Thanks
You can use a simple StringFormat to get your required value... if you are in a country that uses the comma as a decimal separator:
DoubleValue = 10001.1;
...
<TextBlock Text="{Binding DoubleValue, StringFormat={}{0:### ###.#}}" />
In England, this would output the following:
10 001.1
In France, this should output:
10 001,1
You can also do this in code using the NumberFormatInfo class:
NumberFormatInfo numberFormatInfo = new NumberFormatInfo();
numberFormatInfo.NumberDecimalDigits = 1;
numberFormatInfo.NumberDecimalSeparator = ",";
numberFormatInfo.NumberGroupSeparator = " ";
numberFormatInfo.NumberGroupSizes = new int[] { 3 };
string value = DoubleValue.ToString("N", numberFormatInfo);
This outputs:
10 001,1
I finally use something like this (on Textbox) :
CultureInfo ci = (CultureInfo)Thread.CurrentThread.CurrentUICulture.Clone();
ci.NumberFormat.NumberDecimalSeparator = ".";
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture = ci;
and converter to set value.
ValueConverter="{StaticResource StringToDoubleConverterN3Key}"
public class StringToDoubleConverterN3 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((double)value).ToString("N3").Replace('.', ',');
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Related
I'm using the following code to mask an entry field for currency. The only issue is I want to limit the input to 2 decimal places. How can I modify the code the accomplish this?
public class CurrencyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Decimal.Parse(value.ToString()).ToString("C");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string valueFromString = Regex.Replace(value.ToString(), #"\D", "");
if (valueFromString.Length <= 0)
return 0m;
long valueLong;
if (!long.TryParse(valueFromString, out valueLong))
return 0m;
if (valueLong <= 0)
return 0m;
return valueLong / 100m;
}
}
If you want to limit two decimals,you could add the conditions in the entry textchanged event.
like:
<Entry x:Name="entry" Keyboard="Numeric" HorizontalOptions="StartAndExpand" WidthRequest="600" TextChanged="Entry_TextChanged"></Entry>
in the behind code:
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue.Contains("."))
{
if (e.NewTextValue.Length - 1 - e.NewTextValue.IndexOf(".") > 2)
{
var s = e.NewTextValue.Substring(0, e.NewTextValue.IndexOf(".") + 2 + 1);
entry.Text =s;
entry.SelectionLength = s.Length;
}
}
}
the effect:
I'm using a Type Converter to do some string manipulations on text being bound to TextBlocks in a UWP app. I used a type converter class that I have used in a number of UWP apps with no issues. But in a new UWP app I keep seeing the issue where the Binding value gets passed back and no changes have been made in the type converter to my passed values. I had been using a type converter that takes parameters to take custom actions and that isn't working as well as a simple new type converter I created. The odd thing is that the type converters work at run-time but as ignored at design-time.
Here are the parts of my code and XAML:
This is a data class that creates sample data for this repro to use for my bindings:
public class Alerts_DataClass
{
public string id { get; set; }
public string severity { get; set; }
public string status { get; set; }
public string title { get; set; }
public DateTime lastEventTime { get; set; }
}
public class AlertsSampleData : Alerts_DataClass
{
public AlertsSampleData()
{
Alerts_DataClass myAlert = new Alerts_DataClass { id = "636531490526731751_2124891065", title = "Category Type XVI", severity = "Medium", lastEventTime = new DateTime(2018, 2, 02) };
this.id = myAlert.id;
this.status = myAlert.status;
this.title = myAlert.title;
this.lastEventTime = myAlert.lastEventTime;
this.severity = myAlert.severity;
}
}
Here is the XAML:
<Page
x:Class="NestedBindingsTester.TypeConverterTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:NestedBindingsTester"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="using:System"
mc:Ignorable="d">
<Page.Resources>
<local:StringFormatConverterWithChangeType x:Key="StringFormatConverter"/>
<local:DebugDataBindingConverter x:Key="DebugBinding"/>
<local:ValueToStringConverter x:Key="valueToStringConverter" />
<local:AlertsSampleData x:Key="data2"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="103*"/>
<RowDefinition Height="897*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="1" d:DataContext="{StaticResource data2}">
<TextBlock Text="{Binding title, ConverterParameter=lower, Converter={StaticResource StringFormatConverter}, Mode=TwoWay}" Style="{StaticResource BaseTextBlockStyle}" Margin="50,0,0,0" />
<TextBlock Text="{Binding title, Converter={StaticResource valueToStringConverter}, Mode=TwoWay}" Style="{StaticResource BaseTextBlockStyle}" Margin="50,0,0,0" />
</StackPanel>
</Grid>
</Page>
And finally here are the two Type Converter modules. The first is the simple one, valueToStringConverter which I just have take any String text passed and return it as lower case:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Data;
namespace NestedBindingsTester
{
public sealed class ValueToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
//return value?.ToString();
string strThisTest = value.ToString().ToLower();
return strThisTest;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException("Converting from string is not supported.");
}
}
}
And the original type converter I was using that takes parameters as switches for various actions called StringFormatConverter. In my XAML example I am passing the parameter of 'lower' to tell it to convert the string value to lowercase:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Windows.UI.Xaml.Data;
namespace NestedBindingsTester
{
public class DebugDataBindingConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
Debugger.Break();
return "This is";
////return value;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
Debugger.Break();
return "it";
////return value;
}
public object Convert(object value, Type targetType, object parameter, string language)
{
////throw new NotImplementedException();
return "Hello moto";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Sample usage:
/// Text="{Binding Budget, Mode=TwoWay, Converter={StaticResource StringFormatConverterWithChangeType}, ConverterParameter='\{0:C2\}'}"
/// </summary>
public class StringFormatConverterWithChangeType : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, string language)
{
// Used in UWP app
//
if (value != null)
{
// Arbitrarily remove dollar sign.
//value = value.ToString().Replace("$", "");
}
else
{
value = "**NULL**";
}
if (parameter != null)
{
string formatString = parameter.ToString();
if (!string.IsNullOrEmpty(formatString))
{
string strValue = value.ToString();
if (strValue != null && !string.IsNullOrWhiteSpace(strValue))
{
switch (formatString)
{
case "%": // Add %
return String.Format("{0}%", value);
case "mi": // Add miles
return String.Format("{0} miles", value);
case "caps": // Convert to Initial caps
return UpperCaseFirstCharacter(value.ToString());
case "lower": // Convert to Initial caps
return value.ToString().ToLower();
case "LiGa": // Convert from Liters to Gallons
{
//
// Convert kilometers to miles.
// 1 liter = 0.264172052 US gallons
//
double lConversion = 0.264172052;
double liters = System.Convert.ToDouble(strValue);
double gallons = lConversion * liters;
return String.Format("{0:N1} gallons", gallons);
}
case "TimeMath": // Compute time remaining until fully charged
{
int Minutes = System.Convert.ToInt16(value);
DateTime currentTime = DateTime.Now;
TimeSpan twoAndAHalfHours = new TimeSpan(0, Minutes, 0);
DateTime estimatedFullChargedTime = DateTime.Now.AddMinutes(Minutes);
string formattedTime = estimatedFullChargedTime.ToString("HH:mm");
return formattedTime;
}
case "UTCtoShortDate": // 02/03.2018
{
string utcDate = value.ToString(); // "2018-01-26T02:17:23.5099802Z";
DateTime dateTime;
DateTime.TryParse(utcDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out dateTime);
Debug.WriteLine(dateTime.ToString());
string strWDATP_Date = String.Format("{0:MM.dd.yyyy}", dateTime);
return strWDATP_Date;
}
case "{0:d}": // Short date.
case "{0:D}": // Long date.
////return String.Format(culture, formatString, System.Convert.ChangeType(value, typeof(DateTime), null));
return String.Format("US", formatString, System.Convert.ChangeType(value, typeof(DateTime), null));
}
}
else
{
return String.Format("US", formatString, value);
}
}
}
return value.ToString();
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
// Arbitrarily remove dollar sign.
//value = value.ToString().Replace("$", "");
}
if (parameter != null)
{
string formatString = parameter.ToString();
if (!string.IsNullOrEmpty(formatString))
{
string strValue = value.ToString();
if (strValue != null && !string.IsNullOrWhiteSpace(strValue))
{
switch (formatString)
{
case "%": // Add %
return String.Format("{0}%", value);
case "mi": // Add miles
return String.Format("{0} miles", value);
case "caps": // Convert to Initial caps
return UpperCaseFirstCharacter(value.ToString());
case "LiGa": // Convert from Liters to Gallons
{
//
// Convert kilometers to miles.
// 1 liter = 0.264172052 US gallons
//
double lConversion = 0.264172052;
double liters = System.Convert.ToDouble(strValue);
double gallons = lConversion * liters;
return String.Format("{0:N1} gallons", gallons);
}
case "TimeMath": // Compute time remaining until fully charged
{
int Minutes = System.Convert.ToInt16(value);
DateTime currentTime = DateTime.Now;
TimeSpan twoAndAHalfHours = new TimeSpan(0, Minutes, 0);
DateTime estimatedFullChargedTime = DateTime.Now.AddMinutes(Minutes);
string formattedTime = estimatedFullChargedTime.ToString("HH:mm");
return formattedTime;
}
case "{0:d}": // Short date.
case "{0:D}": // Long date.
return String.Format(culture, formatString, System.Convert.ChangeType(value, typeof(DateTime), null));
}
}
else
{
return String.Format(culture, formatString, value);
}
}
}
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public string UpperCaseFirstCharacter(string text)
{
//return Regex.Replace(text, "^[a-z]", m => m.Value.ToUpper());
return text.First().ToString().ToUpper() + text.Remove(0, 1).ToLower();
}
#endregion
};
}
I am using Visual Studio 2017 and targeting the Windows 10 Fall Creators Update SDK (16299).
I just can't spot why it's no longer working as these type converters have been working fine in two other UWP projects.
NOTE: I am using Binding in my binding for the TextBlocks since x:Bind does not work with design-time data.
Thanks for taking a look, I have been staring at this code for a while and can't find the problem.
Rick
Using a TextBox in WPF I have the problem that if I use ',' instead of '.' every time I try to get the value, the text inside the TextBox is transformed with the same number without comma..
How can I disable this automatic transformation?
<TextBox
x:Name="XValue"
Text="{Binding XInitValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="80" VerticalAlignment="Center"
TextChanged="XValue_TextChanged"
</TextBox>
private void XValue_TextChanged(object sender, TextChangedEventArgs e)
{
double a = XInitValue;
}
I solved it using an converter
public class DecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ".")
return value.ToString().Replace(",", ".");
else
return value.ToString().Replace(".", ",");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ".")
return value.ToString().Replace(".", ",");
else
return value.ToString().Replace(",", ".");
}
}
It really works! Thanks!
I have change it a bit to be more general, not use the CurrentCulture in the converter itself and return error if the entered value is ended with decimal separator. Without last part I could not enter the decimal separator at all if the UpdateSourceTrigger=PropertyChanged.
public class DecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
if (value is string stringValue && targetType == typeof(decimal))
{
var decSeparator = culture.NumberFormat.NumberDecimalSeparator;
var normString = decSeparator == "."
? stringValue.Replace(",", ".")
: stringValue.Replace(".", ",");
if (!normString.EndsWith(decSeparator) && decimal.TryParse(normString, out var decResult))
{
return decResult;
}
}
return DependencyProperty.UnsetValue;
}
}
Certainly the CurrentCulture should be set in App.xml.cs or somewhere else on starting the application:
var culture = CultureInfo.GetCultureInfo("de-DE");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(culture.IetfLanguageTag)));
I have a class with a List<string> Tags that I want to display in a TextBox. For this I use a IValueConverter.
ListToStringConverter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var list = value as List<string>;
var returnvalue = string.Empty;
if(list != null)
list.ForEach(item => returnvalue += item + ", ");
return returnvalue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var strValue = value as string;
if(strValue != null)
return strValue.Split(new char[] { ',' });
return null;
}
Class Test:
public class Test : IDataErrorInfo
{
public Test()
{
Tags = new List<string>();
}
public List<string> Tags { get; set; }
string errors;
const string errorsText = "Error in Test class.";
public string Error
{
get { return errors; }
}
public string this[string propertyName]
{
get
{
errors = null;
switch (propertyName)
{
case "Tags":
if (Tags.Count <= 1)
{
errors = errorsText;
return "...more tags plz..";
}
break;
}
return null;
}
}
}
Now I want to Validate the Tags:
<TextBox Text="{Binding Test.Tags, Converter={StaticResource ListToStringConverter}, Mode=TwoWay, ValidatesOnDataErrors=True}" />
But there is no error displayed. Maybe because of the conversion but how can i still accomplish a validation?
I don't see why it shouldn't work. If you will run this at first time - SL will highlight the TextBox with red border. But if you will try to change the list of tags - you will see nothing, because you in your converter you have a bug in ConvertBack you return type array of strings (string[]), but property expects object with type List, so you just need to update the ConvertBack method to:
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var strValue = value as string;
if (strValue != null)
return strValue.Split(new char[] { ',' }).ToList();
return null;
}
One more note about Convert method, you can use Join method:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var list = value as List<string>;
if (list != null)
return String.Join(", ", list);
return null;
}
And one more thing about combining the strings. Don't use operator +, because you can have performance issues with it, use StringBuilder instead.
I believe this code may require more effort to comprehend than average, so I'm using contextual code-comments to explain my predicament in an effort to make it a little easier to understand my question.
public class MyControl : System.Windows.FrameworkElement
{
public double Property1 { get; set; }
public double Property2 { get; set; }
}
...
<StackPanel>
<local:MyControl Name="ControlA" />
<local:MyControl Name="ControlB" />
<!--We want to use data-binding to force ControlA.MinWidth to be equal to
ControlA.Property1 * ControlB.Property2 at all times-->
</StackPanel>
...
static readonly MyPrimaryConverter PrimConv = new MyPrimaryConverter();
static readonly MySecondaryConverter SecConv = new MySecondaryConverter();
public Window1()
{
InitializeComponent();
System.Windows.Data.BindingExpressionBase BindExp1, BindExp2;
System.Windows.Data.Binding MyBinding;
MyBinding = new System.Windows.Data.Binding("Property1");
MyBinding.Source = ControlA;
MyBinding.Mode = System.Windows.Data.BindingMode.OneWay;
MyBinding.Converter = PrimConv;
MyBinding.ConverterParameter = ControlB;
BindExp1 = ControlA.SetBinding(
System.Windows.UIElement.MinWidthProperty, MyBinding);
//Binds correctly and shows "Status" as active
MyBinding = new System.Windows.Data.Binding("Property2");
MyBinding.Source = ControlB; // ControlB.Property2 is the source value now
MyBinding.Mode = System.Windows.Data.BindingMode.OneWay;
MyBinding.Converter = SecConv; //Uses the secondary converter
MyBinding.ConverterParameter = ControlA; //ControlA is the parameter now
BindExp2 = ControlA.SetBinding(
System.Windows.UIElement.MinWidthProperty, MyBinding);
/* Also binds correctly and shows "Status" as active,
but causes BindExp1 to show "Status" as "Detached"
****THIS IS THE PROBLEM I NEED HELP WITH****
How can I successfully bind ControlA.MinWidth to BOTH of the values
that are used to calculate MinWidth?
This is required so that MinWidth will be recalculated if EITHER
value changes at any time
Bonus points if you can show how to do it with
only one converter class instead of two */
}
...
[System.Windows.Data.ValueConversion(typeof(double), typeof(double))]
public class MyPrimaryConverter : System.Windows.Data.IValueConverter
{
public object Convert( object value, System.Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if ((value is double) && (parameter is MyControl))
return ((double)value) * (parameter as MyControl).Property2;
/* This converter ALWAYS returns:
ControlA.Property1 * ControlB.Property2
value == ControlA.Property1 and parameter == ControlB,
so we return value * parameter.Property2 */
return System.Windows.DependencyProperty.UnsetValue;
}
public object ConvertBack( object value, System.Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{ return System.Windows.DependencyProperty.UnsetValue; }
}
[System.Windows.Data.ValueConversion(typeof(double), typeof(double))]
public class MySecondaryConverter : System.Windows.Data.IValueConverter
{
public object Convert( object value, System.Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if ((value is double) && (parameter is MyControl))
return ((double)value) * (parameter as MyControl).Property1;
/* This converter also ALWAYS returns:
ControlA.Property1 * ControlB.Property2
"value" and "parameter" are swapped:
value == ControlB.Property2 and parameter == ControlA
so we return value * parameter.Property1
instead of value * parameter.Property2 */
return System.Windows.DependencyProperty.UnsetValue;
}
public object ConvertBack( object value, System.Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{ return System.Windows.DependencyProperty.UnsetValue; }
}
I think you should use a MultiValueConverter