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);
}
}