Wednesday, July 16, 2014

Subtle effect of UpdateSourceTrigger

I'm writing my own numeric edit box by styling a standard textbox. Everything was going well until I notices that removing the first zero from "0.00" was causing problems. On the screen the textbox showed ".00" but the textbox text property during the PreviewKeyDown event was still "0.00" (that's in the keydown event after the one that deleted the zero). After a lot of digging I realized the problem was that the textbox had UpdateSourceTrigger=PropertyChanged.

Try out the following XAML and VB to see the problem. Run the project with the Output window visible. Put the cursor after the first zero in the top textbox and press backspace, then cursor right. The textbox will show ".00" but the TextBox.Text property will be "0.00". Interestingly the TextBox.GetLineText(0) method returns the correct value.

Now do the same thing with the bottom TextBox and you will see the TextBox.text property now shows the correct value. The only difference is the UpdateSourceTrigger value.


<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="120"></ColumnDefinition> <ColumnDefinition Width="120"></ColumnDefinition> </Grid.ColumnDefinitions> <Label Grid.Row="0" Grid.Column="0" Content="PropertyChanged"></Label> <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Decimal0, Mode=TwoWay, StringFormat={}{0:0.00}, UpdateSourceTrigger=PropertyChanged}" PreviewKeyDown="BoundTextBox_KeyDown"></TextBox> <Label Grid.Row="1" Grid.Column="0" Content="LostFocus"></Label> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Decimal1, Mode=TwoWay, StringFormat={}{0:0.00}, UpdateSourceTrigger=LostFocus}" PreviewKeyDown="BoundTextBox_KeyDown"></TextBox> </Grid> </Window> Class MainWindow Public Property Decimal0 As Decimal = 0 Public Property Decimal1 As Decimal = 0 Private Sub BoundTextBox_KeyDown(sender As System.Object, e As System.Windows.Input.KeyEventArgs) Dim oTextBox As TextBox = DirectCast(e.OriginalSource, TextBox) Trace.WriteLine("Text=[" & oTextBox.Text & "], Line(0)=[" & oTextBox.GetLineText(0) & "]") End Sub End Class
The problem is that the Textbox is attempting to update the backing store Decimal value every time the value changes. When the content of the textbox is a valid decimal it gets stored and reformatted so that the Text property does not match what's visible on the screen.

Surely this is a bug.

This behavior can be altered by either setting UpdateSourceTrigger=LostFocus (or Explicit) or setting Mode=OneWay.

No comments:

Post a Comment