Intersoft ClientUI Documentation
How-to: Enable Data Drill Down In UXChart

This example shows how to enable data drill down in UXChart.

Example

Description

With drilldown feature provided by UXChart, you can easily produce charts that enables data drill down. The data plots in each chart will act as a hotspot to reveal the breakdown values of the selected data. Actually multiple levels of drilldown is supported by UXChart through it solid architecture. In this scenario, UXChart will display the sales data based on area at first, then followed by sales based on category in the selected area. Users can perform drill down by double clicking on an area of interest. In the 3rd level, the sales data will be drilled down again into sales based on product when a category is selected.

Code

The following code shows how to implement data drill down in UXChart. You will notice that you need to specify the child of the chart, which can be specified through binding.

XAML
Copy Code
<Grid>
    <Grid.DataContext>
        <ViewModels:SalesDrillDownViewModel/>
    </Grid.DataContext>
                                        
    <Intersoft:UXChart Name="Chart1" Title="Area" Child="{Binding ElementName=Chart2}" DrillDownCommand="{Binding DrillDownCommand}">
        <Intersoft:UXChart.Series>
            <Intersoft:PieSeries ItemsSource="{Binding AreaSales}" IndependentValueBinding="{Binding Area}" DependentValueBinding="{Binding Sales}" />
        </Intersoft:UXChart.Series>
    </Intersoft:UXChart>

    <Intersoft:UXChart Name="Chart2" Title="Category" Child="{Binding ElementName=Chart3}" Visibility="Collapsed" DrillUpCommand ="{Binding DrillUpCommand}" DrillDownCommand="{Binding DrillDownCommand}">
        <Intersoft:UXChart.Series>
            <Intersoft:ColumnSeries Title="{Binding ChartName}" ItemsSource="{Binding CategorySales}" IndependentValueBinding="{Binding Category}" DependentValueBinding="{Binding Sales}" />
        </Intersoft:UXChart.Series>
    </Intersoft:UXChart>

    <Intersoft:UXChart Name="Chart3" Title="Product" DrillUpCommand ="{Binding DrillUpCommand}" Visibility="Collapsed">
        <Intersoft:UXChart.Series>
            <Intersoft:AreaSeries Title="{Binding CategoryName}" ItemsSource="{Binding ProductSales}" IndependentValueBinding="{Binding Product}" DependentValueBinding="{Binding Sales}" />
        </Intersoft:UXChart.Series>
    </Intersoft:UXChart>
</Grid>                   
C#
Copy Code
public class SalesDrillDownViewModel : ViewModelBase
{
    #region Fields

    private ObservableCollection<SalesDrillDownData> _areaSales;
    private ObservableCollection<SalesDrillDownData> _categorySales;
    private ObservableCollection<SalesDrillDownData> _productSales;   
    private string _selectedArea;
    private string _selectedCategory;
        
    #endregion

    #region Properties

    public ObservableCollection<SalesDrillDownData> AreaSales { get; set; }
    public ObservableCollection<SalesDrillDownData> CategorySales { get; set; }
    public ObservableCollection<SalesDrillDownData> ProductSales { get; set; }

    public string SelectedArea
    {
        get { return _selectedArea; }
        set
        {
            if (_selectedArea != value)
            {
                _selectedArea = value;
                OnPropertyChanged("SelectedArea");
            }
        }
    }

    public string SelectedCategory
    {
        get { return _selectedCategory; }
        set
        {
            if (_selectedCategory != value)
            {
                _selectedCategory = value;
                OnPropertyChanged("SelectedCategory");
            }
        }
    }

    #endregion

    #region Commands

    public DelegateCommand DrillDownCommand { get; set; }
    public DelegateCommand DrillUpCommand { get; set; }
        
    #endregion
        
    #region Data Source
        
    public IEnumerable<SalesDrillDownData> DataSource
    {
        get { return DataManager.Instance.SalesDrillDownData; }
    }
        
    #endregion
        
    #region Constructor

    public SalesDrillDownViewModel()
    {                
        this.DrillDownCommand = new DelegateCommand(ExecuteDrillDown);
        this.DrillUpCommand = new DelegateCommand(ExecuteDrillUp);

        this.AreaSales = this.GetAreas();
    }

    #endregion

    #region Methods

    private void ExecuteDrillDown(object parameter)
    {
        UXChartDrillDownCommandArgs args = parameter as UXChartDrillDownCommandArgs;

        if (args.Level == 1)
        {
            SalesDrillDownData parent = args.DataPointContext as SalesDrillDownData;
            if (parent != null)
            {                               
                this.SelectedArea = parent.Area;
                this.CategoriesSales = this.GetCategories();

            }
        }
        else if (args.Level == 2)
        {
            SalesDrillDownData parent = args.DataPointContext as SalesDrillDownData;
            if (parent != null)
            {
                this.SelectedCategory = parent.Category;
                this.ProductSales = this.GetProducts();
            }
        }
    }
        
    private void ExecuteDrillUp(object parameter)
    {
        UXChartDrillDownCommandArgs args = parameter as UXChartDrillDownCommandArgs;

        if (args.Level == 2)
            this.CategorySales.Clear();
        else if (args.Level == 3)
            this.ProductSales.Clear();
    }
        
    private ObservableCollection<SalesDrillDownData> GetAreas()
    {
        ObservableCollection<SalesDrillDownData> areas = new ObservableCollection<SalesDrillDownData>();
                
        foreach (IGrouping<string, SalesDrillDownData> groupData in this.DataSource.GroupBy(p => p.Area))
        {
            List<SalesDrillDownData> data = groupData.ToList();
            if (data != null && data.Count > 0)
            {
                SalesDrillDownData firstData = data.FirstOrDefault();
                if (firstData != null)
                    areas.Add(new SalesDrillDownData() { Area = firstData.Area, Sales = data.Sum(p => p.Sales) });
            }
        }
                
        return areas;
    }
        
    private ObservableCollection<SalesDrillDownData> GetCategories(string area)
    {
        ObservableCollection<SalesDrillDownData> categories = new ObservableCollection<SalesDrillDownData>();

        foreach (IGrouping<string, SalesDrillDownData> groupData in this.DataSource.Where(p => p.Area == area).GroupBy(p => p.Category))
        {
            List<SalesDrillDownData> data = groupData.ToList();
            if (data != null && data.Count > 0)
            {
                SalesDrillDownData firstData = data.FirstOrDefault();
                if (firstData != null)
                    categories.Add(new SalesDrillDownData() { Area = firstData.Area, Category = firstData.Category, Sales = data.Sum(p => p.Sales) });
            }
        }
                
        return categories;
    }

    private ObservableCollection<SalesDrillDownData> GetProducts(string area, string category)
    {
        ObservableCollection<SalesDrillDownData> products = new ObservableCollection<SalesDrillDownData>();

        foreach (IGrouping<string, SalesDrillDownData> groupData in this.DataSource.Where(o => o.Area == area && o.Category == category).GroupBy(o => o.Product))
        {
            List<SalesDrillDownData> data = groupData.ToList();
            if (data != null && data.Count > 0)
            {
                SalesDrillDownData firstData = data.FirstOrDefault();
                if (firstData != null)
                    products.Add(new SalesDrillDownData() { Area = firstData.Area, Category = firstData.Category, Product = firstData.Product, Sales = data.Sum(p => p.Sales) });
            }
        }
                
        return products;
    }
        
    #endregion
}                       

Results

After implementing these code, the results will look like the following image.

 

When an area is selected, sales data based on category will be shown, as follows.

 

When a category is selected, sales data based on product will be shown, as follows.

See Also

Concepts