WPF TextBox绑定与格式

时间:2011-01-04 06:27:55

标签: wpf binding string-formatting

我刚刚将我们的wpf应用程序从3.5sp1升级到4.0。

下面的代码用于将文本框绑定到底层视图模型。文本框是可编辑的。

    <TextBox HorizontalContentAlignment="Right"
Text="{Binding Path=Price,   StringFormat={0:#,##0;(#,##0)},  Mode=TwoWay,  ValidatesOnDataErrors=True,  UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>

在3.5sp1中,格式化最初只会发生。因此,当文本框被加载并绑定到值4000时,格式化将其更改为4,000。如果用户编辑了此值,则不会进行格式化。

在4.0中,格式化随着值的变化而发生(即当用户输入新值时)。虽然从理论上说这听起来不错,实际上它是一场灾难。光标到处都是。它无法使用。

现在,我们可以将UpdateSourceTrigger更改为“LostFocus”,但这会引入在某些情况下未验证数据的新问题。

有没有办法让旧的3.5sp1行为恢复?

更新1

使用Converter仍会产生相同的行为:

public class DecimalConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null)
            return ((decimal)value).ToString("#,##0;(#,##0)");

        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
}

和修改过的XAML:

<TextBox Text="{Binding Path=Price, Converter={StaticResource DecimalConverter}, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>

更新2

与此connect article类似。

3 个答案:

答案 0 :(得分:1)

作为更新,我采用了Jonathans的建议,并重新调整了Binding以使用LostFocus而不是PropertyChanged(在适当的地方 - 即指定了StringFormat的地方)。

正如Jonathan所说,在某些情况下,你必须手动触发绑定刷新/验证。

如果有人有更好的方法,我很乐意看到它。

答案 1 :(得分:0)

我对LostFocus解决方案不满意,所以我决定编写一个手动移动插入符号的方法。我把它放在代码隐藏文件中,然后将它添加到TextBox上的TextChanged事件中,每次文本更改时都会运行它。

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;
}

只需将其添加到TextChanged事件中,如下所示:

MyTextBox.TextChanged += moveCaret;

我不是百分百肯定,但这似乎表现得很好,但它没有处理删除千位分隔符。

编辑:我想出了如何处理千位分隔符。我在代码隐藏文件中创建了另一个方法,并将它放在TextBox上的PreviewKeyDown事件中。此方法检查TextBox是否正在接收Backspace of Delete按钮输入,只是忽略它并移动插入符号。

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;
            }
        }
    }
}     

请注意TextBox中第一个字符处的千位分隔符的特殊情况,它将被删除而不是跳过。理想情况下,千位分隔符应该永远不会存在,但是n0数字格式化程序不处理在第一千个分隔符之前删除第一个数字的情况。

答案 2 :(得分:-1)

您可以尝试删除StringFormat={0:#,##0;(#,##0)}并编写转换器以进行格式化。