Dr. Strangelove
Dr. Strangelove

Reputation: 3328

MultiBinding with Multiple output

Lets consider an animal Model as following:

public class Animal
{
    public string name { set; get; }
    public int age { set; get; }
}

and a collection of Animal object as following:

Dictionary<string, Dictionary<string, ObservableCollection<Animal>>> animals;

in my ViewModel I populate animals like:

var lions = new ObservableCollection<Animal>();
lions.Add(new Animal("myLion 1", 2));
var tigers = new ObservableCollection<Animal>();
tigers.Add(new Animal("myTiger 1", 1));
var wild = new Dictionary<string, ObservableCollection<Animal>>();
wild.Add("Lion", lions);
wild.Add("Tiger", tigers);


var cats = new ObservableCollection<Animal>();
cats.Add(new Animal("myCat 1", 2));
cats.Add(new Animal("myCat 2", 4));
var dogs = new ObservableCollection<Animal>();
dogs.Add(new Animal("myDog 1", 1));
var pets = new Dictionary<string, ObservableCollection<Animal>>();
pets.Add("cat", cats);
pets.Add("dog", dogs);


animals = new Dictionary<string, Dictionary<string, ObservableCollection<Animal>>>();
animals.Add("wild animals", wild);
animals.Add("pets", pets);

In the View I define two ComboBoxes and one DataGrid which are described as following:

My DataGrid is defined and bound as following:

<DataGrid>
    <DataGrid.ItemsSource>
        <MultiBinding Converter="{StaticResource animalSelectionConverter}">
            <Binding Path="selectedAnimalType"/>
            <Binding Path="selectedAnimalCategory"/>
        </MultiBinding>
    </DataGrid.ItemsSource>
    <DataGrid.Columns>
        <DataGridTextColumn Header="Animal Type" Binding="{Binding Path=XYZ}"/> <!--The Question-->
        <DataGridTextColumn Header="Name" Binding="{Binding name}"/>
        <DataGridTextColumn Header="Age" Binding="{Binding age}"/>
    </DataGrid.Columns>
</DataGrid>

And finally, I define the AnimalSelectionConverter as following:

public class AnimalSelectionConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ViewModel.Default.animals[(string)values[0]][(string)values[1]];
    }    
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Question :

I'm binding Animal.name and Animal.age of selected collection of animals to columns of DataGrid. However, I'm also interested to show the SelectedItem of first combobox (i.e., selectedAnimalType) on the DataGrid. For example, I would like to see columns such as following on my DataGrid:

| Animal Type |    Name     |   Age   |
---------------------------------------
|     pets    |   myCat 1   |    2    |
|     pets    |   myCat 2   |    4    |

pets are passed by selectedAnimalType to the converter, and converter uses this information plus selectedAnimalCategory to get myCat 1 and myCat 2. Therefore, would it be possible to return multiple values from the MultiValueConverter so that I see my DataGrid as aforementioned ?

Just in case anyone is interested to see a minimal possible running code example, I prepared a small project of my question and you can find the source-code here on DropBox.

Upvotes: 1

Views: 827

Answers (1)

BradleyDotNET
BradleyDotNET

Reputation: 61369

The weirdness of that design aside...

No, you cannot The signature of Convert is:

Object Convert(
    Object[] values,
    Type targetType,
    Object parameter,
    CultureInfo culture
)

(MSDN)

So you have to return a single object. That object could even be a Tuple<string, object> which would approximate multiple return values, but thats an even worse design.

If you want that data in your DataGrid I would just name your combo-box and do an ElementName binding:

<DataGridTextColumn Header="Animal Type" 
Binding="{Binding ElementName=TypeComboBox, Path=SelectedItem}"/>

In a more general case, access the main data context by naming your root element "Root" and doing this:

<DataGridTextColumn Header="Animal Type" 
Binding="{Binding ElementName=Root, Path=DataContext.SelectedType}"/>

Upvotes: 1

Related Questions