New to Telerik UI for WinForms? Download free 30-day trial

Drill down

The drill down functionality of a chart allows users to click on a graphical element (bar, pie segment etc.), representing some data, in order to navigate to another view which contains different data than the first one. The new view usually contains finer grained data like displaying information from yearly to quarterly data, from quarterly to monthly, etc., or in the case of a retail store from categories to brands, from brands to items, etc. The drill-down functionality basically makes your data points act like a hotspots for “drilling-down” or “zooming” into your data.

The animation below demonstrates how from BarSeries showing sales for 10 years as bars, when a bar is clicked you are navigated to another view showing BarSeries again, but this time displaying the sales for all months in the selected year. Clicking a month bar will produce another view, which displays line series with data for each day of the month.

Figure 1: Drill Down Functionality

WinForms RadChartView Drill Down Functionality

To support this functionality a DrillDownControler should be used:

Add Controller

DrillDownController drillControler = new DrillDownController();
this.radChartView1.Controllers.Add(drillControler);

Then, you will need to add as many ChartViews as you need. Each ChartView represents different level of the drill operation.

Add Views

radChartView1.Views.AddNew("Revenue by Month");
radChartView1.Views.AddNew("Revenue by Day");

In order to show the added ChartViews you should set the ShowDrillNavigation property to true.

To handle the different levels, the Drill event should be used. Depending on the Level provided in the event arguments, you can decide how to setup the View. In the example below, different data is represented for Years, Months and Days:

Drill Event

int year, month;
void radChartView1_Drill(object sender, DrillEventArgs e)
{
    CartesianSeries series = new BarSeries();
    series.ValueMember = "Value";
    series.CategoryMember = "Date";
    DateTimeCategoricalAxis horizontalAxis = new DateTimeCategoricalAxis();
    LinearAxis verticalAxis = new LinearAxis();
    verticalAxis.AxisType = AxisType.Second;
    verticalAxis.Title = "USD";
    switch (e.Level)
    {
        case 0:
            series.DataSource = LoadDataByYears();
            horizontalAxis.LabelFormat = "{0:yyyy}";
            horizontalAxis.Title = "Year";
            break;
        case 1:
            if (e.SelectedPoint != null)
                year = ((DrillDownDataInfo)e.SelectedPoint.DataItem).Date.Year;
            horizontalAxis.LabelFormat = "{0:MM}";
            horizontalAxis.Title = "Month";
            e.View.Palette = KnownPalette.Metro;
            series.DataSource = ParseDataByMonth(year);
            break;
        case 2:
            if (e.SelectedPoint != null)
                month = ((DrillDownDataInfo)e.SelectedPoint.DataItem).Date.Month;
            series = new LineSeries();
            series.ValueMember = "Value";
            series.CategoryMember = "Date";
            series.DataSource = ParseDataByDay(year, month);
            series.ShowLabels = true;
            horizontalAxis.LabelFormat = "{0:dd}";
            horizontalAxis.Title = "Day";
            break;
    }
    e.View.Series.Clear();
    e.View.Axes.Clear();
    series.HorizontalAxis = horizontalAxis;
    series.VerticalAxis = verticalAxis;
    e.View.Series.Add(series);

    series.DrawLinesToLabels = true;
    series.BorderColor = series.BackColor = Color.FromArgb(142, 196, 65);
}

If your chart is being oriented horizontally, please make sure that in the Drill event you are setting the correct axes as First and Second. In the example above, for a horizontally oriented view, the horizontal axis should be set as Second and the vertical axis should be set as First.

The navigation element’s text is taken from TitleElement.Text property by default, so every time you drill, you have to change this text accordingly. If this text is empty, it will be taken from the RadChartView.View.ViewName property.

To make the example complete you should make few more steps:

1. First you should create DrillDownDataInfo class which will contain two properties Value and Date and will implement the INotifyPropertyChanged interface:

Data Object

public class DrillDownDataInfo : INotifyPropertyChanged
{
    double value;
    DateTime date;
    public DrillDownDataInfo(DateTime date, double value)
    {
        this.date = date;
        this.value = value;
    }
    public double Value
    {
        get
        {
            return this.value;
        }
        set
        {
            this.value = value;
            this.OnPropertyChanged("Value");
        }
    }
    public DateTime Date
    {
        get
        {
            return this.date;
        }
        set
        {
            this.date = value;
            this.OnPropertyChanged("Date");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

2. Now you can use this class to create three binding lists. Each one will contain data for the chart view. These methods are used in the previously described Drill event handler.

Load Data

private BindingList<DrillDownDataInfo> LoadDataByYears()
{
    Stream stream = System.Reflection.Assembly.GetAssembly(this.GetType()).GetManifestResourceStream("SamplesCS.ChartView.Features.DJIA.csv");
    BindingList<DrillDownDataInfo> chartData = new BindingList<DrillDownDataInfo>();
    using (StreamReader streamReader = new StreamReader(stream))
    {
        int yearsCount = 1;
        while (streamReader.Peek() != -1)
        {
            string[] data = streamReader.ReadLine().Split(',');
            DrillDownDataInfo dataItem = new DrillDownDataInfo(
                DateTime.Parse(data[0], CultureInfo.InvariantCulture),
                double.Parse(data[1], CultureInfo.InvariantCulture)
                );
            chartData.Add(dataItem);
            if (yearsCount++ > 10)
            {
                break;
            }
        }
    }
    return chartData;
}
internal BindingList<DrillDownDataInfo> ParseDataByMonth(int year)
{
    Stream stream = System.Reflection.Assembly.GetAssembly(this.GetType()).GetManifestResourceStream("SamplesCS.ChartView.Features.DJIAM.csv");
    BindingList<DrillDownDataInfo> chartData = new BindingList<DrillDownDataInfo>();
    using (StreamReader streamReader = new StreamReader(stream))
    {
        while (streamReader.Peek() != -1)
        {
            string line = streamReader.ReadLine();
            if (string.IsNullOrEmpty(line))
                continue;
            string[] data = line.Split(',');
            DateTime date = DateTime.Parse(data[0], CultureInfo.InvariantCulture);
            if (date.Year == year)
            {
                DrillDownDataInfo dataItem = new DrillDownDataInfo(
                    date,
                    double.Parse(data[1], CultureInfo.InvariantCulture)
                    );
                chartData.Add(dataItem);
            }
        }
    }
    return chartData;
}
internal BindingList<DrillDownDataInfo> ParseDataByDay(int year, int month)
{
    Stream stream = System.Reflection.Assembly.GetAssembly(this.GetType()).GetManifestResourceStream("SamplesCS.ChartView.Features.DJIAD.csv");
    BindingList<DrillDownDataInfo> chartData = new BindingList<DrillDownDataInfo>();
    using (StreamReader streamReader = new StreamReader(stream))
    {
        while (streamReader.Peek() != -1)
        {
            string line = streamReader.ReadLine();
            if (string.IsNullOrEmpty(line))
            {
                continue;
            }
            string[] data = line.Split(',');
            DateTime date = DateTime.Parse(data[0], CultureInfo.InvariantCulture);
            if (date.Year == year && date.Month == month && !string.IsNullOrEmpty(data[1]))
            {
                DrillDownDataInfo dataItem = new DrillDownDataInfo(
                    date,
                    double.Parse(data[1], CultureInfo.InvariantCulture)
                    );
                chartData.Add(dataItem);
            }
        }
        return chartData;
    }
}

Note that the data is loaded from external files. These files contain dates and values which are parsed and stored in out DrillDownDataInfo class objects. The files are included in Telerik UI for WinForms suite (navigate to Telerik\UI for WinForms Q3 2013\Examples\QuickStart\Resources ).

3. Finally you should initialize the chart by adding a series to it. Also the corresponding axes should be added.

Add Series

BarSeries barSeries = new BarSeries();
barSeries.ValueMember = "Value";
barSeries.CategoryMember = "Date";
barSeries.DataSource = LoadDataByYears();
this.radChartView1.View.Palette = KnownPalette.Metro;
DateTimeCategoricalAxis horizontalAxis = new DateTimeCategoricalAxis();
horizontalAxis.LabelFormat = "{0:yyyy}";
horizontalAxis.Title = "Year";
barSeries.HorizontalAxis = horizontalAxis;
LinearAxis verticalAxis = new LinearAxis();
verticalAxis.AxisType = AxisType.Second;
verticalAxis.Title = "USD";
barSeries.VerticalAxis = verticalAxis;
radChartView1.Series.Add(barSeries);

Now you can examine how this functionality works by clicking a data point in the chart. You can use the additional buttons to drill up or drill to top.