Hello all,
we are having a big problem with radchart performance. I'll explain an example to illustrate the situation.
In one part of our app, a user control that contains 5 Radcharts is loaded. Each chart contains two series for a total of 10, and each series has exactly 2880 points in our database. Internally, each chart is set to use a sampling threshold of 200, X axis range is set to DateTime and AutoRange, and zoom settings are set to zoom and scroll. As these are multi-series charts, the schema we used is that found in Telerik's documentation (binding to nested collections), associating each series mapping to one collection index.
As the user refreshes, one WCF call is asynchronously made to retrieve each series data (thus, 10 calls in total). The call returns really fast, and in around 2 seconds all the lists of points that feed the series are loaded in memory.
The problem arises when updating the charts's ItemsSource property. It takes a lot of time since the ItemsSource of each chart is updated until all the charts are shown in the screen properly (around 15-20 seconds!!). This is totally a mess for us, as our customer needs to change the date queries dinamycally and perform refresh operations frequently. Waiting for 20-25 seconds every time a refresh is made is not acceptable for him.
Showing point marks, labels and so on is disabled, as well as animations. Please, could you provide any help for this?
I post the chart configuration code here:
And here, the function used to process the result of the asynchronous WCF call. As you can see, we make a local copy of the list returned by the WCF method inserting it in an ObservableCollection and then setting the ObservableCollection as an item of m_lstChartSource, which is a list and the object set as Chart.ItemsSource target. "ProcessChartSerie" makes additional processing regarding the serie (color, item mappings and so on):
we are having a big problem with radchart performance. I'll explain an example to illustrate the situation.
In one part of our app, a user control that contains 5 Radcharts is loaded. Each chart contains two series for a total of 10, and each series has exactly 2880 points in our database. Internally, each chart is set to use a sampling threshold of 200, X axis range is set to DateTime and AutoRange, and zoom settings are set to zoom and scroll. As these are multi-series charts, the schema we used is that found in Telerik's documentation (binding to nested collections), associating each series mapping to one collection index.
As the user refreshes, one WCF call is asynchronously made to retrieve each series data (thus, 10 calls in total). The call returns really fast, and in around 2 seconds all the lists of points that feed the series are loaded in memory.
The problem arises when updating the charts's ItemsSource property. It takes a lot of time since the ItemsSource of each chart is updated until all the charts are shown in the screen properly (around 15-20 seconds!!). This is totally a mess for us, as our customer needs to change the date queries dinamycally and perform refresh operations frequently. Waiting for 20-25 seconds every time a refresh is made is not acceptable for him.
Showing point marks, labels and so on is disabled, as well as animations. Please, could you provide any help for this?
I post the chart configuration code here:
// Local variables bool bResult = true; TimeSpan DateDifference = new TimeSpan(); try { // Sampling threshold this.Chart.SamplingSettings.SamplingThreshold = 200; this.Chart.SamplingSettings.SamplingFunction = ChartSamplingFunction.Min; // Settings for X axis --> manual setting for min, max, step, label step this.Chart.DefaultView.ChartArea.AxisX.IsDateTime = true; // Note: set to true !!!! this.Chart.DefaultView.ChartArea.AxisX.AutoRange = true; this.Chart.DefaultView.ChartArea.AxisX.TicksDistance = 15; // Set the label format depending on the selected timespan DateDifference = this.m_dtDateTo.Subtract(this.m_dtDateFrom); TimeSpan FifteenMinutes = new TimeSpan(0, 15, 0); TimeSpan OneDay = new TimeSpan(1, 0, 0, 0); TimeSpan OneMonth = new TimeSpan(31, 0, 0, 0); TimeSpan OneYear = new TimeSpan(365, 0, 0, 0); // Less than 15 minutes if (DateDifference.TotalMilliseconds < FifteenMinutes.TotalMilliseconds) { // Hours, minutes and seconds this.Chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "HH:mm:ss"; } // Between 15 minutes and 1 day else if ((DateDifference.TotalMilliseconds > FifteenMinutes.TotalMilliseconds) && (DateDifference.TotalMilliseconds < OneDay.TotalMilliseconds)) { // Hours and minutes this.Chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "HH:mm"; } // Between 1 day and 31 days else if ((DateDifference.Days >= OneDay.Days) && (DateDifference.Days <= OneMonth.Days)) { // Show the day this.Chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "M-dd"; // No auto range... the chart does not handle this properly in some cases this.Chart.DefaultView.ChartArea.AxisX.AutoRange = false; this.Chart.DefaultView.ChartArea.AxisX.MinValue = this.m_dtDateFrom.ToOADate(); this.Chart.DefaultView.ChartArea.AxisX.MaxValue = new DateTime(this.m_dtDateTo.Year, this.m_dtDateTo.Month, this.m_dtDateTo.Day, 0, 0, 0).ToOADate(); DateTime aux1 = new DateTime(2000, 1, 1, 0, 0, 0); DateTime aux2 = new DateTime(2000, 1, 2, 0, 0, 0); double Step = aux2.ToOADate() - aux1.ToOADate(); this.Chart.DefaultView.ChartArea.AxisX.Step = Step; this.Chart.DefaultView.ChartArea.AxisX.LabelStep = 1; } else if ((DateDifference.TotalMilliseconds > OneMonth.TotalMilliseconds) && (DateDifference.TotalMilliseconds <= OneYear.TotalMilliseconds)) { // Month and year this.Chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "yyyy-M"; } else if (DateDifference.TotalMilliseconds > OneYear.TotalMilliseconds) { // Year value this.Chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "yyyy"; } // Label orientation this.Chart.DefaultView.ChartArea.AxisX.LabelRotationAngle = 45; this.Chart.DefaultView.ChartArea.AxisX.LayoutMode = AxisLayoutMode.Auto; // Disable animations this.Chart.DefaultView.ChartArea.EnableAnimations = false; // X Axis label style this.Chart.DefaultView.ChartArea.AxisX.AxisStyles.ItemLabelStyle = this.Resources["ItemLabelStyle"] as Style; // Configuring the Y axis this.Chart.DefaultView.ChartArea.AdditionalYAxes.Clear(); // Get the lower Y axis index this.m_iLowerAxisIndex = (from axis in this.m_obcAxisY select axis.ID).Min(); foreach (DLL_CustomControls.ReportChartSettings.AxisY obConfiguredAxisY in this.m_obcAxisY) { if (obConfiguredAxisY.ID == this.m_iLowerAxisIndex) { // Configuration for the default axis this.Chart.DefaultView.ChartArea.AxisY.MinValue = obConfiguredAxisY.MinValue; this.Chart.DefaultView.ChartArea.AxisY.MaxValue = obConfiguredAxisY.MaxValue; this.Chart.DefaultView.ChartArea.AxisY.AutoRange = obConfiguredAxisY.AutoRange; this.Chart.DefaultView.ChartArea.AxisY.Title = obConfiguredAxisY.Label; continue; } // Create and add new axis Telerik.Windows.Controls.Charting.AxisY NewAxis = new Telerik.Windows.Controls.Charting.AxisY(); NewAxis.MinValue = obConfiguredAxisY.MinValue; NewAxis.MaxValue = obConfiguredAxisY.MaxValue; NewAxis.AutoRange = obConfiguredAxisY.AutoRange; NewAxis.AxisName = obConfiguredAxisY.ID.ToString(); NewAxis.Title = obConfiguredAxisY.Label; // Add the secondary axis this.Chart.DefaultView.ChartArea.AdditionalYAxes.Add(NewAxis); } } catch (Exception ex) { bResult = false; ExceptionManager.Write("DLL_CustomControls", "ReportChart", "ConfigureChart", "Exception: " + ex.Message); } return bResult;And here, the function used to process the result of the asynchronous WCF call. As you can see, we make a local copy of the list returned by the WCF method inserting it in an ObservableCollection and then setting the ObservableCollection as an item of m_lstChartSource, which is a list and the object set as Chart.ItemsSource target. "ProcessChartSerie" makes additional processing regarding the serie (color, item mappings and so on):
// Capture the data if (!e.Result) { // Debug, warn and return DebugManager.Write(DebugManager.DebugStatus.ERROR, "DLL_CustomControls", "ReportChart", "OnGetDataCompleted", "Error in the web service function for Serie ID = " + e.outSerieID.ToString() + ": " + e.strError); MessageBox.Show("Error getting data for serie " + e.outSerieID.ToString() + ". Please try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation); return; } // Capture the serie that has been processed DLL_CustomControls.ReportChartSettings.ChartSerie obSerieToProcess = null; obSerieToProcess = (from series in this.m_obcSeries where series.ID == e.outSerieID select series).First(); // Process the serie data if (!ProcessSerieData(obSerieToProcess)) { DebugManager.Write(DebugManager.DebugStatus.ERROR, "DLL_CustomControls", "ReportChart", "OnGetDataCompleted", "Error processing serie number " + e.outSerieID.ToString()); } else { // If the list has points, process if (e.lstChartPoints != null) { // Add to list of datasources of the chart ObservableCollection<DLL_Resources.WCF_Clients.ChartValueDateDouble> obcPoints = new ObservableCollection<DLL_Resources.WCF_Clients.ChartValueDateDouble>(); // Convert in observable collection foreach (DLL_Resources.WCF_Clients.ChartValueDateDouble obPoint in e.lstChartPoints) { DLL_Resources.WCF_Clients.ChartValueDateDouble obNewPoint = new DLL_Resources.WCF_Clients.ChartValueDateDouble(); obNewPoint.XValueDateTime = obPoint.XValueDateTime; obNewPoint.XValueDouble = obPoint.XValueDouble; obNewPoint.YValue = obPoint.YValue; // Add point obcPoints.Add(obNewPoint); } this.m_lstChartSource.Add(obcPoints); } }