Search code examples
c#wpfxamlmultibindingconverters

Bind text to converter based on change in two different objects


I am trying to bind the text of a textblock based on two things -

  • Object ShoppingList
  • Object Items(which is a property of the ShoppingList object. Type is List).

I want to invoke the converter as I want the text to be dependent on change in the value of either of the above.The only way that I could think of was this way as shown below. But this is not possible as I cannot bind the ConverterParameter to the object ShoppingList as it is not a dependency property .

  <TextBlock
           Margin="5"
           TextWrapping="Wrap"
           Text="{Binding Items, Converter={StaticResource ABCDConverter}, ConverterParameter="???" />

Below is the converter I had written

Convert(Items obj, object par, xyz culture)
{
      if (obj != null && par!=null)
      {
            var parameter = (ShoppingList)par;
            // Different Logic to determine the string to be returned
      }
   return string.Empty;
}

In simple words, how can I invoke the converter based on changes in either of Items or ShoppingList


Solution

  • Sounds like you're looking for MultiBinding, paired with an IMultiValueConverter.

    That being said, I would strongly suggest that you determine the needed value in your ViewModel since you already have all the needed properties there and you know when and how they change. Once you have your derived property, just use regular binding to bind to it in the View. Using MultiBindings will generally tend to break your separation of concerns as you will end up adding specific logic to the converter that really should be in the ViewModel. One case where I did find MultiBindings indispensable was in a group header template (i.e. for a DataGrid). It would be pretty well impossible to get the values from the ViewModel as you don't know how many groups you have or what they will contain at runtime as there are just too many moving parts. In this case, MultiBindings were great.
    On the other hand, if you are just targeting a specific element that you know about at design time, you should generally avoid using MultiBinding for the reasons stated above. In addition, MutliBinding are quite verbose compared to regular Bindings, making your XAML harder to read which creates a greater potential for bugs and limits future extensibility and maintainability.

    But if you must, here's a simple example:

    XAML

    <Window x:Class="testapp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:testapp"
        Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <local:MyMultiConverter x:Key="multiTextConverter"/>
        </Window.Resources>
    
        <Grid>
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding Converter="{StaticResource multiTextConverter}">
                        <Binding Path="someProp"/>
                        <Binding Path="someOtherProp" />
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </Grid>
    </Window>
    

    Converter

    public class MyMultiConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, 
                object parameter, CultureInfo culture)
        {
            string ret = null;
            if(values.Count() > 1)
            {
                string value1 = values[0] as string;
                string value2 = values[1] as string;
                ret = value1 + value2;
            }
            return ret;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, 
                object parameter, CultureInfo culture)
        {
        }
    }
    

    Now, if you're binding to a List, and, assuming you care when items are added or removed, you will rather need to use an ObservableCollection as it implements the INotifyCollectionChanged Interface see here.But this is not quite enough, as the binding will not be subscribing to that event. You will need to listen to the change in collection in your DataContext (ViewModel?). Then create some dummy property, and when the collection changes, just increment the dummy property (with INotifyPropertyChanged of course) and use that property in the multibinding as a third binding. This way, the TextBlock text will get updated if either a property changes, or your list changes.

    If you don't care about the changes in the collection, only when you assign a whole new collection, then the sample provided will work as is.