Xamarin Mask Entry Currency 2 Decimal Places - c#

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:

Related

UWP Type Converter for Binding TextBlock values not processed on my XAML at design-time

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

Doubles decimal separator WPF

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)));

Multiple culture with textbox

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;
}
}

Error validation with IDataErrorInfo and IValueConverter

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.

How to change combobox particular item color dynamically in wpf

<Grid x:Name="LayoutRoot">
<ComboBox x:Name="com_ColorItems" Height="41" Margin="198,114,264,0" VerticalAlignment="Top" FontSize="13.333" FontWeight="Bold" Foreground="#FF3F7E24"/>
</Grid>
With above code I colored all items in the combobox green.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 5; i++)
{
com_ColorItems.Items.Add(i);
}
}
With above code I have filled five items into combobox. Now I like to change the color of the 3rd item (3) to "red" in code behind dynamically. How can I do that?
Instead of adding the actual value of i in the combobox, add a ComboBoxItem instead:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 5; i++)
{
ComboBoxItem item = new ComboBoxItem();
if (i == 2) item.Foreground = Brushes.Blue;
else item.Foreground = Brushes.Pink;
item.Content = i.ToString();
com_ColorItems.Items.Add(item);
}
}
If you want to modify the ComboBoxItem created with this method later, this is how you can do it:
var item = com_ColorItems.Items[2] as ComboBoxItem; // Convert from Object
if (item != null) // Conversion succeeded
{
item.Foreground = Brushes.Tomato;
}
First, try to bind your Source and avoid the directly access through code behind.
And than you can use an Converter in your ItemSource Binding.
e.g.
ItemSource={Binding MyComboboxItems, Converter={StaticResource MyConverter}}
and in your Converter find you the 3rd Item and give them a different ForegroundColor
In addition to Mario Binders answer, here an example of such a converter:
public class ListToColoredComboboxItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is IEnumerable<Measurement> measurements)
{
var comboBoxItems = new List<ComboBoxItem>(measurements.Count());
foreach (var measurement in measurements)
{
var item = new ComboBoxItem();
item.Content = measurement;
if (!string.IsNullOrWhiteSpace(measurement.ErrorMessage))
item.Foreground = Brushes.Red;
comboBoxItems.Add(item);
}
return comboBoxItems;
}
return null;
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And probably you also want to convert the selected Item back to a value:
public class ComboBoxItemToItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
if (value is ComboBoxItem comboBoxItem)
{
return comboBoxItem.Content;
}
return null;
}
}

Categories