Hi,
I am having a chart in my UI. I am adding a series to the chart and then assign the DataSource to the series. Chart takes lots of time to render the chart.
int[] zi = new int[356]; Infragistics.Silverlight.Chart.Series s3 = new Infragistics.Silverlight.Chart.Series(); s3.ChartType = ChartType.Line; for (int j = 0; j < 356; j++) { zi[j] = j; } igChart3.Series.Add(s3); igChart3.Series[0].DataSource = zi;
Alternatively, if I assign data to the series and then add the series to chart, it works perfectly(much quicker).
int[] zi = new int[356]; Infragistics.Silverlight.Chart.Series s3 = new Infragistics.Silverlight.Chart.Series(); s3.ChartType = ChartType.Line; for (int j = 0; j < 356; j++) { zi[j] = j; } s3.DataSource = zi; igChart1.Series.Add(s3);
Why is this performance difference?
Due to this limitation we are not able to do data binding to the series from XAML code.
Regards,
Madhan
Could you describe what happens each time you populate the data source? Some pseudocode, or better a sample? Its possible you are leaking event listeners, or the chart may be, as this version does not use a weak event listener pattern. There are definitely some usage scenarios to avoid with this style of control due to the way they need to listen for data updates. But if you can provide some details on your usage we can track down what is occurring.
Graham,
I am able to see minor performance improvement with the above approach(tried with the same sample code as above). Need to use this approach in my evaluation application and get back to you.
I am facing another issue in evaluation application as well as in sample code above related to memory.
If you keep on clicking populate button for 40 to 50 times over a period of time. it can be in 30 mins or with in possible quickest time. Memory of the IE will keep on increasing. It will reach 1500 MB and finally IE will crash. We are facing the same scenario in our evaluation application and also in the actual application prototype.
Can you check on this behavior?
Thanks for getting back to me and being so up front about the issue, which is always a pleasure to experience. Yes we are binding to objects, other than binding speed is making the chart visible, as they are on tabs there is a pause of a few seconds.
I've reported the perfomance issue as bug 30397. You can ask here or Developer Support for the status of this bug at any time. As a workaround you can set the datapoints directly for the series when dealing with these kinds of volumes. Here's a sample that demonstrates one way that you could set the points directly while still using the same kind of binding.
The Xaml:
<UserControl.Resources> <local:DataPointsProxy x:Key="theProxy" LabelPropertyName="Label" ValuePropertyName="Value" /> </UserControl.Resources> <Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <igChart:XamWebChart x:Name="chart1" Grid.Row="0" Grid.Column="0" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart2" Grid.Row="0" Grid.Column="1" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart3" Grid.Row="0" Grid.Column="2" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart4" Grid.Row="0" Grid.Column="3" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart5" Grid.Row="0" Grid.Column="4" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart6" Grid.Row="1" Grid.Column="0" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart7" Grid.Row="1" Grid.Column="1" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart8" Grid.Row="1" Grid.Column="2" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart9" Grid.Row="1" Grid.Column="3" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <igChart:XamWebChart x:Name="chart10" Grid.Row="1" Grid.Column="4" RefreshEnabled="{Binding RefreshEnabled, Source={StaticResource theProxy}}"> <igChart:XamWebChart.Series> <igChart:Series ChartType="Line" DataPoints="{Binding DataPoints, Source={StaticResource theProxy}}" /> </igChart:XamWebChart.Series> </igChart:XamWebChart> <Button Grid.Row="2" HorizontalAlignment="Left" Content="Populate" x:Name="populate" Click="populate_Click"/> </Grid>
And the code behind:
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); proxy = this.Resources["theProxy"] as DataPointsProxy; } private DataPointsProxy proxy; private Random rand = new Random(); public ObservableCollection<TestDataItem> GetTestData() { ObservableCollection<TestDataItem> ret = new ObservableCollection<TestDataItem>(); for (int i = 0; i < 100; i++) { ret.Add(new TestDataItem() { Label = "Label " + i.ToString(), Value = rand.NextDouble() * 100.0 }); } return ret; } private void populate_Click( object sender, RoutedEventArgs e) { proxy.RefreshEnabled = false; proxy.DataSource = GetTestData(); proxy.RefreshEnabled = true; } } public class TestDataItem { public string Label { get; set; } public double Value { get; set; } } public class DataPointsProxy : FrameworkElement { public DataPointsProxy() { DataPoints = new ObservableCollection<DataPoint>(); } public static readonly DependencyProperty RefreshEnabledProperty = DependencyProperty.Register("RefreshEnabled", typeof(bool), typeof(DataPointsProxy), new PropertyMetadata(false)); public bool RefreshEnabled { get { return (bool)this.GetValue( DataPointsProxy.RefreshEnabledProperty); } set { this.SetValue( DataPointsProxy.RefreshEnabledProperty, value); } } public static readonly DependencyProperty DataPointsProperty = DependencyProperty.Register("DataPoints", typeof(ObservableCollection<DataPoint>), typeof(DataPointsProxy), new PropertyMetadata(null)); public ObservableCollection<DataPoint> DataPoints { get { return (ObservableCollection<DataPoint>) this.GetValue( DataPointsProxy.DataPointsProperty); } set { this.SetValue( DataPointsProxy.DataPointsProperty, value); } } public string LabelPropertyName { get; set; } public string ValuePropertyName { get; set; } public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource", typeof(IEnumerable), typeof(DataPointsProxy), new PropertyMetadata(null, (o, e) => { (o as DataPointsProxy).OnDataSourceChanged(e); })); private void UpdateDataPoints() { DataPoints.Clear(); foreach (object item in DataSource) { DataPoint point = new DataPoint(); point.Label = (string)item.GetType() .GetProperty(LabelPropertyName) .GetValue(item, null); point.Value = (double)item.GetType() .GetProperty(ValuePropertyName) .GetValue(item, null); DataPoints.Add(point); } } private void OnDataSourceCollectionChanged( object sender, NotifyCollectionChangedEventArgs e) { UpdateDataPoints(); } private void OnDataSourceChanged( DependencyPropertyChangedEventArgs e) { if (e.OldValue != null) { INotifyCollectionChanged coll = (e.OldValue as INotifyCollectionChanged); if (coll != null) { coll.CollectionChanged -= OnDataSourceCollectionChanged; } } if (e.NewValue != null) { INotifyCollectionChanged coll = (e.NewValue as INotifyCollectionChanged); if (coll != null) { coll.CollectionChanged += OnDataSourceCollectionChanged; } } UpdateDataPoints(); } public IEnumerable DataSource { get { return (IEnumerable)this.GetValue( DataPointsProxy.DataSourceProperty); } set { this.SetValue( DataPointsProxy.DataSourceProperty, value); } } }
Note: you would want to extend this proxy class if you wanted to make efficient updates to the DataSource collectionor detect changes to the objects therein.Let me know if you have any questions.-Graham
I've tried to replicate the environment and volume you describe and I have discovered some problems where the chart is refreshing things that it doesn't need to during the binding, which are causing problems when you throw enough volume at them.
In the meantime, if you bind datapoints to the DataPoints collection directly rather than using the DataSource and DataMapping properties, you can avoid these performance problems. I'll attach a sample demonstrating this shortly. Remember to still use the RefreshEnabled property as described above.
I'll also get back to you with a bug number for the performance problem.
-Graham