在全球化的今天,软件开发者经常需要面对不同文化背景下的用户输入习惯。例如,在意大利,小数点通常使用逗号表示,而不是点。这在开发应用程序时可能会带来一些挑战,尤其是当涉及到数字输入时。本文将探讨这个问题,并提供一个解决方案。
许多不使用点作为小数分隔符的用户可能已经注意到,无论操作系统设置如何,数字键盘上发出的字符总是点。如果输入了错误的字符,数字可能不会被识别为有效值(有时甚至更糟,因为它被误认为是有效的)。如果尝试打开记事本或任何原始输入应用程序,会发现没有办法“破解”Windows设置,以便将数字键盘上的“点”正确输入为逗号。顺便说一句,如果在Microsoft Excel中输入数字,字符实际上是逗号。看起来翻译是由应用程序管理的。
然而,这并不简单。想象一下编写自己的应用程序(以WPF为例),并有一系列文本框。虽然用于输入数字的文本框(例如大多数物理单位)使用逗号进行“翻译”是没有问题的,但用于IP模式的另一个文本框显然不应该在任何时候被翻译。
看起来一些国家对一般数字和货币使用不同的标点符号:“邻居”瑞士朋友对任何数字使用逗号,但在货币方面更喜欢点。
这里有一个解决方案,但相信很难满足所有开发者的习惯。选择了一个简单的附加属性,作为任何TextBoxBase对象的“行为”,它“拦截”小数键(numpad的DP)并用适当的字符替换它。
以下是C#代码实现:
namespace DecimalPointCorrectorDemo
{
public enum DecimalPointCorrectionMode
{
Inherits,
Number,
Currency,
Percent,
}
public static class TextBoxHelper
{
public static readonly DependencyProperty DecimalPointCorrectionProperty = DependencyProperty.RegisterAttached(
"DecimalPointCorrection",
typeof(DecimalPointCorrectionMode),
typeof(TextBoxHelper),
new UIPropertyMetadata(default(DecimalPointCorrectionMode), DecimalPointCorrectionChanged)
);
public static DecimalPointCorrectionMode GetDecimalPointCorrection(TextBoxBase obj)
{
return (DecimalPointCorrectionMode)obj.GetValue(DecimalPointCorrectionProperty);
}
public static void SetDecimalPointCorrection(TextBoxBase obj, DecimalPointCorrectionMode value)
{
obj.SetValue(DecimalPointCorrectionProperty, value);
}
private static void DecimalPointCorrectionChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var tbox = (TextBoxBase)sender;
switch ((DecimalPointCorrectionMode)args.OldValue)
{
case DecimalPointCorrectionMode.Number:
case DecimalPointCorrectionMode.Currency:
case DecimalPointCorrectionMode.Percent:
tbox.PreviewKeyDown -= tbox_PreviewKeyDown;
break;
}
switch ((DecimalPointCorrectionMode)args.NewValue)
{
case DecimalPointCorrectionMode.Number:
case DecimalPointCorrectionMode.Currency:
case DecimalPointCorrectionMode.Percent:
tbox.PreviewKeyDown += tbox_PreviewKeyDown;
break;
}
}
static void tbox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Decimal)
{
e.Handled = true;
var tbox = (TextBoxBase)sender;
var mode = TextBoxHelper.GetDecimalPointCorrection(tbox);
var culture = Thread.CurrentThread.CurrentCulture;
SimulateDecimalPointKeyPress(tbox, mode, culture);
}
}
private static async void SimulateDecimalPointKeyPress(TextBoxBase tbox, DecimalPointCorrectionMode mode, CultureInfo culture)
{
string replace;
switch (mode)
{
case DecimalPointCorrectionMode.Number:
replace = culture.NumberFormat.NumberDecimalSeparator;
break;
case DecimalPointCorrectionMode.Currency:
replace = culture.NumberFormat.CurrencyDecimalSeparator;
break;
case DecimalPointCorrectionMode.Percent:
replace = culture.NumberFormat.PercentDecimalSeparator;
break;
default:
replace = null;
break;
}
if (!string.IsNullOrEmpty(replace))
{
var tc = new TextComposition(InputManager.Current, tbox, replace);
TextCompositionManager.StartComposition(tc);
}
await Task.FromResult(false);
}
}
}
代码相当简单,认为没有必要再详细讨论了。唯一值得一提的是按键替换函数中的“异步”模式。只是想让原始事件(PreviewKeyDown)在添加另一个(可能的)事件之前有一点时间来完成。老实说,不知道这是否真的必要:异步-等待模式既容易又可靠,所以更倾向于保持代码更安全。欢迎改进。
完整的演示解决方案源代码可以。