My team and I are developing a WPF application that displays several concurrent XamDataChart controls (by Infragistics). Each chart is bound to a different ObservableCollection that can contain up to 2 million points. For each chart, a DispatcherTimer periodically retrieves new items (up to 1000 every 100 ms) that are to be appended to the collection. Every time new items come, they are added to the "tail" of a collection and the same amount is removed from the "head", so that the amount of items in the collection remains constant across time.
The problem we are facing is that the add/remove operations are freezing the GUI, because the collection can only be modified by the main thread. We have tried many approaches (BackgroundWorker, Application.Current.Dispatcher with DispatcherPriority.Background, Task.Factory, etc.) but none of them seems to solve the problem and the GUI keeps freezing.
Could you please advise us on the best approach for handing large amount of bound data while keeping the GUI responsive?
UPDATEs:
1) As indicated in my comment below, we have already tried to add and remove items while suppressing the OnCollectionChanged. Even if it seems to have effect with a small amount of data, in our scenario the advantages of this solution are really not observable.
2) Data are prepared and collected in a separate thread. This is a long-running operation, yet no slowness or unresponsiveness is evident. The application freezes when data are passed on to the chart component for rendering.
3) Here are the methods that generate data (in a separate thread) and display data on the UI:
private void GenerateDataButtonClick(object sender, RoutedEventArgs e)
{
Task<List<RealTimeDataPoint>> task = Task.Factory.StartNew(() => this.RealTimeDataPointGenerator.GenerateData(2000000));
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { this.DataPoints.Clear();
this.DataPoints.AddRange(task.Result);
if (!this.StartDataFeedButton.IsEnabled)
this.StartDataFeedButton.IsEnabled = true;
}));
}
public void DispatcherTimerTick(object sender, EventArgs e)
{
Task<List<RealTimeDataPoint>> task = Task.Factory.StartNew(() => this.RealTimeDataPointGenerator.GenerateData(1000));
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { this.DataPoints.RemoveRange(0, task.Result.Count); this.DataPoints.AddRange(task.Result); }));
}
Thanks in advance, Gianluca