Just a quick-and-dirty solution for solving the tedious problem of the numpad’s decimal-point insertion where the culture require something different than a dot “.”.
The Problem
Many of you, who aren’t using a dot as decimal separator, maybe noticed that the character issued by the numeric-pad is always a doe, regardless the OS settings. In Italy, for instance, we’re using the comma as decimal separator.
You know, if you type the wrong character, the number won’t be recognized as valid (sometimes even worse, because it’s mistaken as valid).
If you try to open Notepad or any raw-input application, you’ll notice that there’s no way to “hack” the Windows settings in order to correctly input the numeric-pad “point” as a comma. By the way, if you enter a number in Microsoft Excel or so, the character is actually a comma.
Looks like the translation is something managed by the application.
It’s not so simple, though.
Imagine to write your own application (WPF in my case), and have a series of textboxes. Whereas a textbox used for entering a number (e.g. most physical units) would be fine having a “translation” to a comma, when another textbox used for an IP-pattern, clearly should *NOT* be translated any time.
Looks like that some countries use a different punctuation for generic numbers and for currency: my “neighbor” friends of Switzerland do use the comma for any number but currency, where the dot is preferred.
The Solution
Here is a solution, but I believe is difficult to satisfy all the developers’ habits. I just opted for a simple attached-property, as “behavior” to any TextBoxBase object, which “intercepts” the Decimal key (the numpad’s DP) and replaces it with the proper one.
namespace DecimalPointCorrectorDemo
{
public enum DecimalPointCorrectionMode
{
Inherits,
Number,
Currency,
Percent,
}
public static class TextBoxHelper
{
#region DPA DecimalPointCorrection
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);
}
#endregion
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) == false)
{
var tc = new TextComposition(
InputManager.Current,
tbox,
replace
);
TextCompositionManager.StartComposition(tc);
}
await Task.FromResult(false);
}
}
}
The code is rather simple, so I think would be useless chatting more.
The only worthwhile point is regarding the “async” pattern in the key-replace function. I just wanted to leave the originating event (PreviewKeyDown) a bit of time to finish before adding another (possible) event. Honestly, I don’t know whether that’s really necessary: the async-await pattern comes easy and reliable, so I prefer to keep the code safer. Feel free to improve the it.
The complete demo solution source code can be downloaded here.
This code has been tested widely enough, including on the Windows 8 on-screen touch-keyboard.
Enjoy!