Please, for definitely fix see the second part of this article 😉

Hi!
Today I spent 2 hours of my coding in understanding the reason why a TextBox didn’t display a new value set by CoerceValue Callback!

Click here to download the example.

This is the XAML code containing the Text property binding.

<Window x:Class="TextBindingAndCoerceValue.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="Binding and CoerceValue" 
    Height="300" 
    Width="300">
    
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <Label Content="Insert Value (0-100):" VerticalAlignment="Center"/>
            <TextBox Name="_txtBox" Margin="5" Text="{Binding MyValue, Mode=TwoWay}" HorizontalAlignment="Stretch" Width="152"/>
        </StackPanel>
        <Button Margin="5" Content="Set New Value"/>
    </StackPanel>
    
</Window>

And here you are the code behind (affected with described issue).

    public partial class Window1 : Window
    {

        public int MyValue
        {
            get { return (int)GetValue(MyValueProperty); }
            set { SetValue(MyValueProperty, value); }
        }
        public static readonly DependencyProperty MyValueProperty =
            DependencyProperty.Register("MyValue", typeof(int), typeof(Window1), new UIPropertyMetadata(0, new PropertyChangedCallback(MyValueChanged), new CoerceValueCallback(MyValueCoerceValue)));

        private static void MyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        private static object MyValueCoerceValue(DependencyObject d, object value)
        {
            int intValue = (int)value;

            if (intValue < 0)
                intValue = 0;
            else if (intValue > 100)
                intValue = 100;

            return intValue;
        }


        public Window1()
        {
            InitializeComponent();
        }
    }

The problem is that MyValue Dependency Property is properly updated when inserting a value less than 0 or greater than 100, but the TextBlock still display the just typed value!
Honestly I didn’t understood the real cause of the problem and every WPF developers should expect TwoWay binding to display coerced value! Instead, even with a binding, the text block is still presenting what is typed into it ignoring the update!

As developer, you need to force a target update on the binding (via UpdateTarget()) when the coersion fails if you want to display the coerced value. Here you are the problem fixing:


        private static object MyValueCoerceValue(DependencyObject d, object value)
        {
            int intValue = (int)value;

            if (intValue < 0)
                intValue = 0;
            else if (intValue > 100)
                intValue = 100;

            Window1 w1 = d as Window1;
            w1._txtBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();

            return intValue;
        }

Happy coding,
Marzio.

Advertisements