Search code examples
wpfvalidationdata-bindingtextboxbinding

Change Binding Format with Focus


WPF application using MVVM. I have a TextBox whose Text property is bound to a decimal property of the view model. This property represents a currency value. I would like the TextBox to display the data in currency format. To this end, I have set StringFormat to "c" in the Binding. This works as expected.

The problem is that, if SourceUpdateTrigger is PropertyChanged, when the user starts typing, formatting is applied after entering the first character and the caret is then positioned before the character just entered. This means that the next character will be entered before the first instead of after. If SourceUpdateTrigger is LostFocus then the user must shift focus to another control before the OK button is enabled, which occurs after validating the currency field.

What I was hoping to do was handle the GotFocus and LostFocus events, get a reference to the Binding and change its StringFormat property. I have no issue with this in regards to MVVM because it's a purely UI issue. The problem is that an exception was thrown and I was told that the Binding could not be changed after being used.

I've considered various other options, including a custom converter. That didn't work though, because I couldn't work out how to use the ConverterParameter to expose the control's IsFocused property to the converter.

Anyone have any ideas?


Solution

  • You can use a data trigger in a data template to dynamically change your binding for a text box.

     <DataTemplate x:Key="DataTemplate1">
          <TextBox x:Name="TheTextBox"
                   Text="{Binding Path=ThePropertyPath, StringFormat={}{0:p0}, UpdateSourceTrigger=PropertyChanged}" />
        <DataTemplate.Triggers>
           <!--  Set the TextBox.Text Binding but this time leave the StringFormat off -->
           <DataTrigger Binding="{Binding Path=IsKeyboardFocusWithin, ElementName=TheTextBox}" Value="True">
                <Setter TargetName="TheTextBox" Property="Text" Value="{Binding Path=ThePropertyPath, UpdateSourceTrigger=PropertyChanged}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=IsFocused, ElementName=TheTextBox}" Value="True">
                <Setter TargetName="TheTextBox" Property="Text" Value="{Binding Path=ThePropertyPath, UpdateSourceTrigger=PropertyChanged}" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>