I have a text box whose Text property has a TwoWay MultiBinding with UpdateSourceTrigger set to PropertyChanged. The first Binding is to a dependency property (Value) which has a PropertyChangedCallBack function that rounds the value to one decimal place.
The purpose of the text box is to perform the rounding as the user types rather than when the text box loses focus, hence why UpdateSourceTrigger is set to PropertyChanged.
The problem I am having is that if text is entered that does NOT result in Value changing, the Text property and Value become out of sync. Only if the rounding operation causes Value to change does Text get updated on the fly. E.g., if Text and Value are both 123.4 and the user types 1 after this, Value is rounded to the same value (123.4), but Text shows 123.41. However, if 9 is then typed after the 4, Value is rounded up to 123.5. And because of this actual change, Text is then updated to the same (123.5).
Is there any way of forcing a text box to update from its source even when the source hasn't changed since the last trigger? I have tried using BindingExpressionBase.UpdateTarget()
but this only works when UpdateSourceTrigger is set to Explicit, which can't be used as Value no longer gets updated prior to a suitable time where UpdateTarget could be called (such as a TextInput handler). I have tried other methods such as explicitly updating the Text value from the bound Value, forcing an actual change to Value temporarily to invoke an update, but these "hacks" either don't work or cause other problems.
Any help would be greatly appreciated.
The code is below.
XAML snippet
<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# snippet
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));
}
Converter class required
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
}