13

我有一个文本框,其Text属性有一个TwoWay MultiBinding,其中UpdateSourceTrigger设置为PropertyChanged。第一个Binding是一个依赖属性 ( Value ),它有一个PropertyChangedCallBack函数,该函数将值四舍五入到小数点后一位。

文本框的目的是在用户键入时执行舍入,而不是在文本框失去焦点时执行舍入,因此UpdateSourceTrigger设置为PropertyChanged

我遇到的问题是,如果输入的文本不会导致更改,则文本属性和会不同步。只有当舍入操作导致Value发生变化时, Text才会即时更新。例如,如果TextValue都是 123.4 并且用户在此之后键入 1,则Value将四舍五入为相同的值 (123.4),但Text显示 123.41。但是,如果在 4 之后键入 9,则Value向上舍入为 123.5。并且由于这种实际变化,文本随后更新为相同的 (123.5)。

即使自上次触发以来源没有更改,是否有任何方法可以强制文本框从其源更新?我尝试过使用BindingExpressionBase.UpdateTarget(),但这仅在UpdateSourceTrigger设置为Explicit时有效,因为在可以调用UpdateTarget的合适时间(例如TextInput处理程序)之前不再更新Value,因此无法使用。我尝试了其他方法,例如从绑定的Value中显式更新Text值,暂时强制对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
}
4

1 回答 1

11

我在 Internet 上进行了一些挖掘,结果发现这在 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",根据这篇文章: 强制 WPF 文本框在 .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;
    }
}
于 2010-11-18T16:20:35.700 回答