WPF TextBox Binding with Formatting

天大地大妈咪最大 提交于 2019-11-30 12:36:57

As an update I took Jonathans suggestion and rejigged the Binding to use LostFocus instead of PropertyChanged (where appropriate - ie wherever StringFormat was also specified).

As Jonathan said, in some cases you have to trigger binding refresh / validation manually taking this approach.

If anyone has better approach, I would love to see it.

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.

You can try to remove StringFormat={0:#,##0;(#,##0)} and write converter to do formating.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!