Есть ли лучший способ обработки двойных значений в разных языках?
Прямо сейчас, чтобы приспособить использование запятых в качестве десятичных заполнителей в таких языках, как датский, я извлекаю значения, хранящиеся с десятичными точками, например, ".123" из файла.resx, например так:
// Getting a value from a .resx parameter
double minValue = Convert.ToDouble(AppParams.minVal, CultureInfo.InvariantCulture);
И когда мне нужно работать со значением, полученным из TextBox, например, ",321", я использую это:
// Getting a value from a TextBox
double newValue = Convert.ToDouble(value, CultureInfo.CurrentCulture);
В файле.csproj я добавил <SupportedCultures>da;</SupportedCultures>
но в остальном не пытались каким-то общеинформационным способом решения этих двух проблем, кроме того, что показано.
3 ответа
Я очень ценю ответы Томаса Левеска и Лукаса. Они содержали некоторые полезные идеи и примеры. Я публикую это как ответ, потому что хочу предоставить больше информации и пример решения. Как и в случае многих проблем с вычислительной средой / пользовательским интерфейсом, часто приходится идти на компромиссы. Я сделал неудачное открытие, что ни один из InputScopeNameValues ( MSDN InputScopeNameValue Enumeration) не переключается между десятичной (.) И запятой (,) при изменении настроек региона + языка (и, да, я дважды проверил, что клавиатура на моем телефоне была установлена на используйте только Deutsch).
Однако, поскольку эти входные данные TextBox являются числовыми и должны вводиться быстро, числовые входные области все еще являются лучшим способом. Интересно, что даже если пользователь вынужден использовать десятичную точку вместо запятой, как только она вводится в TextBox, формат строки изменяется, например, с ".123" на ",123", даже если показано "{0".:. # 000}". Таким образом, компромисс и в приведенном ниже коде обходной путь (проверено до сих пор в en-US и de-DE).
Примечание. Как упоминал Лукас, всегда полезно проверять вводимые пользователем данные. Я не использую здесь TryParse (хотя могу), поэтому мне не нужно переписывать много кода. Это смягчается в пользовательском интерфейсе путем выбора числового InputScope и в коде обработки с помощью блоков try/catch, которые даже корректно обрабатывают пользователя, пытающегося обойти числовой ввод, вставляя текст из буфера обмена:
<TextBox x:Name="myTBox" InputScope="Number" Text="{Binding SomeNumber, Mode=TwoWay}" />
И код:
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);
}
}
}
Вам не нужно хранить значение в виде строки в файле resx:
<data name="minVal" type="System.Double, mscorlib">
<value>.123</value>
</data>
Таким образом, генерируется minVal
свойство будет иметь тип double
и вам не придется конвертировать его вручную.
Единственная проблема с этим подходом заключается в том, что вам нужно вручную редактировать файл resx в XML, так как конструктор ресурсов не может обрабатывать ресурсы этого типа (на самом деле вы можете переименовать или удалить ресурс или изменить его значение, но вы не можете измените его тип, и вы не можете создать новый). В любом случае, я переключился на ручное редактирование файла resx с тех пор, как начал использовать Resharper, потому что он предоставляет некоторые хорошие возможности анализа и рефакторинга для этих файлов;)
Как примечание стороны, я не думаю, что это minValue
константа - хороший кандидат на ресурсы. Если это настройка, которую можно изменить, поместите ее в настройках, а не в ресурсах. Если это действительно константа, сделайте это const
в коде C#. Единственная веская причина поместить его в ресурсы, если вы хотите, чтобы значение было локализуемым, и в этом случае это маловероятно.
Когда вы анализируете строку из пользовательского ввода, попробуйте принять много возможных входных данных, например
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;
}
}
}
При разборе.resx, XML и других файлов также используется InvariantCulture. Вот проблема, с которой я столкнулся при разборе XML.
При показе данных пользователю используйте текущую культуру.