This is a migrated thread and some comments may be shown as answers.

Bug when using long series and sampling

3 Answers 80 Views
Chart
This is a migrated thread and some comments may be shown as answers.
Andre
Top achievements
Rank 1
Andre asked on 25 Apr 2011, 09:20 PM
Hi Telerikers,

Just found a bug when using long series (I don't know exactly the number, but +1000 is enough) and sampling.
The scenario is:

-> AxisX as DateTime 
-> AxisX using XCategory
-> Sampling enabled
-> +1000 datapoints

Result: the categories in AxisX disappear and only the first one is visible showing 30/12/1899 00:00 (first OADate value). 

Any workarounds?

Cheers,

André

3 Answers, 1 is accepted

Sort by
0
Andre
Top achievements
Rank 1
answered on 26 Apr 2011, 01:37 PM
Ops, it's the same problem as here http://www.telerik.com/community/forums/silverlight/chart/autorange-doesn-t-works-with-datetime-values-in-axisx-as-xcategory.aspx

Sorry for the duplicate.

André

UPDATE: It's not really a duplicate since I'm not using XValue and XCategory simultaneously.
0
Andre
Top achievements
Rank 1
answered on 26 Apr 2011, 06:26 PM
Problem solved!

I created a new dependency property for RadChart where you can apply any kind of filter to the DataSeries after all calculations.

How it works:

Create a simple interface called IDataSeriesFilter

        public interface IDataSeriesFilter {
		void ApplyFilter(DataSeries dataSeries);
	}

Then a specialized collection so we can use it in xaml:

public class DataSeriesFilterCollection : List<IDataSeriesFilter> {}

Extend the RadChart class and add the following code:

protected override void OnDataBound(ChartDataBoundEventArgs e) {
			base.OnDataBound(e);
			ApplyFilters();
		}
 
		protected void ApplyFilters() {
			List<ChartArea> chartAreas = new List<ChartArea>();
			if(UseDefaultLayout) {
				chartAreas.Add(DefaultView.ChartArea);
			}
			else {
				chartAreas.AddRange(GetChartAreas());
			}
			foreach (IDataSeriesFilter filter in DataSeriesFilters) {
				foreach (ChartArea chartArea in chartAreas) {
					foreach (DataSeries serie in chartArea.DataSeries) {
						filter.ApplyFilter(serie);
					}	
				}
			}
		}
 
		protected List<ChartArea> GetChartAreas() {
			return Descendents(this).OfType<ChartArea>().ToList();
		}
 
		public static IEnumerable<DependencyObject> Descendents(DependencyObject root) {
			int count = VisualTreeHelper.GetChildrenCount(root);
			for (int i = 0; i < count; i++) {
				var child = VisualTreeHelper.GetChild(root, i);
				yield return child;
				foreach (var descendent in Descendents(child))
					yield return descendent;
			}
		}

I would love to use the class LogicalTreeExtensions to find all ChartAreas, but Telerik did us the favor to make it internal. Reasons for making an utility class internal are beyond the scope of this post. Also, it would be really good to have a protected method called FindChartAreas that returns all ChartAreas regardless using DefaultLayout or not instead of repeating several times the code that uses LogicalTreeExtensions.

Ok, now it's very very easy to add modifications to any DataSeries before it gets drawn. Let's show how to create a SamplingFirst filter so we can solve my original problem:

public class SamplingFirst : IDataSeriesFilter {
 
		public int SeriesMaxNumberOfDataPoints { getset; }
 
		public void ApplyFilter(DataSeries dataSeries) {
			if (SeriesMaxNumberOfDataPoints <= 0) {
				return;
			}
			int grouping = dataSeries.Count / SeriesMaxNumberOfDataPoints * 2;
			if (grouping < 2return;
			List<DataPoint> itemsToRemove = new List<DataPoint>();
			for (int i = 0; i < dataSeries.Count; i++) {
				if (i % grouping != 0) {
					itemsToRemove.Add(dataSeries[i]);
				}
			}
			dataSeries.SuspendNotifications();
			for (int i = 0; i < itemsToRemove.Count; i++) {
				dataSeries.Remove(itemsToRemove[i]);
			}
			dataSeries.ResumeNotifications();
		}
	}

And here the xaml part:

       <TelerikChart:RadChart ItemsSource="{Binding Series}">
 
            <Controls1:RadChart.SamplingSettings>
                <telerik:SamplingSettings SamplingThreshold="0" />
            </Controls1:RadChart.SamplingSettings>
 
            <TelerikChart:RadChart.DataSeriesFilters>
                <Filters:SamplingFirst SeriesMaxNumberOfDataPoints="400" />
            </TelerikChart:RadChart.DataSeriesFilters>
        </TelerikChart:RadChart>

Don't forget to disable SamplingSettings when using your custom one.

Note: this sampling filter is very simple and it doesn't care which category is removed. It also doesn't generate always 400 datapoints per serie, see the code for info and change it to fit your needs.

So what we get?
- It works with XCategories and any number of items (also with zooming)
- It's very very easy to add new filters, just implement the interface (TDD friendly)
- You can apply your own logic to category grouping or even change them when it makes sense

Be careful changing the DataSeries object, you can really mess up your chart. A better approach would be passing an interface contract as the ApplyFilter parameter with only the things you could change in a DataSeries, but I can't change the DataSeries class and I'm too lazy to create a decorator.

Cheers,

André Carlucci
 

0
Tobias Rodewi
Top achievements
Rank 1
answered on 10 May 2011, 06:01 PM
Thanks for the solution! Works great..
Tags
Chart
Asked by
Andre
Top achievements
Rank 1
Answers by
Andre
Top achievements
Rank 1
Tobias Rodewi
Top achievements
Rank 1
Share this question
or