Is there a better way to handle double values across languages? - c#

Right now, to accomodate the use of commas as decimal placeholders in languages like Danish, I retrieve values stored with decimal points, e.g. ".123" from a .resx file like this:
// Getting a value from a .resx parameter
double minValue = Convert.ToDouble(AppParams.minVal, CultureInfo.InvariantCulture);
And when I need to work with a value received from a TextBox, e.g. ",321" I use this:
// Getting a value from a TextBox
double newValue = Convert.ToDouble(value, CultureInfo.CurrentCulture);
In the .csproj file, I've added <SupportedCultures>da;</SupportedCultures> but otherwise haven't attempted some application-wide way of dealing with these two issues, other than what's shown.

You don't need to store the value as a string in resx file:
<data name="minVal" type="System.Double, mscorlib">
<value>.123</value>
</data>
This way the generated minVal property will be of type double, and you won't have to convert it manually.
The only issue with this approach is that you have to edit the resx file manually in XML, since the resource designer can't handle resources of this type (actually you can rename or remove a resource or change its value, but you can't change its type and you can't create a new one). Anyway, I've switched to manual editing of resx file since I started using Resharper, because it provides some nice analysis and refactoring features for these files ;)
As a side note, I don't think this minValue constant is a good candidate for resources. If it's a setting that can be changed, put it in settings, not in resources. If it's really a constant, make it a const in C# code. The only good reason to put it in resources is if you want the value to be localizable, and it doesn't seem likely in this case.

When you parse the string from the user input try to accept many possible inputs e.g.
public static class Helper
{
public static bool TryParseDouble(this TextBox textbox, out double value)
{
if (double.TryParse(textbox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out value))
{
textbox.Foreground = Brushes.Black; //indicates that the user typed correct number
return true;
}
else
{
textbox.Foreground = Brushes.Red; // not a number
return false;
}
}
}
When you parse .resx, XML and other files use also InvariantCulture. Here is a problem that I've encountered with XML parser.
When showing data to the user use the current culture.

I much appreciate the answers from Thomas Levesque and lukas. They contained some useful insight and examples. I'm posting this as an answer because I want to provide more info and an example solution. As with many computing/UI problems, compromises often have to be made. I made the unfortunate discovery that none of the InputScopeNameValues (MSDN InputScopeNameValue Enumeration) switch between decimal (.) and comma (,) when changing region+language settings (and, yes, I double-checked that the keyboard was set on my phone to only use Deutsch).
However, because these TextBox inputs are numeric and need to entered quickly, the numeric InputScopes are still the best way to go. Interestingly, even if the user is forced to use the decimal point in place of a comma, as soon as it's entered in the TextBox the string format changes, e.g. from ".123" to ",123" even though as shown"{0:#.000}". Thus the compromise and in the code below, the workaround (tested so far in en-US and de-DE).
Note: As lukas mentioned, it is always wise to validate user input. I'm not using TryParse here (although I may) so I don't have to rewrite a lot of code. This is mitigated in the UI by choosing a numeric InputScope and in the handling code via try/catch blocks, which even handle correctly a user attempting to bypass the numeric input by pasting text from the clipboard:
<TextBox x:Name="myTBox" InputScope="Number" Text="{Binding SomeNumber, Mode=TwoWay}" />
And code:
public string SomeNumber
{
get
{
return String.Format("{0:#.000}", SomeProfileModel.Instance.SomeProfile.SomeNumber);
}
set
{
if (SomeProfileModel.Instance.SomeProfile.SomeNumber.ToString() == value) return;
var oldValue = SomeProfileModel.Instance.SomeProfile.SomeNumber;
try
{
double newValue;
try
{
newValue = Convert.ToDouble(value, CultureInfo.CurrentCulture);
}
catch (Exception)
{
newValue = Convert.ToDouble(value, CultureInfo.InvariantCulture);
}
if (Convert.ToDouble(MyAppParams.SomeNumberMin, CultureInfo.InvariantCulture) > newValue || Convert.ToDouble(MyAppParams.SomeNumberMax, CultureInfo.InvariantCulture) < newValue)
{
// Revert back to previous value
// NOTE: This has to be done here. If done in the catch statement,
// it will never run since the MessageBox interferes with it.
throw new Exception();
}
SomeProfileModel.Instance.SomeProfile.SomeNumber = newValue;
RaisePropertyChanged("SomeNumber", oldValue, newValue, true);
}
catch (Exception err)
{
System.Windows.MessageBox.Show("Value must be a number between " + MyAppParams.SomeNumberMin + " and " + MyAppParams.SomeNumberMax);
}
}
}

Related

C# code returns out of method when value is too large

I got a small method that splits a string into two parts, retrieves one part and uses it to establish a connection to a server:
private void RetrieveData(string vesselCode)
{
string[] splitSelectedVessel = vesselCode.Split('_');
int selectedVessel = int.Parse(splitSelectedVessel[1]);
// URL of server to retrieve data.
// constructed with vesselCode.
string baseUrl = ".../vehicle/";
string jsonUrl = baseUrl + selectedVessel;
PtVehicle currentVessel = DownloadAndDeserialize<PtVehicle>(jsonUrl);
}
VesselCode containts two parts, combined with an '_' in between:
company code
vesselnumber
Both are of entry fields from a Xamarin project and are originally stored as strings.
Whenever the VesselNumber part gets too large, it just stops right after int selectedVessel = int.Parse(splitSelectedVessel[1]);. It seems to break out of the method above, without giving a warning or anything. I've tested it with breakpoints on every line. It simply stops running that method and goes on with whatever it was doing, often letting the app crash later on (since it needs that value). What is happening here?
I suppose that your number is all too long for an int. I don't know what can the maximum number be, but you can try long instead. Probably it's enough.
If it isn't then you have to do something with handling of too large numbers. You can write
if (long.TryParse(myString, out myValue)
{
// Your logic here
}
else
{
// Do something if the value is too large
}
I ended up fixing the issue with #Ilya Chernomordik's and the documentation provided by #Mihai.
The issue was that int selectedVessel = int.Parse(splitSelectedVessel[1]); parses the value of that string to an int. When the value was larger than the int max size, it crashed. Since VesselNumber is an ID and I don't have to calculate it, I simply solved it by storing it as a string:
string selectedVessel = splitSelectedVessel[1];
Another way is to store it as a long (int) of 64 bits, but this only "moves" the issue to a much larger number.
What is happening here is that you are throwing an exception. Consider the following code...
try
{
string s1 = "2147483649";
int j = int.Parse(s1);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
2147483649 is int.MaxValue + 1
This code generates the following message:
Value was either too large or too small for an Int32.
It should throw an System.OverflowException if the number is too big to be parsed as int.
According to Microsoft documentation long/ ulong has the biggest range.
You might have breaking for exceptions disabled if you are using Visual Studio so you might want to enable them to see what's the actual problem Debug->Windows->Exception Settings and make sure Common Language Runtime Exceptions are checked.
On the other hand, the best practice is to use the .TryParse approach when converting to an integral type.

Textbox Number Formating & start with zero & accept only Numbers

I am using below Property example to make some calculation on textbox and if textbox is null I am assigning zero to it so calculation won't fail as you can see I am using Math.Round and I want to make several checks on these textbox input like
textbox that only accepts numbers I searched and found method 1
I want my textbox to be formated I searched and found Method 2
Now my question is ..
Is there any way to mareg all these method in the property method I am using
so my code won't be like "spaghetti code" ?
is there any better ways to do these checks ?
Thank you in advance
Property example
public double ItemPriceResult
{
get
{
return Math.Round(ItemCost * RevenuePercentage / 100 + ItemCost, 0);
}
}
Method 1
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (System.Text.RegularExpressions.Regex.IsMatch(textBox1.Text, "[^0-9]"))
{
MessageBox.Show("Please enter only numbers.");
textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);
}
}
Method 2
textBox1.Text = string.Format(System.Globalization.CultureInfo.GetCultureInfo("id-ID"), "{0:#,##0.00}", double.Parse(textBox1.Text));
UPDATE after some answers
MaskedTextBox seems fit my needs I read and searched and below some question
if you kindly would like to help me
I need to use MaskedTextBox because I can set it to accept
number and I can also force number formating so
also I need to make number textboxs easer to read for users
so 1000 will be come 1,000
and 10000 will be come 10,000
then according to Microsoft Docs formating MaskedTextBox to fit my needs
Masked MaskedTextBox with 999,999,999,
second I do not want the PromptCharto be visible I google it but none of search result did it
Try this , it will accept only numbers and u can format the string as u want using regex.
public static string ToMaskedString(this String value)
{
var pattern = "^(/d{2})(/d{3})(/d*)$";
var regExp = new Regex(pattern);
return regExp.Replace(value, "$1-$2-$3");
}
You have a TextBox. Alas you don't tell what kind of TextBox you use. System.Windows.Forms.TextBox? System.Web.UI.MobileControls.TextBox?
You write "if text box is null I am assigning zero to it". I assume that you mean that if no text is entered in the text box you assume that 0 is entered.
Furthermore you want to format the output of the text box whenever the text is changed. So while the operator is typing text you want to change this text? For the operator this is very annoying.
Wouldn't you prefer that the operator is obliged to type his text in the format you desire, helping him visually. For this you may use the class MaskedTextBox
The MaskedTextBox has a property Mask, which forces the operator to type in a certain format. I'm not really familiar with what you do with the format {0:#,##0.00}, but I assume you want the output double in a real format with two digits after the decimal point using the decimal point and the thousand separator as common in the current culture.
via the designer put in initialize component:
this.maskedTextBox1.Mask = "99990.00";
after adding the event for text changed:
private void maskedtextBox1_TextChanged(object sender, EventArgs e)
{
double enteredValue = 0.0; // value to use when empty text box
if (!String.IsNullOrEmpty(this.maskedtextBox1.Text))
{
enteredValue = double.Parse(maskedTextBox1.Text, myFormatProvider)
}
ProcessEnteredValue(enteredValue);
}
}
After your edit, the specifications have changed.
While entering the number in the text box, the operator should not have any visual feedback of the formatting of his number.
The operator is free to enter the real number in any commonly used format.
The value of the text box should not be used while the operator is editing the text box.
Only after editing is finished, the value of the text box should be interpreted for correctness, and if correct it should be used.
The actually used value should be displayed in the text box in a defined format.
The desire not to show any visual feedback while entering is understandable. After all, the program doesn't care whether the operator types 1000, 1000.00, or even 1.0E3.
The MaskedTextBox is especially used to force the operator to enter his number in a given format. Since this is not desired, my advise would be to use a TextBox instead of aMaskedTextBox.
However, because you give the operator the freedom to enter his number in any way he wants, including copy-paste, repairing typing errors, etc. you'll have to add the possibility for the user to express to the program that he has finished entering the number.
An often used method in the windows UI would be a Button. Another possibility would be the enter button. Be aware though that this is not really standard within windows. It might make learning your program a little bit more difficult.
So after the operator notified that he finished editing and the corresponding event function is called, your code could be:
// Get the numberformat to use, use current culture, or your own format
private readonly IFormatProvider myNumberFormat = CultureInfo.CurrentCulture.NumberFormat
private void OperatorFinishedEditing(TextBox box)
{
// read the text and try to parse it to a double
// accepting all common formats of real numbers in the current culture
bool valueOk = true;
double resultValue = 0;
if (!String.IsNullOrEmpty(box.Text))
{
bool valueOk = Double.TryParse(box.Text, out resultValue);
}
if (valueOk)
{
box.Text = FormatResultValue(resultValue);
ProcessValue(resultValue);
}
else
{
ShowInputProblem();
}
}

Formatting a string to display custom decimal values

I'm trying to format some doubles properly so that they will display properly for my uses (I'm building a statement in Devexpress, so I'm working with a lot of numbers).
Here are the basic formatting rules I'd like to have happen:
1000.2 -> 1,000.20
1000 -> 1,000
1000.22 -> 1,000.22
Is this possible using string formatting in C#? I've tried the following, but not been able to achieve my goal:
#,#.## - gives me 1,000.2 for the first value
#,#.#0 - gives me 1,000.00 for the second value
#,#.00 - gives me 1,000.00 for the second value
EDIT: Some more information. DevExpress gives me the ability to use string formatting to set up the values after they've been bound to the report. We're doing it at report time (and not at calculation time in the behind the scenes code) because we use the Sum function within the tables that DevExpress offers us. The reason we do THAT is so that we can minimize calls to our database by doing one large pull of data, then using that table over and over again in the statement and filtering based on the restrictions within.
EDIT EDIT:
Based on the feedback I've receieved here in the comments, it's not possible to perform the formatting I'd like to do with only providing a string format; I would need to insert some code either when I provide the data to the report (and then remove any and all formatting from the report) and perform all summing functions at the code level (to ensure that the sum values have the expected decimal places), or I would need to accept .00 at the end of, for example, some amount of yen (100 JPY would never be represented as 100.00 JPY, as an example).
This is a bit of an esoteric case, but it's good to know!
You can use string formatting coupled to a simple if condition. To shorten it's use, you can also make it an Extension method. It can look like this :
public static string FormatConditionnaly(this double input)
{
return input % 1 == 0 ? input.ToString("#,0") : input.ToString("#,0.00");
}
Basically, if you number does not contain any decimals (the % 1 == 0 check), you format it without decimals. If it fails the check, you add the two zeroes.
It is used like that :
const double flatNumber = 1000;
string result1 = flatNumber.FormatConditionnaly(); //1,000
const double numberWithDecimals = 1000.5;
string result2 = numberWithDecimals.FormatConditionnaly(); //1,000,50
Bit of a hack but you can give this a try:
s = String.Format("{0:N2}", 1000).Replace(".00", "");
Use the "N" format specifier as the format string when you call ToString(); See here
For example:
int intValue = 123456789;
Console.WriteLine(intValue.ToString("N2",
CultureInfo.InvariantCulture).Replace(".00", "");
You can customize group sizes etc. as needed.
Why don't you format the values before binding to DevExpress control using plain old C# (Assuming you are doing a bind, as you have not given sufficient details.)
In c# the Math.Round() should do the trick.
Example Math.Round(doubleValue,2) where the second parameter is the number of decimal places.
EDIT:
#,##0.00
I Do not have DevExpress controls to test my solution but I did find http://documentation.devexpress.com/#windowsforms/CustomDocument1498 online (not sure if you see it already).
It seems you can use the Number or Currency masks.
Also take a look at the Zero Placeholder under the custom section. based on the description, '0' is filled where the user has not supplied a value.
example: 123.4 --> 123.40
If the string that you're trying to format is in an XRTableCell of an XtraReport instance, you can handle the BeforePrint event on that cell to format its text. This event is triggered anytime that the report is rendered. Call GetCurrentColumnValue to retrieve the value that you want to format, use any of the code methods from the previous answers that will work for you, and then set that cell's text with your formatted string. Using #dweeberly's answer:
private void OnBeforePrint(object sender, PrintEventArgs e)
{
object value = this.GetCurrentColumnValue("YourField");
if (value != null)
{
yourCell.Text = String.Format("{0:N2}", value.ToString()).Replace(".00", "");
}
}
Based on the feedback I've receieved here in the comments, it's not possible to perform the formatting I'd like to do with only providing a string format; I would need to insert some code either when I provide the data to the report (and then remove any and all formatting from the report) and perform all summing functions at the code level (to ensure that the sum values have the expected decimal places), or I would need to accept .00 at the end of, for example, some amount of yen (100 JPY would never be represented as 100.00 JPY, as an example).
This is a bit of an esoteric case, but it's good to know!

How to prohibit comma as decimal separator

I'm writing code with german culture settings
Nevertheless I would like to force the user to use the point as a decimal separator.
My piece of test code outputs the wrong value.
How do I detect the "wrong" comma ?(throw an exception)
string nok_str = "14,9";
string ok_str = "14.9";
double nok_Var1 = double.Parse(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
double nok_Var2 =Convert.ToDouble(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
First off, and please forgive me, Iā€™d like to question your design decision:
How is this enhancing the user experience? The application should rather try to accept all unambiguous user input, not reject theoretically sound input.
That said, a number such as ā€œ19,2ā€ will be interpreted, with an invariant culture, as having a thousands separator (which is simply discarded). This is why your code silently produces bad values. If you want to explicitly forbid this input, the easiest way to achieve this is an explicit test:
if (nok_str.Contains(","))
throw new FormatException(ā€¦);
As an alternative, you can try modifying the NumberFormatInfo.NumberGroupSeparator property of a custom NumberFormatInfo object that you pass to the Parse method.
Basically the default is to include AllowThousands in the number style. If you specify the number style you want, you can prohibit this:
using System;
using System.Globalization;
class Test
{
static void Main(string[] args)
{
string text = "19,2";
double value;
bool valid = double.TryParse(text, NumberStyles.Float,
CultureInfo.InvariantCulture,
out value);
Console.WriteLine(valid); // Prints false
}
}
Note that NumberStyles.Float is a composite style for AllowLeadingWhite, AllowTrailingWhite, AllowLeadingSign, AllowDecimalPoint, and AllowExponent - but not AllowThousands.
I am not sure what is the source of your input.
If it comes from user it also depends... If it is GUI application, you may think of restricting the input to certain possible keys, excluding comma. If it is a console app, you can try regular expressions to pre-validate input strings.
If it comes from various sources (i.e. web service) maybe simply brute-force string replace will do the trick?
Last, but not least: there are reasons for parsing to be culture-sensitive and if I were you, I would encourage users to enter valid regional number format instead forcing them to provide incorrect one.

C# Why won't this substring work? Error: Input string was not in a correct format

The problem is with the convert of the txt box value, but why?
string strChar = strTest.Substring(0, Convert.ToInt16(txtBoxValue.Text));
Error is: Input string was not in a correct format.
Thanks all.
txtBoxValue.Text probably does not contain a valid int16.
A good way to avoid that error is to use .tryParse (.net 2.0 and up)
int subLength;
if(!int.TryParse(txtBoxValue.Text,out subLength)
subLength= 0;
string strChar = strTest.Substring(0, subLength);
This way, if txtBoxValue.Textdoes not contain a valid number then subLength will be set to 0;
One thing you may want to try is using TryParse
Int16 myInt16;
if(Int16.TryParse(myString, out myInt16)
{
string strChar = strTest.Substring(0, myInt16);
}
else
{
MessageBox.Show("Hey this isn't an Int16!");
}
A couple reasons the code could be faulty.
To really nail it down, put your short conversion on a new line, like this:
short val = Convert.ToInt16(txtBoxValue.Text);
string strChar = strTest.Substring(0, val);
Likely the value in txtBoxValue.Text is not a short (it might be too big, or have alpha characters in it). If it is valid and val gets assigned, then strTest might not have enough characters in it for substring to work, although this normally returns a different error. Also, the second parameter of substring might require an int (not sure, can't test right now) so you may need to actually convert to int32 instead of 16.
What is the value of txtBoxValue.Text during your tests?
ASP.NET offers several validation controls for checking user input. You should use something like a CompareValidator or RegularExpressionValiditor in your WebForm if you're expecting a specific type of input, eg, an Integer.

Categories

Resources