views:

40

answers:

1

Q: Why does my custom TextBox UserControl using a MultiBinding and IMultiValueConverter gets its Convert() method called only once (during instanciation) ??

I have defined a UserControl that requires a MultiBinding and a IMultiValueConverter in order to change its behavior/presentation upon 2 indenpendant DependencyProperty.

<proj:MyControl Value="10" Digits="1" />

UserControl:

<UserControl x:Class="MyControl"
             x:Name="uc"
             ...>

    <UserControl.Resources>
        <conv:DecimalToStringMultiConverter x:Key="DecToString" />
    </UserControl.Resources>

    [...]

    <Grid>
        <ctrl:VTextBox x:Name="vTb" Grid.Column="0" Margin="0,0,2,0">
            <ctrl:VTextBox.Text>
                <MultiBinding Converter="{StaticResource DecToString}" UpdateSourceTrigger="LostFocus" Mode="TwoWay">
                    <Binding ElementName="uc" Path="Value" Mode="TwoWay" />
                    <Binding ElementName="uc" Path="Digits" Mode="TwoWay" />
                </MultiBinding>
            </ctrl:VTextBox.Text>
        </ctrl:VTextBox>
    </Grid>
</UserControl>

When executing the application, the UserControls are all correctly instanciated. However, the IMultiValueConverter.Convert() method gets called only once.

Using an simple Binding + IValueConverter with a constant ConvertParameter worked great: the converter's Convert() method would get called everytime the TextBox contained inside the UserControl had its Text property changed.

Design changed and I had to resort to using a MultiBinding + IMultiValueConverter, and now the Convert() method only gets called once, and the TextBox.Text property is never updated upon the LostFocus() event.

What gives?

The MultiValueConverter is defined as below. I just wrap the IMultiValueConverter upon the IValueConverter to reuse existing code.

[ValueConversion(/*sourceType*/ typeof(Decimal), /*targetType*/ typeof(string))]
public class DecimalToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return "0.00";

        int? digits = parameter as int?;

        if(digits == null)
            digits = 2;

        NumberFormatInfo nfi = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone();
        nfi.NumberGroupSeparator = " ";
        nfi.CurrencyDecimalSeparator = ".";
        nfi.NumberDecimalDigits = (int)digits;

        return ((decimal)value).ToString("n", nfi);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return 0.00m;

        decimal d;

        return decimal.TryParse((string)value, out d) ? d : 0.00m;
    }
}

[ValueConversion(/*sourceType*/ typeof(Decimal), /*targetType*/ typeof(string))]
public class DecimalToStringMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        DecimalToStringConverter conv = new DecimalToStringConverter();
        return conv.Convert(values[0], targetType, values.Length > 1 ? values[1] : null, culture);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        DecimalToStringConverter conv = new DecimalToStringConverter();
        return new[] { conv.ConvertBack(value, targetTypes[0], null, culture) };
    }
}
A: 

It seems like you have some conflicting expectations about the updating behavior of the Binding and TextBox. The only reason Convert will be called multiple times is if the values of Digits or Value change multiple times, and there is nothing in your posted code to indicate that will happen. Changes to TextBox.Text won't cause calls to Convert, but should instead be calling ConvertBack on every change+LostFocus. Are you seeing that when you run your code?

You also need to return two values, instead of the one there now, from your ConvertBack method in order to supply both of the Bindings used in the MultiBinding with values.

John Bowen
You are right. My understanding is that the traditional Binding makes any change to the Text property update the bound DependencyProperty (in this case, Value). Upon updating Value, the Convert() method gets called (that does my custom formating and other things removed for simplicity in the example above). -- However, the MultiBinding, although it updates Value, it doesn't look like it triggers the Convert() method. Like if the 2way binding was broken somehow... I added some code-behind to update Value (and used OneWay binding instead) and everything is fine now, thanks!
matthew.perron