What I want to do is simple, show one formatting when a TextBox has focus and another when it doesn't. In my case I'm rounding a number to 3 digits when not focused but showing the actual, entire number when focused for editing.
I have a fairly simple solution using a multibinding and I feel like I'm almost there. Everything works as expected and there are no errors in the immediate window, but the binding won't update the source.
I'm using this style to pass my binding and whether or not the TextBox has focus to the converter.
<Style x:Key="RoundedTextBox" TargetType="{x:Type ContentControl}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox x:Name="TB" TextAlignment="Right" DataContext="{TemplateBinding Content}">
<TextBox.Text>
<MultiBinding Converter="{StaticResource DecRounder}" UpdateSourceTrigger="PropertyChanged">
<MultiBinding.Bindings>
<Binding ElementName="TB" Path="DataContext" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" BindsDirectlyToSource="True" />
<Binding ElementName="TB" Path="IsFocused" Mode="OneWay" />
</MultiBinding.Bindings>
</MultiBinding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Example usage:
<ContentControl Style="{StaticResource RoundedTextBox}"
Content="{Binding Path=Model.SomeNumber}" />
And the multi-value converter is here.
public class DecimalRoundingConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Length != 2)
throw new Exception("Invalid number of values for DecimalRoundingConverter!");
if (values[0] is string)
return values[0];
if (values[0] != null && !(values[0] is decimal))
throw new Exception("Invalid type for first value used with DecimalRoundingConverter!");
if (!(values[1] is bool))
throw new Exception("Invalid type for second value used with DecimalRoundingConverter!");
if (targetType != typeof(string))
throw new Exception("Invalid target type used with DecimalRoundingConverter!");
if (values[0] == null)
return null;
decimal number = (decimal)values[0];
bool isFocused;
if (values[1] == null)
isFocused = true;
else if (values[1] is bool)
isFocused = (bool)values[1];
else
if (!bool.TryParse((string)values[1], out isFocused))
throw new Exception("Invalid converter parameter used with DecimalRoundingConverter!");
return string.Format(isFocused ? "{0:.#############################}" : "{0:.###}", number);
}
public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
{
decimal d;
var ret = new object[2];
if (value == null)
ret[0] = null;
else if (decimal.TryParse((string)value, out d))
ret[0] = d;
else
ret[0] = value;
ret[1] = Binding.DoNothing;
return ret;
}
}