How do I bind series in XAML? Something like an ItemsSource (SeriesSource?) property with a SeriesTemplate - to set the DataMapping and other series properties - would make the most sense to me. If this is not possible, then does anyone have an attached property that would do the equivalent work.
Glad it helped! Let me know if you have any questions.
-Graham
I was trying to figure out how to bind a chart to a collection for initializing the Series in the chart and ended coming across this post. Excellent information that works very well.
Thanks, Graham!
see here for a version that should work with XamDataChart : http://community.infragistics.com/forums/p/40011/242380.aspx#242380
Here's a reposting which will hopefully be less mangled.
<Window x:Class="WpfApplication22.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" x:Name="theWindow" xmlns:igChart="http://infragistics.com/Chart" xmlns:local="clr-namespace:WpfApplication22"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="1*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <igChart:XamChart x:Name="theChart"> <local:SeriesBinder.BindingInfo> <local:SeriesBindingInfo ItemsSource="{Binding}"> <local:SeriesBindingInfo.SeriesTemplate> <igChart:Series ChartType="{Binding ChartType}" DataMapping="{Binding DataMapping}" DataSource="{Binding DataSource}"> </igChart:Series> </local:SeriesBindingInfo.SeriesTemplate> </local:SeriesBindingInfo> </local:SeriesBinder.BindingInfo> </igChart:XamChart> <Button Grid.Row="1" Click="Button_Click"> Add Series </Button> </Grid> </Window>
And the window code behind:
public partial class Window1 : Window { public Window1() { InitializeComponent(); DataContext = GetSampleData(); } private ObservableCollection<TestSeriesItem> GetSampleData() { ObservableCollection<TestSeriesItem> data = new ObservableCollection<TestSeriesItem>() { new TestSeriesItem() { ChartType = ChartType.Column, DataMapping = "Label = Label; Value = Value", DataSource = new ObservableCollection<TestDataItem>() { new TestDataItem() { Label = "1", Value = 1 }, new TestDataItem() { Label = "2", Value = 2 } } }, new TestSeriesItem() { ChartType = ChartType.Line, DataMapping = "Label = Label; Value = Value", DataSource = new ObservableCollection<TestDataItem>() { new TestDataItem() { Label = "1", Value = 1 }, new TestDataItem() { Label = "2", Value = 2 } } } }; return data; } private void Button_Click(object sender, RoutedEventArgs e) { ((ObservableCollection<TestSeriesItem>)DataContext).Add( new TestSeriesItem() { ChartType = ChartType.Area, DataMapping = "Label = Label; Value = Value", DataSource = new ObservableCollection<TestDataItem>() { new TestDataItem() { Label = "1", Value = 5 }, new TestDataItem() { Label = "2", Value = 6 } } }); } } public class TestSeriesItem { public ChartType ChartType { get; set; } public string DataMapping { get; set; } public object DataSource { get; set; } } public class TestDataItem { public string Label { get; set; } public double Value { get; set; } }
And the attached properties and helper classes
class DependencyPropertyEnumerator { public static IEnumerable<DependencyProperty> DependencyProperties(Type type) { var props = from prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy) join field in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy) on prop.Name + "Property" equals field.Name into propGroup from item in propGroup.DefaultIfEmpty(null) where item != null select (DependencyProperty)item.GetValue(null); return props; } public static bool DependencyPropertyHasBinding( FrameworkContentElement obj, DependencyProperty dp, out BindingExpression be) { be = null; if (obj == null) { return false; } be = obj.GetBindingExpression (dp); if (be != null) { return true; } return false; } public static bool DependencyPropertyHasValue( FrameworkContentElement obj, DependencyProperty dp, out object value) { value = null; if (obj == null) { return false; } object localValue = obj.ReadLocalValue(dp); object defaultValue = dp.GetMetadata(typeof(Series)).DefaultValue; if (localValue != DependencyProperty.UnsetValue) { if ((localValue == null && defaultValue != null) || !localValue.Equals(defaultValue)) { value = localValue; return true; } } return false; } } class SeriesBindingInfo : FrameworkElement { private XamChart targetChart; public XamChart TargetChart { get { return targetChart; } set { targetChart = value; //bridge the data context from the parent hierarchy. Binding binding = new Binding("DataContext") { Source = targetChart }; SetBinding(DataContextProperty, binding); Clear(); UpdateFromCollection(ItemsSource); } } public static DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(SeriesBindingInfo), new FrameworkPropertyMetadata(null, (o, e) => { (o as SeriesBindingInfo).OnItemsSourceChanged(e); })); public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static DependencyProperty SeriesTemplateProperty = DependencyProperty.Register("SeriesTemplate", typeof(Series), typeof(SeriesBindingInfo), new FrameworkPropertyMetadata(null, (o, e) => { (o as SeriesBindingInfo).OnSeriesTemplateChanged(e); } )); public Series SeriesTemplate { get { return (Series)GetValue(SeriesTemplateProperty); } set { SetValue(SeriesTemplateProperty, value); } } protected void OnItemsSourceChanged(DependencyPropertyChangedEventArgs e) { Clear(); if (e.OldValue != null) { UnRegCollection(e.OldValue as IEnumerable); } if (e.NewValue != null) { RegCollection(e.NewValue as IEnumerable); UpdateFromCollection(e.NewValue as IEnumerable); } } protected void OnSeriesTemplateChanged(DependencyPropertyChangedEventArgs e) { Clear(); UpdateFromCollection(ItemsSource); } private void Clear() { if (TargetChart == null) { return; } TargetChart.Series.Clear(); } private void UpdateFromCollection(IEnumerable collection) { if (collection == null) { return; } foreach (object item in collection) { AddItem(item); } } private void AddItem(object item) { if (SeriesTemplate == null) { return; } Series series = new Series(); series.DataContext = item; SetSeriesBindings(series); TargetChart.Series.Add(series); } private void SetSeriesBindings(Series series) { if (SeriesTemplate == null) { return; } foreach (DependencyProperty dp in DependencyPropertyEnumerator.DependencyProperties(typeof(Series))) { object value; BindingExpression be; if (DependencyPropertyEnumerator. DependencyPropertyHasBinding( SeriesTemplate, dp, out be)) { series.SetBinding(dp, be.ParentBinding); } else if (DependencyPropertyEnumerator. DependencyPropertyHasValue( SeriesTemplate, dp, out value)) { series.SetValue(dp, value); } } } private void RegCollection(IEnumerable collection) { if (collection is INotifyCollectionChanged) { (collection as INotifyCollectionChanged).CollectionChanged += new NotifyCollectionChangedEventHandler( SeriesBindingInfo_CollectionChanged); } } private void UnRegCollection(IEnumerable collection) { if (collection is INotifyCollectionChanged) { (collection as INotifyCollectionChanged).CollectionChanged -= new NotifyCollectionChangedEventHandler( SeriesBindingInfo_CollectionChanged); } } void SeriesBindingInfo_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (TargetChart == null) { return; } //this could be made more efficient of course. Clear(); UpdateFromCollection(ItemsSource); } } class SeriesBinder : DependencyObject { public static DependencyProperty BindingInfoProperty = DependencyProperty.RegisterAttached("BindingInfo", typeof(SeriesBindingInfo), typeof(SeriesBinder), new PropertyMetadata(null, (o, e) => { if (o is XamChart) { SeriesBindingInfo prev = GetBindingInfo(o); if (prev != null && prev.TargetChart != null) { prev.TargetChart = null; } if (e.NewValue != null) { (e.NewValue as SeriesBindingInfo).TargetChart = (XamChart)o; } } })); public static void SetBindingInfo(DependencyObject target, SeriesBindingInfo info) { target.SetValue(BindingInfoProperty, info); } public static SeriesBindingInfo GetBindingInfo(DependencyObject target) { return (SeriesBindingInfo)target.GetValue(BindingInfoProperty); } }
Glad you like it!
Yeah, there are probably other ways to achieve this too, I was experimenting a little bit with DataTemplates, but the Series isn't a visual control, so you would have to house it in a visual control to do the templating with a DataTemplate, but that was starting to seem less elegant.
So, this is the most elegant approach I've come up with so far. There may be an even cleaner way to do this, and if it comes to me, I'll try to remember to come back and update this.