Reputation: 4125
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
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