13

TextプロパティにTwoWay MultiBindingがあり、 UpdateSourceTriggerPropertyChangedに設定されているテキスト ボックスがあります。最初のBindingは、値を小数点以下 1 桁に丸めるPropertyChangedCallBack関数を持つ依存関係プロパティ ( Value )に対するものです。

テキスト ボックスの目的は、テキスト ボックスがフォーカスを失ったときではなく、ユーザーが入力したときに丸めを実行することです。そのため、 UpdateSourceTriggerがPropertyChangedに設定されています。

私が抱えている問題は、値が変更されないテキストが入力された場合、 Textプロパティとが同期しなくなることです。丸め操作によってValueが変更された場合にのみ、 Textがその場で更新されます。たとえば、TextValueが両方とも 123.4 で、ユーザーがこの後に 1 を入力した場合、Valueは同じ値 (123.4) に丸められますが、Textは 123.41 を示します。ただし、4 の後に 9 を入力すると、Valueは 123.5 に切り上げられます。この実際の変更により、Textは同じ (123.5) に更新されます。

ソースが最後のトリガーから変更されていない場合でも、テキスト ボックスをソースから強制的に更新する方法はありますか? 使用してみましたが、これはUpdateSourceTriggerがExplicitに設定されてBindingExpressionBase.UpdateTarget()いる場合にのみ機能します。これは、 UpdateTargetを呼び出すことができる適切な時間 ( TextInputハンドラーなど)の前にが更新されないため、使用できません。バインドされた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

私はインターネットで少し掘り下げましたが、これは WPF 4 で壊れていたことがわかりました。私とほぼ同じ問題を抱えている人がここに投稿しました :強制されている値-137799.aspx

「回答8」は、これがWPF 4で壊れていたと述べており、実際に使用するUpdateSourceTrigger="Explicit"がTextChangedイベントを処理し、BindingExpression.UpdateSource()を呼び出して、テキストボックスの変更が基になる値に反映されるようにする解決策を提案していますUpdateSourceTrigger="PropertyChanged"、この投稿によると: Coerce a WPF TextBox not working again in .NET 4.0

私はこれを実装しましたが、さらに副作用がありました。特に、ソースを更新してPropertyChangedイベントを発生させたため、キーストロークごとにキャレットがテキスト ボックスの先頭にジャンプしました。また、さらに数字を入力するつもりで入力された先頭または末尾のゼロまたは小数点以下の桁数は、すぐに消去されます。そのため、テキスト ボックスの解析された 10 進数値と基になる値をチェックする単純な条件によって、これが解決されました。

必要なのは次のイベント ハンドラーだけです。

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 に答える