Связывание WPF TextBox с форматированием - PullRequest
10 голосов
/ 04 января 2011

Я только что обновил наше приложение 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, форматирование изменило бы его на 4000.Если пользователь отредактировал это значение, форматирование не произошло бы.

В 4.0 форматирование происходит при изменении значения (т. Е. Пока пользователь вводит новое значение).Хотя в теории это звучит нормально, в действительности это катастрофа.Курсор повсюду.Его невозможно использовать.

Теперь мы можем изменить UpdateSourceTrigger на «LostFocus», но это создает новые проблемы с данными, которые не проверяются в определенных сценариях.

Есть ли способ получить старый 3.5sp1поведение назад?

Обновление 1

Использование конвертера по-прежнему вызывает то же поведение:

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

Аналогично этому подключить статью .

Ответы [ 3 ]

1 голос
/ 05 января 2011

В качестве обновления я принял предложение Джонатана и перенастроил привязку, чтобы использовать LostFocus вместо PropertyChanged (где это уместно - т. Е. Везде, где также был указан StringFormat).

Как сказал Джонатан, в некоторых случаях вам нужно инициировать привязкуобновить / проверить вручную, используя этот подход.

Если у кого-то есть лучший подход, я хотел бы увидеть его.

0 голосов
/ 13 ноября 2013

Я не был удовлетворен решением LostFocus, поэтому я решил написать метод, который вручную перемещает курсор.Я поместил его в код позади файла и, добавив его к событию TextChanged в TextBox, он запускается каждый раз, когда изменяется текст.

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;

Я не уверен на 100%, но это, кажется, ведет себя хорошо, хотя это не обрабатывает удаление разделителя тысяч.

РЕДАКТИРОВАТЬ Я разобрался, как обращаться с разделителем тысяч.Я сделал другой метод в файле code code и поместил его в событие PreviewKeyDown в TextBox.Этот метод проверяет, получает ли 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 не обрабатывает случай, когда вы удаляете первые числа перед разделителем первой тысячи.

0 голосов
/ 04 января 2011

Вы можете попробовать удалить StringFormat={0:#,##0;(#,##0)} и записать конвертер для форматирования.

...