views:

5582

answers:

3

I currently have two text boxes in which accept any number. I have text block that takes the two numbers entered and calculates the average. I was wondering if there was a way I could bind this text block to both text boxes and utilize a custom converter to calculate the average? I current am catching the text changed events on both text boxes and calculating the average that way, but I am under the assumption data binding would be more efficient and easier.

Any help would be greatly appreciated. Thanks.

+14  A: 

You're looking for MultiBinding.

Your XAML will look something like this:

<TextBlock>
  <TextBlock.Text>
    <MultiBinding Converter="{StaticResource myConverter}">
      <Binding Path="myFirst.Value" />
      <Binding Path="mySecond.Value" />
    </MultiBinding>
  </TextBlock.Text>
</TextBlock>

With reasonable replacements for myConverter, myFirst.Value, and mySecond.Value.

Jacob Carpenter
@Jacob: In case I want to pass a constance value to one of the binding of multi-binding, how can I do it? Please help!
Nam Gi VU
+7  A: 

Create a converter that implements IMultiValueConverter. It might look something like this:

class AverageConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int total = 0;
        int number = 0;
        foreach (object o in values)
        {
            int i;
            bool parsed = int.TryParse(o.ToString(), out i);
            if (parsed)
            {
                total += i;
                number++;
            }
        }
        if (number == 0) return 0;
        return (total/number).ToString();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

A multivalue converter receives an object array, one for each of the bindings. You can process these however you need, depending on whether you're intending it for double or int or whatever.

If the two textboxes are databound, you can use the same bindings in the multibinding for your textblock (remembering to notify when the property changes so that your average is updated), or you can get the text value by referring to the textboxes by ElementName.

<TextBox Text="{Binding Value1}" x:Name="TextBox1" />
<TextBox Text="{Binding Value2}" x:Name="TextBox2" />

<TextBlock>
   <TextBlock.Text>
      <MultiBinding Converter="{StaticResource AverageConverter}">
         <Binding ElementName="TextBox1" Path="Text" />
         <Binding ElementName="TextBox2" Path="Text" />
         <!--  OR  -->
         <!-- <Binding Path="Value1" />  -->
         <!-- <Binding Path="Value2" />  -->

      </MultiBinding>
   </TextBlock.Text>
</TextBlock>
Donnelle
+1  A: 

Or, you could make a property in code behind, and bind the TextBlock to that ... I do that all the time, and it's a little simpler than making a converter, then doing that same code there.

Example: (in your code behind of the xaml):

public double AvgValue
{
    get { return (valueA + valueB) / 2.0; }
}

And then, in your XAML:

<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=AvgValue}" />

That's a LOT simpler than a custom converter.

Timothy Khouri
Sorry for those of you who saw this without the XAML... I forgot to space it... it's there now :)
Timothy Khouri
You can also put an x:Name on your control and use that in the text binding rather than RelativeSource :)
Donnelle
Umm, that doesn't work. The AvgValue property (in your example) doesnt exist in the UserControl class, and changing the UserControl x:type to the TableRow (the class that I have extending the UserControl class) wont find the ancestor. So can you extend this example to a more working solution?
Nick