How can I bind the XamWebChart to a SeriesCollection. I'm getting data from a service, in Xml format, containing the values and styles for few series' with several datapoints.
I created SeriesCollection in my ViewModel. Now how should I bind my view to make it use the SeriesCollection to draw graphs.
You will want to check this post to get you started:
https://es.infragistics.com/community/forums/f/ultimate-ui-for-wpf/38169/series-binding-in-xaml-custom-attached-property
Unfortunately there seems to be a problem with the forums this morning, so this post isn't currently accessible.
-Graham
Its back up. So you can check that out if you want. But the exampl linked is really more for if you want to create the series based on a template in Xaml. If you are already creating the series collection in your View Model have you tried just binding it to the chart?
I.E if the property holding the SeriesCollection on your DataContext is called Series. Have you tried
<XamWebChart Series="{Binding Series} ...
Sorry for the delay, I've been out of the office for a while. Here's something that can should get you started. The gist is to use an attached property to synchronize your source viewmodel with the series that are populated in the chart.The viewmodels for the series are produce the concrete series by means of a DataTemplate you provide which should produce a series when loaded.Here's an example of the Xaml:
<Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ig:XamDataChart Name="xamDataChart1" > <ig:XamDataChart.Axes> <ig:CategoryXAxis x:Name="xAxis" ItemsSource="{Binding Item.Data}" Label="{}{Label}"/> <ig:NumericYAxis x:Name="yAxis" /> </ig:XamDataChart.Axes> <local:SeriesBinder.BindingInfo> <local:SeriesBindingInfo ItemsSource="{Binding}"> <local:SeriesBindingInfo.SeriesTemplate> <DataTemplate> <ig:LineSeries ItemsSource="{Binding Item.Data}" ValueMemberPath="{Binding Item.ValueMemberPath}" XAxis="{Binding Path=Owner.Axes[0]}" YAxis="{Binding Path=Owner.Axes[1]}" > </ig:LineSeries> </DataTemplate> </local:SeriesBindingInfo.SeriesTemplate> </local:SeriesBindingInfo> </local:SeriesBinder.BindingInfo> </ig:XamDataChart> <Button Grid.Row="1" Content="Click" Click="Button_Click"/> </Grid>
And here is the code behind to support this usage:
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); DataContext = new TestSeries(); } private void Button_Click(object sender, RoutedEventArgs e) { (DataContext as TestSeries).Add( new TestSeriesItem()); } } public class TestSeries : ObservableCollection<TestSeriesItem> { public TestSeries() { Add(new TestSeriesItem()); Add(new TestSeriesItem()); Add(new TestSeriesItem()); } } public class TestSeriesItem { public TestSeriesItem() { Data = new TestData(); ValueMemberPath = "Value"; } public IEnumerable Data { get; set; } public string ValueMemberPath { get; set; } } public class TestData : ObservableCollection<TestDataItem> { private static Random rand = new Random(); public TestData() { for (int i = 0; i < 20; i++) { Add(new TestDataItem() { Label = i.ToString(), Value = rand.NextDouble() }); } } } public class TestDataItem { public string Label { get; set; } public double Value { get; set; } } public class SeriesDataContext : DependencyObject { public static DependencyProperty ItemProperty = DependencyProperty.Register("Item", typeof(object), typeof(SeriesDataContext), new PropertyMetadata(null, (o, e) => { })); public object Item { get { return GetValue(ItemProperty); } set { SetValue(ItemProperty, value); } } public static DependencyProperty OuterContextProperty = DependencyProperty.Register("OuterContext", typeof(object), typeof(SeriesDataContext), new PropertyMetadata(null, (o, e) => { })); public object OuterContext { get { return GetValue(OuterContextProperty); } set { SetValue(OuterContextProperty, value); } } public static DependencyProperty OwnerProperty = DependencyProperty.Register("Owner", typeof(XamDataChart), typeof(SeriesDataContext), new PropertyMetadata(null, (o, e) => { })); public XamDataChart Owner { get { return (XamDataChart)GetValue(OwnerProperty); } set { SetValue(OwnerProperty, value); } } } public class SeriesBindingInfo : FrameworkElement { public static DependencyProperty OwnerProperty = DependencyProperty.Register("Owner", typeof(XamDataChart), typeof(SeriesBindingInfo), new PropertyMetadata(null, (o, e) => { (o as SeriesBindingInfo).OnOwnerChanged(e); })); public XamDataChart Owner { get { return (XamDataChart)GetValue(OwnerProperty); } set { SetValue(OwnerProperty, value); } } public static DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(SeriesBindingInfo), new PropertyMetadata(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(DataTemplate), typeof(SeriesBindingInfo), new PropertyMetadata(null, (o, e) => { (o as SeriesBindingInfo).OnSeriesTemplateChanged(e); } )); public DataTemplate SeriesTemplate { get { return (DataTemplate)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); } } public static DependencyProperty OuterContextProperty = DependencyProperty.Register("OuterContext", typeof(object), typeof(SeriesBindingInfo), new PropertyMetadata(null, (o, e) => { })); public object OuterContext { get { return GetValue(OuterContextProperty); } set { SetValue(OuterContextProperty, value); } } protected void OnSeriesTemplateChanged( DependencyPropertyChangedEventArgs e) { Clear(); UpdateFromCollection(ItemsSource); } private void OnOwnerChanged( DependencyPropertyChangedEventArgs e) { SetBinding(DataContextProperty, new Binding("OuterContext") { Source = this }); Clear(); UpdateFromCollection(ItemsSource); } private void Clear() { if (Owner == null) { return; } Owner.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 = SeriesTemplate.LoadContent() as Series; if (series != null) { SeriesDataContext context = new SeriesDataContext() { Item = item, Owner = Owner }; BindingOperations.SetBinding( context, SeriesDataContext.OuterContextProperty, new Binding("OuterContext") { Source = this }); series.DataContext = context; Axis xAxis = null; Axis yAxis = null; GetAxes(series, ref xAxis, ref yAxis); if (xAxis != null) { UpdateAxis(context, xAxis); } if (yAxis != null) { UpdateAxis(context, yAxis); } Owner.Series.Add(series); } } private void UpdateAxis(SeriesDataContext context, Axis axis) { axis.DataContext = context; if (!Owner.Axes.Contains(axis)) { Owner.Axes.Add(axis); } } private void GetAxes(Series series, ref Axis xAxis, ref Axis yAxis) { if (series is AnchoredCategorySeries) { xAxis = (series as AnchoredCategorySeries).XAxis; yAxis = (series as AnchoredCategorySeries).YAxis; } else if (series is RangeCategorySeries) { xAxis = (series as RangeCategorySeries).XAxis; yAxis = (series as RangeCategorySeries).YAxis; } else if (series is FinancialSeries) { xAxis = (series as FinancialSeries).XAxis; yAxis = (series as FinancialSeries).YAxis; } else if (series is ScatterBase) { xAxis = (series as ScatterBase).XAxis; yAxis = (series as ScatterBase).YAxis; } } private void RegCollection( IEnumerable collection) { if (collection is INotifyCollectionChanged) { (collection as INotifyCollectionChanged).CollectionChanged += SeriesBindingInfo_CollectionChanged; } } private void UnRegCollection( IEnumerable collection) { if (collection is INotifyCollectionChanged) { (collection as INotifyCollectionChanged).CollectionChanged -= SeriesBindingInfo_CollectionChanged; } } void SeriesBindingInfo_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e) { if (Owner == null) { return; } //this could be made more efficient of course. Clear(); UpdateFromCollection(ItemsSource); } } public class SeriesBinder : DependencyObject { public static DependencyProperty ContextBridgeProperty = DependencyProperty.RegisterAttached("ContextBridge", typeof(object), typeof(SeriesBinder), new PropertyMetadata(null, (o, e) => { SeriesBindingInfo sbi = GetBindingInfo(o); if (sbi != null) { sbi.OuterContext = e.NewValue; } })); public static DependencyProperty BindingInfoProperty = DependencyProperty.RegisterAttached("BindingInfo", typeof(SeriesBindingInfo), typeof(SeriesBinder), new PropertyMetadata(null, (o, e) => { if (o is XamDataChart) { SeriesBindingInfo prev = GetBindingInfo(o); if (prev != null && prev.Owner != null) { prev.Owner = null; } if (e.NewValue != null) { XamDataChart newOwner = o as XamDataChart; newOwner.SetBinding( SeriesBinder.ContextBridgeProperty, new Binding()); (e.NewValue as SeriesBindingInfo) .Owner = (XamDataChart)o; if (GetContextBridge(o) != null) { (e.NewValue as SeriesBindingInfo) .OuterContext = GetContextBridge(o); } } } })); public static void SetBindingInfo( DependencyObject target, SeriesBindingInfo info) { target.SetValue(BindingInfoProperty, info); } public static SeriesBindingInfo GetBindingInfo( DependencyObject target) { return (SeriesBindingInfo)target.GetValue(BindingInfoProperty); } public static void SetContextBridge( DependencyObject target, object context) { target.SetValue(ContextBridgeProperty, context); } public static object GetContextBridge( DependencyObject target) { return target.GetValue(ContextBridgeProperty); } }
The above example was assuming a situation where you wanted to share axes between all the series in the chartHowever, you could also use an approach like this to define seperate axes per item in your viewmodel collection, e.g:
<Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ig:XamDataChart Name="xamDataChart1" > <ig:XamDataChart.Axes> <ig:NumericYAxis x:Name="yAxis" /> </ig:XamDataChart.Axes> <local:SeriesBinder.BindingInfo> <local:SeriesBindingInfo ItemsSource="{Binding}"> <local:SeriesBindingInfo.SeriesTemplate> <DataTemplate> <ig:LineSeries ItemsSource="{Binding Item.Data}" ValueMemberPath="{Binding Item.ValueMemberPath}" YAxis="{Binding Path=Owner.Axes[0]}" > <ig:LineSeries.XAxis> <ig:CategoryXAxis ItemsSource="{Binding Item.Data}" Label="{}{Label}"/> </ig:LineSeries.XAxis> </ig:LineSeries> </DataTemplate> </local:SeriesBindingInfo.SeriesTemplate> </local:SeriesBindingInfo> </local:SeriesBinder.BindingInfo> </ig:XamDataChart> <Button Grid.Row="1" Content="Click" Click="Button_Click"/> </Grid>
Hope this helps! Let me know if you have questions.-Graham
Dear Graham,
how can i redo this but for stacked column series, its really urgent.
Thanks
For xamWebChart or for xamDataChart? Could you describe what your data looks like?
its xamDataChart,
this.grdXmlData.ItemsSource = markets; //this for the grid and its working great
CategoryXAxis xAxis = new CategoryXAxis(); xAxis.ItemsSource = markets; xAxis.Label = "{Date}"; NumericYAxis numYAxis = new NumericYAxis();
this.DataChart.Axes.Add(xAxis); this.DataChart.Axes.Add(numYAxis);
StackedColumnSeries stackedColumnSeries = new StackedColumnSeries(); stackedColumnSeries.XAxis = xAxis; stackedColumnSeries.YAxis = numYAxis; stackedColumnSeries.ItemsSource = markets;
for (int nbrand = 0; nbrand < brandcount; nbrand++) {
StackedFragmentSeries stackedFragment = new StackedFragmentSeries(); // creating the brands' graphs and assigning the values stackedFragment.Name = "g" + nbrand; stackedFragment.Title = cbrand; // stackedFragment.SetBinding(StackedFragmentSeries.ItemsSourceProperty, new Binding("Item.value" + nbrand)); //stackedFragment.ItemsSource = markets; stackedFragment.ValueMemberPath = "value" + nbrand; // stackedFragment.LegendItemTemplate = customLegendItemTemplate; StackPanel sp = new StackPanel(); TextBlock tb1 = new TextBlock(); TextBlock tb2 = new TextBlock(); sp.Orientation = System.Windows.Controls.Orientation.Vertical; sp.Children.Add(tb1); sp.Children.Add(tb2); tb1.SetBinding(TextBlock.TextProperty, new Binding("Series.Title")); tb2.SetBinding(TextBlock.TextProperty, new Binding("Item.value" + nbrand)); stackedFragment.ToolTip = sp; // add the brands' graphs to the chart stackedColumnSeries.Series.Add(stackedFragment);
}
this.DataChart.Series.Add(stackedColumnSeries);
//here is the class
Markets markets = new Markets();
public class Markets : ObservableCollection<Market> { public Markets() {
//data retrieval from xml
field2 = "value" + i;
Market.AddProperty(field2, typeof(double));
Market market1 = new Market();
market1.SetPropertyValue(field2, msval); market1.SetPropertyValue("Date", String.Format("{0:y}", curDate).ToString()); Add(market1);
public class Market : ICustomTypeProvider { // public String Date { get; set; } CustomTypeHelper<Market> helper = new CustomTypeHelper<Market>(); public static void AddProperty(String name) { CustomTypeHelper<Market>.AddProperty(name); } public static void AddProperty(String name, Type propertyType) { CustomTypeHelper<Market>.AddProperty(name, propertyType); } public static void AddProperty(String name, Type propertyType, List<Attribute> attributes) { CustomTypeHelper<Market>.AddProperty(name, propertyType, attributes); } public void SetPropertyValue(string propertyName, object value) { helper.SetPropertyValue(propertyName, value); } public object GetPropertyValue(string propertyName) { return helper.GetPropertyValue(propertyName); } public PropertyInfo[] GetProperties() { return helper.GetProperties(); } public Type GetCustomType() { return helper.GetCustomType(); } }
//
Hi,
We don't have direct support for that interface yet, as its Silverlight 5. The chart doesn't directly use the Silverlight binding system for fetching column values because its too slow for volume scenarios.
I'm uncertain where that interface hooks into Silverlight, not having read into it yet. If it hooks into the binding system, then it wont work for describing a type to the chart yet, but if it hooks in to the reflection subsystem at a lower level, then it has a chance of working.
I'll try to run an experiment, but I can tell you that it will work if you use nominal types instead of dynamic types such as you are currently. You can also generate nominal types on the fly if the columns you are dealing with are completely dynamic. I'll try to find some samples I've done of this before.
Where are you running into the overhead? Do you have a profile? You may be able to tweak that method a bit to avoid the issue. There should be a bit of overhead when the custom type is generated, but as long as most of the dynamic types look the same it should be hitting the dictionary for each subsequent type that needs to be created.
Also, xamGrid probably supports binding against dictionaries, I would assume, is there a reason you aren't using that approach?
I'm not too familar with the xamGrid's backlog. I'd recommend makign a feature request, or possibly emailing productmanager@infragistics.com to inquire further.
Hi Graham,
I am facing the same issue with xamGrid - Silverlight. From the description you gave in the post above, it make sense that ICustomTypeProvider works at silverlight binding level but xamGrid works at a level lower than that.
The workaround you suggested of creating runtime types works fine but causes performance overhead. I would ideally like to see ICustomTypeProvider or DynamicObjects binding seamlessly with Infragistics components.
Is there any plan to support this in upcoming Infragistics releases ? I have tried Infragistics 12.1 as well, but its still not implemented.
thx alot
Is there any article or blog that binds the pivot grid with the datachart??
am searching but there is only for webchart
I believe you can just set the Brush property on each fragment.
OK, i will now try to work with the pivotgrid and bind it :)
but i want to ask a question first,
can i chose the colors of the stacked fragments, because i don't want the randomely selected colors i get
i want to specify the color of the fragment when adding it, can i??