Текст в TextBox с UpdateSourceTrigger = PropertyChanged не обновляется, когда приведение к вводу текста приводит к неизменному исходному значению - PullRequest
13 голосов
/ 09 ноября 2010

У меня есть текстовое поле, свойство Text которого имеет TwoWay MultiBinding с UpdateSourceTrigger , установленным в PropertyChanged ,Первая Binding относится к свойству зависимости ( Value ), которое имеет функцию PropertyChangedCallBack , которая округляет значение до одного десятичного знака.

Цель текстового поля - выполнять округление по типу пользователя, а не когда текстовое поле теряет фокус, поэтому UpdateSourceTrigger имеет значение PropertyChanged .

.У меня проблема заключается в том, что если ввести текст, который НЕ приводит к изменению Значение , свойство Текст и Значение становятся не синхронизированы.* Только если операция округления вызывает изменение Значение , Текст обновляется на лету.Например, если Текст и Значение равны 123,4 и пользовательские типы 1 после этого, Значение округляется до того же значения (123,4), но Текст показывает 123,41.Однако, если после 4 набирается 9, Значение округляется до 123,5.И из-за этого фактического изменения Text затем обновляется до того же значения (123.5).

Есть ли способ заставить текстовое поле обновляться из своего источника, даже если источник не 'с момента последнего запуска?Я пытался использовать BindingExpressionBase.UpdateTarget(), но это работает только тогда, когда UpdateSourceTrigger имеет значение Явное , которое нельзя использовать как Значение больше не обновляется доподходящее время, когда можно вызвать UpdateTarget (например, обработчик TextInput ).Я пробовал другие методы, такие как явное обновление значения Text с привязанного Value , принудительное фактическое изменение на Value , чтобы временно вызвать обновление, но они "хаки "или не работают, или вызывают другие проблемы.

Любая помощь будет принята с благодарностью.

Код ниже.

Фрагмент XAML

<TextBox>
  <TextBox.Text>
    <MultiBinding Converter="{local:NumberFormatConverter}"
                  UpdateSourceTrigger="Explicit"
                  Mode="TwoWay">
      <Binding Path="Value"
               RelativeSource="{RelativeSource AncestorType={x:Type Window}}"
               Mode="TwoWay" />
    </MultiBinding>
  </TextBox.Text>
</TextBox>

C # фрагмент

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register(
        "Value", typeof(decimal), typeof(MainWindow),
        new FrameworkPropertyMetadata(0m,
        new PropertyChangedCallback(OnValueChanged)));

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    obj.SetValue(ValueProperty, Math.Round((decimal)args.NewValue, 1));
}

Требуется класс преобразователя

public class NumberFormatConverter : MarkupExtension, IMultiValueConverter
{
    public static NumberFormatConverter Instance { private set; get; }

    static NumberFormatConverter()
    {
        Instance = new NumberFormatConverter();
    }

    public override object ProvideValue(IServiceProvider serviceProvider_)
    {
        return Instance;
    }

    #region Implementation of IMultiValueConverter

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values[0].ToString();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        var result = 0m;
        if (value != null)
        {
            decimal.TryParse(value.ToString(), out result);
        }
        return new object[] { result };
    }

    #endregion
}

1 Ответ

10 голосов
/ 18 ноября 2010

Я немного покопался в Интернете, и оказалось, что это было сломано в WPF 4. Кто-то с почти идентичной мне проблемой разместил здесь: http://www.go4answers.com/Example/textbox-shows-old-value-being-coerced-137799.aspx

«Ответ 8» утверждает этобыл сломан в WPF 4 и предлагает решение, которое должно фактически использовать UpdateSourceTrigger="Explicit", но обрабатывать событие TextChanged и вызывать BindingExpression.UpdateSource () , чтобы заставить изменения в текстовом поле отражаться в базовомзначение, как если бы UpdateSourceTrigger="PropertyChanged", согласно этому сообщению: Принудительный WPB TextBox больше не работает в .NET 4.0

Я реализовал это, но вот, есть еще побочные эффекты, вв частности, что каждое нажатие клавиши вызывало переход каретки к началу текстового поля из-за обновления источника и возникновения события PropertyChanged .Кроме того, любые начальные или конечные нули или десятичные знаки, введенные с целью ввода дальнейших цифр, будут немедленно уничтожены.Таким образом, простое условие для проверки проанализированного десятичного значения текстового поля по отношению к базовому значению разрешило это.

Все, что было нужно, - следующий обработчик события:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    var tb = (TextBox)e.Source;
    MultiBindingExpression binding = BindingOperations.GetMultiBindingExpression(tb, TextBox.TextProperty);

    decimal result = 0m;
    decimal.TryParse(tb.Text, out result);

    if ((decimal)GetValue(ValueProperty) != result && binding != null)
    {
        int caretIndex = tb.CaretIndex;
        binding.UpdateSource();
        tb.CaretIndex = caretIndex;
    }
}
...