Sturm
Sturm

Reputation: 4125

Binding to multiple sources

I'm plotting graphs with D3. At this moment I'm doing it by binding this way:

<d3:ChartPlotter x:Name="plotter" ItemsSource="Charts" Margin="20">

And I Add/Remove items to Charts and the plotter updates automatically. Works very well.

The problem is that I need to bind from several collections, but obviously I can't set ItemsSource twice. I've read something about CompositeCollections, but almost every article is based on a StaticResource, that is not my case.

 <d3:ChartPlotter x:Name="plotter"Margin="20">
 <d3:ChartPlotter.ItemsSource>
    <CompositeCollection>
      <CollectionContainer Collection="{Binding Charts}" />
      <CollectionContainer Collection="{Binding Charts2}" />
      </CompositeCollection>
  </d3:ChartPlotter.ItemsSource>

This code compiles but the binding doesn't work.

I've searched a lot but surprisingly I have not found the answer. I thought this hadto be a common task in WPF.

I'm open to other ways of binding multiple collections to a single ItemsSource, but adding manually each item from each sub collection to Charts I think it's too troublesome. Thank you.

EDIT:

I'm trying do it via MultiBinding and this is the scheme of the Converter

EDIT2:

Charts is an ObservableCollection<LineGraph>

public class ConcatConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        ObservableCollection<LineGraph> enumerables = new ObservableCollection<LineGraph>();

        foreach (LineGraph line in values[0])
        {
            enumerables.Add(line);
        }

        foreach (LineGraph line in values[1])
        {
            enumerables.Add(line);
        }

        return enumerables;

    }
    public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

I can't compile because of this error: "foreach statement cannon operate in variables of type "object" because "object" does not contain public definition for "GetEnumerator".

Upvotes: 0

Views: 2294

Answers (1)

It&#39;sNotALie.
It&#39;sNotALie.

Reputation: 22814

Use a MultiBinding. First, make a converter that will do what you want:

public class ConcatConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        List<IEnumerable> enumerables = new List<IEnumerable>();
        foreach (object obj in values)
        {
            IEnumerable temp = obj as IEnumerable;
            if (temp == null) throw new ArgumentException();
            enumerables.Add(temp);
        }
        List<dynamic> enDynamic = new List<dynamic>();
        enDynamic.AddRange(enumerables);
        return Concat((dynamic)enDynamic);
    }
    private IEnumerable<T> Concat<T>(params IEnumerable<T>[] toConcat)
    {
        return toConcat.Aggregate((a, b) => a.Concat(b));
    }
    private IEnumerable Concat(params IEnumerable[] toConcat)
    {
        ArrayList temp = new ArrayList();
        foreach (IEnumerable x in toConcat)
        {
            foreach (object n in x)
            {
                temp.Add(n);
            }
        }
        return temp;
    }
    public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

(non-generic but I didn't want to use obtuse amounts of reflection)

Then add it to your window's resources:

<!--in your window declaration-->
xmlns:local="clr-namespace:YourNameSpace"
<!--after that-->
<Window.Resources>
    <ttp:ConcatConverter x:Key="Concat"/>
</Window.Resources>
<!--finally:-->
<d3:ChartPlotter.ItemsSource>
    <MultiBinding Converter="{StaticResource ResourceKey=Concat}">    
        <Binding Source="Charts"/>
        <Binding Source="Charts2"/>
    </MultiBinding>
</d3:ChartPlotter.ItemsSource>

Upvotes: 2

Related Questions