wpf-400717-controls-and-libraries-charts-suite-chart-control-provide-data-drill-down.md
The Chart control provides the Drill Mode in which the control visualizes data hierarchies and allows end users to navigate between detail levels:
The Chart control requires object aggregation and composition to be able to use these relationships to navigate between detail levels. For example, this topic uses the following object hierarchy to explain how the drill mode works:
public interface INameable {
string Name { get; }
}
public sealed class DevAVBranch: INameable {
public string Name { get; set; }
public List<DevAVProductCategory> Categories { get; set; }
public double TotalIncome { get => Categories.Sum(c => c.TotalIncome); }
}
public sealed class DevAVProductCategory: INameable {
public string Name { get; set; }
public List<DevAVProduct> Products { get; set; }
public double TotalIncome { get => Products.Sum(p => p.TotalIncome); }
}
public sealed class DevAVProduct: INameable {
public string Name { get; set; }
public List<DevAVMonthlyIncome> Sales { get; set; }
public double TotalIncome { get => Sales.Sum(s => s.Income); }
}
public class DevAVMonthlyIncome {
public DateTime Month { get; set; }
public double Income { get; set; }
}
Public Interface INameable
ReadOnly Property Name As String
End Interface
Public Class DevAVBranch
Implements INameable
Public Property Name As String Implements INameable.Name
Public Property Categories As List(Of DevAVProductCategory)
Public ReadOnly Property TotalIncome As Double
Get
Return Categories.Sum(Function(c) c.TotalIncome)
End Get
End Property
End Class
Public Class DevAVProductCategory
Implements INameable
Public Property Name As String Implements INameable.Name
Public Property Products As List(Of DevAVProduct)
Public ReadOnly Property TotalIncome As Double
Get
Return Products.Sum(Function(p) p.TotalIncome)
End Get
End Property
End Class
Public Class DevAVProduct
Implements INameable
Public Property Name As String Implements INameable.Name
Public Property Sales As List(Of DevAVMonthlyIncome)
Public ReadOnly Property TotalIncome As Double
Get
Return Sales.Sum(Function(s) s.Income)
End Get
End Property
End Class
Public Class DevAVMonthlyIncome
Public Property Month As DateTime
Public Property Income As Double
End Class
class MainViewModel {
public IReadOnlyList<DevAVBranch> Branches { get; }
public MainViewModel() {
Branches = new BranchDAO().GetBranches();
}
}
Class MainViewModel
Private _branches As IReadOnlyList(Of DevAVBranch)
Public ReadOnly Property Branches As IReadOnlyList(Of DevAVBranch)
Get
Return _branches
End Get
End Property
Public Sub New()
_branches = new BranchDAO().GetBranches();
End Sub
End Class
The Chart Control provides the Data Adapter to manage collections of objects that data provides on different drill down levels. The Adapter uses the Children Selector to get the next detail level collection:
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<local:DevAVSeriesChildrenSelector x:Key="childrenSelector"/>
<Window.Resources>
<dxc:ChartControl>
<dxc:XYDiagram2D>
<dxc:XYDiagram.SeriesItemsSource>
<dxc:HierarchicalDataAdapter
DataSource="{Binding Branches}"
ChildrenSelector="{StaticResource childrenSelector}"/>
</dxc:XYDiagram.SeriesItemsSource>
</dxc:XYDiagram2D>
<!-- Other Chart Settings.-->
</dxc:ChartControl>
class DevAVSeriesChildrenSelector : IChildrenSelector {
public IEnumerable SelectChildren(object item) {
if (item as DevAVBranch branch) return new List<DevAVBranch> { branch };
if (item as DevAVProductCategory category) return category.Products;
if (item as DevAVProduct product) return new List<DevAVProduct> { product };
return null;
}
}
Public Class DevAVSeriesChildrenSelector
Implements IChildrenSelector
Public Function SelectChildren(item As Object) As IEnumerable Implements IChildrenSelector.SelectChildren
Dim branch = TryCast(item, DevAVBranch)
If (branch IsNot Nothing) Then Return New List(Of DevAVBranch) From { branch }
Dim category = TryCast(item, DevAVProductCategory)
If (category IsNot Nothing) Then Return category.Products
Dim product = TryCast(item, DevAVProduct)
If (product IsNot Nothing) Then Return New List(Of DevAVProduct) From { product }
Return Nothing
End Function
End Class
The table below lists classes and properties included in the above-mentioned code:
|
Symbol
|
Description
| | --- | --- | |
|
Gets or sets the collection of objects used to generate series.
| |
|
The data adapter that uses data objects with aggregation and composition relationships as data sources for different detail levels when the Chart control is in the Drill Mode.
| |
HierarchicalDataAdapterBase.DataSource
|
Gets or set the collection of data source objects for the Chart in the Drill Mode.
| |
HierarchicalDataAdapter.ChildrenSelector
|
Gets or sets an object that returns a child object collection from a data object on whose representation the end user clicked.
|
Use the SeriesItemTemplateSelector property to configure how the chart control shows data on different detail levels:
The following code snippet shows how to use this property.
<Window.Resources>
<!-- Other resources are here. -->
<local:DevAVSeriesTemplateSelector
x:Key="seriesTemplateSelector"
AllCategoriesTemplate="{StaticResource stackedBarTemplate}"
BranchCategoriesTemplate="{StaticResource barTemplate}"
CategoryProductsTemplate="{StaticResource stackedSplineAreaTemplate}"
ProductTemplate="{StaticResource splineTemplate}">
</local:DevAVSeriesTemplateSelector>
<Window.Resources>
<dxc:ChartControl>
<dxc:XYDiagram2D SeriesItemTemplateSelector="{StaticResource seriesTemplateSelector}">
<!-- Other diagram settings. -->
</dxc:XYDiagram2D>
<!-- Other Chart Settings. -->
</dxc:ChartControl>
class DevAVSeriesTemplateSelector : DataTemplateSelector {
public DataTemplate AllCategoriesTemplate { get; set; }
public DataTemplate BranchCategoriesTemplate { get; set; }
public DataTemplate CategoryProductsTemplate { get; set; }
public DataTemplate ProductTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
Diagram diagram = (Diagram)container;
if (item is DevAVBranch && diagram.BreadcrumbItems.Count == 1)
return AllCategoriesTemplate;
if (item is DevAVBranch)
return BranchCategoriesTemplate;
if (item is DevAVProduct && diagram.BreadcrumbItems[diagram.BreadcrumbItems.Count - 1].SourceObject is DevAVProduct)
return ProductTemplate;
if (item is DevAVProduct)
return CategoryProductsTemplate;
return null;
}
}
Class DevAVSeriesTemplateSelector
Inherits DataTemplateSelector
Public Property AllCategoriesTemplate As DataTemplate
Public Property BranchCategoriesTemplate As DataTemplate
Public Property CategoryProductsTemplate As DataTemplate
Public Property ProductTemplate As DataTemplate
Public Overrides Function SelectTemplate(item As Object, container As DependencyObject) As DataTemplate
Dim diagram = CType(container, Diagram)
Dim branch = TryCast(item, DevAVBranch)
If (branch IsNot Nothing) And (diagram.BreadcrumbItems.Count = 1) Then Return AllCategoriesTemplate
If (branch IsNot Nothing) Then Return BranchCategoriesTemplate
Dim product = TryCast(item, DevAVProduct)
Dim lastLevelSourceProduct = TryCast(diagram.BreadcrumbItems(diagram.BreadcrumbItems.Count - 1).SourceObject, DevAVProduct)
If (product IsNot Nothing) And (lastLevelSourceProduct IsNot Nothing) Then Return ProductTemplate
If (product IsNot Nothing) Then Return CategoryProductsTemplate
Return Nothing
End Function
End Class
The Data Adapter provides the BreadcrumbTextProvider property that manages text the chart shows for detail levels in the breadcrumb panel:
The following code snippet shows how to use this property.
<Window.Resources>
<!-- Other resources. -->
<local:DevAVBreadcrumbTextProvider x:Key="textProvider"/>
</Window.Resources>
<!-- Several tags are skipped. -->
<dxc:XYDiagram2D.SeriesItemsSource>
<dxc:HierarchicalDataAdapter DataSource="{Binding Branches}"
ChildrenSelector="{StaticResource childrenSelector}"
BreadcrumbTextProvider="{StaticResource textProvider}"/>
</dxc:XYDiagram2D.SeriesItemsSource>
<dxc:XYDiagram2D.SeriesItemsSource>
class DevAVBreadcrumbTextProvider : IBreadcrumbTextProvider {
public string GetText(object seriesSourceObj, object pointSourceObj) {
StringBuilder sb = new StringBuilder("(");
if (seriesSourceObj != null) sb.Append(GetName(seriesSourceObj));
if (seriesSourceObj != null && pointSourceObj != null) sb.Append(", ");
if (pointSourceObj != null) sb.Append(GetName(pointSourceObj));
sb.Append(")");
return sb.ToString();
}
private string GetName(object obj) {
if (obj is INameable nameable) return nameable.Name;
return String.Empty;
}
}
Public Class DevAVBreadcrumbTextProvider
Implements IBreadcrumbTextProvider
Public Function GetText(seriesSourceObj As Object, pointSourceObj As Object) As String Implements IBreadcrumbTextProvider.GetText
Dim sb = New StringBuilder("(")
If (seriesSourceObj IsNot Nothing) Then sb.Append(GetName(seriesSourceObj))
If (seriesSourceObj IsNot Nothing And pointSourceObj IsNot Nothing) Then sb.Append(", ")
If (pointSourceObj IsNot Nothing) Then sb.Append(GetName(pointSourceObj))
sb.Append(")")
Return sb.ToString()
End Function
Private Function GetName(obj As Object) As String
Dim nameable = TryCast(obj, INameable)
If (nameable IsNot Nothing) Then Return nameable.Name
Return String.Empty
End Function
End Class
The code above uses the following classes and properties:
|
Symbol
|
Description
| | --- | --- | |
HierarchicalDataAdapterBase.BreadcrumbTextProvider
|
Gets or sets the object that provides text for breadcrumb items when the Chart is in the Drill Mode.
| |
|
An interface of a text provider for the HierarchicalDataAdapter.
|
Chart control diagrams provide the DrillDownStateChanged event whose handler can customize the Chart when the current detail level changes:
<dxc:ChartControl DataContext="{Binding Branches}">
<dxc:XYDiagram2D
x:Name="diagram"
SeriesItemTemplateSelector="{StaticResource seriesTemplateSelector}"
DrillDownStateChanged="XYDiagram2D_DrillDownStateChanged">
<!-- Other Diagram settings. -->
</dxc:XYDiagram>
<!-- Other Chart settings. -->
</dxc:ChartControl>
private void OnDrillDownStateChanged(object sender, DrillDownStateChangedEventArgs e) {
diagram.Rotated = e.BreadcrumbItems.Last().IsHome;
}
Private Sub Diagram_DrillDownStateChanged(sender As Object, e As DrillDownStateChangedEventArgs)
diagram.Rotated = e.BreadcrumbItems.Last().IsHome
End Sub