I have just upgraded our wpf application from 3.5sp1 to 4.0.
The code below we use to bind the textbox to the underlying view model. The textbox is editable.
I wasn't satisfied with the LostFocus solution, so I decided to code a method that manually moves the caret correctly. I've put it in the code behind file and by adding it to the TextChanged event on the TextBox it get it to run every time the text changes.
void moveCaret(object sender, TextChangedEventArgs args)
{
TextBox tb = (TextBox) sender;
if (args.Changes.Any())
{
var first = args.Changes.First();
int offset = 1;
if(first.AddedLength > 0)
{
if (tb.Text.Length > 4 && tb.Text.Length % 4 == 1)
offset = 2;
tb.CaretIndex = first.Offset + offset;
}
else
{
if (tb.CaretIndex > 0)
{
offset = 0;
if (tb.Text.Length > 2 && (tb.Text.Length + 2) % 4 == 1)
offset = -1;
tb.CaretIndex = first.Offset + offset;
}
}
}
args.Handled = true;
}
Just add this to the TextChanged event like so:
MyTextBox.TextChanged += moveCaret;
I'm not 100% sure, but this seems to behave well, though it doesn't handle deleting of the thousand separator.
EDIT: I figured out how to handle the thousand separator. I made another method in the code behind file, and put it on the PreviewKeyDown event on the TextBox. This method checks if the TextBox is receiving a Backspace of Delete button input, and just ignores it and moves the caret in stead.
private void handleThousandSeparator(object sender, KeyEventArgs e)
{
var textBox = sender as TextBox;
if (e.Key == Key.Back)
{
if (textBox.CaretIndex > 0)
{
if (textBox.Text[textBox.CaretIndex - 1] +"" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
{
if (textBox.Text[0] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
return;
textBox.CaretIndex = textBox.CaretIndex - 1;
e.Handled = true;
}
}
}
if (e.Key == Key.Delete)
{
if (textBox.CaretIndex < textBox.Text.Length)
{
if (textBox.Text[textBox.CaretIndex] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
{
if (textBox.Text[0] + "" == System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator)
return;
textBox.CaretIndex = textBox.CaretIndex + 1;
e.Handled = true;
}
}
}
}
Notice the special case for a thousand separator at the first char in the TextBox, where it is deleted in stead of skipped. A thousand separator should ideally never get to be there, but the n0 number formatter doesn't handle the case where you delete the first numbers before the first thousand separator.