Master Details View in WinUI DataGrid
4 Apr 202424 minutes to read
SfDataGrid provides support to represent the hierarchical data in the form of nested tables using Master-Details View. You can expand or collapse the nested tables (DetailsViewDataGrid) by using an expander in a row or programmatically. The number of tables nested with relations is unlimited.
Generating Master-Details view from IEnumerable
Master-Details View’s relation can be generated for the properties of type IEnumerable in the underlying data object contain.
Follow the below steps to generate the Master-Details View for IEnumerable
.
- Create the data model with relations (Here, relations are
IEnumerable
type properties) - Defining relations
- Auto-generating relations
- Manually defining relations
Create the data model with relations
Create an Employee
class with Sales
and Orders
property of type ObservableCollection to form the relations. The Sales
and Orders
properties are defined as ObservableCollection<SalesInfo>
and ObservableCollection<OrderInfo>
respectively.
public class SalesInfo : INotifyPropertyChanged
{
private int _orderID;
private string _salesID;
private string _productName;
public int OrderID
{
get { return _orderID; }
set
{
_orderID = value;
OnPropertyChanged("OrderID");
}
}
public string SalesID
{
get { return _salesID; }
set
{
_salesID = value;
OnPropertyChanged("SalesID");
}
}
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
OnPropertyChanged("ProductName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
public class OrderInfo : INotifyPropertyChanged
{
private int orderId;
private int _quantity;
public int OrderID
{
get { return orderId; }
set
{
orderId = value;
OnPropertyChanged("OrderID");
}
}
public int Quantity
{
get { return _quantity; }
set
{
_quantity = value;
OnPropertyChanged("Quantity");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
public class Employee : INotifyPropertyChanged
{
private int _EmployeeID;
private int _orderId;
private string _city;
private ObservableCollection<SalesInfo> _sales;
private ObservableCollection<OrderInfo> _orders;
public int EmployeeID
{
get { return this._EmployeeID; }
set
{
this._EmployeeID = value;
OnPropertyChanged("EmployeeID");
}
}
public int OrderID
{
get { return this._orderId; }
set
{
this._orderId = value;
OnPropertyChanged("OrderID");
}
}
public string City
{
get { return _city; }
set
{
_city = value;
OnPropertyChanged("City");
}
}
public ObservableCollection<SalesInfo> Sales
{
get { return _sales; }
set
{
_sales = value;
OnPropertyChanged("Sales");
}
}
public ObservableCollection<OrderInfo> Orders
{
get { return _orders; }
set
{
_orders = value;
OnPropertyChanged("Orders");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Create a ViewModel
class with Employees
property and it is initialized with several data objects in the constructor. Similarly, the Sales
and Orders
property are also initialized.
public class ViewModel
{
ObservableCollection<Employee> _employees;
public ObservableCollection<Employee> Employees
{
get { return _employees; }
set { _employees = value; }
}
public ViewModel()
{
this.GenerateOrders();
this.GenerateSales();
_employees = GetEmployeesDetails();
}
public ObservableCollection<Employee> GetEmployeesDetails()
{
var employees = new ObservableCollection<Employee>();
employees.Add(new Employee() { EmployeeID = 1, OrderID = 1001, City = "Berlin", Orders = GetOrders(1001), Sales = GetSales(1001) });
employees.Add(new Employee() { EmployeeID = 2, OrderID = 1002, City = "Mexico D.F.", Orders = GetOrders(1002), Sales = GetSales(1002) });
employees.Add(new Employee() { EmployeeID = 3, OrderID = 1003, City = "London", Orders = GetOrders(1002), Sales = GetSales(1003) });
employees.Add(new Employee() { EmployeeID = 4, OrderID = 1004, City = "BERGS", Orders = GetOrders(1002), Sales = GetSales(1004) });
employees.Add(new Employee() { EmployeeID = 5, OrderID = 1005, City = "Mannheim", Orders = GetOrders(1002), Sales = GetSales(1005) });
return employees;
}
//Orders collection is initialized here.
ObservableCollection<OrderInfo> Orders = new ObservableCollection<OrderInfo>();
public void GenerateOrders()
{
Orders.Add(new OrderInfo() { OrderID = 1001, Quantity = 10 });
Orders.Add(new OrderInfo() { OrderID = 1001, Quantity = 10 });
Orders.Add(new OrderInfo() { OrderID = 1002, Quantity = 20 });
Orders.Add(new OrderInfo() { OrderID = 1002, Quantity = 20 });
Orders.Add(new OrderInfo() { OrderID = 1003, Quantity = 50 });
Orders.Add(new OrderInfo() { OrderID = 1004, Quantity = 70 });
Orders.Add(new OrderInfo() { OrderID = 1005, Quantity = 20 });
Orders.Add(new OrderInfo() { OrderID = 1005, Quantity = 20 });
}
private ObservableCollection<OrderInfo> GetOrders(int orderID)
{
ObservableCollection<OrderInfo> orders = new ObservableCollection<OrderInfo>();
foreach (var order in Orders)
if (order.OrderID == orderID)
orders.Add(order);
return orders;
}
//Sales collection is initialized here.
ObservableCollection<SalesInfo> Sales = new ObservableCollection<SalesInfo>();
public void GenerateSales()
{
Sales.Add(new SalesInfo() { OrderID = 1001, SalesID = "A00001", ProductName = "Bike1" });
Sales.Add(new SalesInfo() { OrderID = 1001, SalesID = "A00002", ProductName = "Bike1" });
Sales.Add(new SalesInfo() { OrderID = 1002, SalesID = "A00003", ProductName = "Cycle" });
Sales.Add(new SalesInfo() { OrderID = 1003, SalesID = "A00004", ProductName = "Car" });
}
private ObservableCollection<SalesInfo> GetSales(int orderID)
{
ObservableCollection<SalesInfo> sales = new ObservableCollection<SalesInfo>();
foreach (var sale in Sales)
if (sale.OrderID == orderID)
sales.Add(sale);
return sales;
}
}
Defining relations in DataGrid
Auto-generating relations
SfDataGrid will automatically generate relations and inner relations for the IEnumerable
property types in the data object. This can be enabled by setting SfDataGrid.AutoGenerateRelations to true
.
Bind the collection created in the previous step to SfDataGrid.ItemsSource and set the SfDataGrid.AutoGenerateRelations to true
.
<dataGrid:SfDataGrid x:Name="dataGrid"
ColumnWidthMode="Star"
GridLinesVisibility="Both"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
ItemsSource="{Binding Employees}" />
dataGrid.AutoGenerateRelations = true;
dataGrid.AutoGeneratingRelations += dataGrid_AutoGeneratingRelations;
private void dataGrid_AutoGeneratingRelations(object sender, AutoGeneratingRelationsArgs e)
{
e.GridViewDefinition.DataGrid.GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both;
}
When relations are auto-generated, you can handle the SfDataGrid.AutoGeneratingRelations event to customize or cancel the GridViewDefinition before they are added to the SfDataGrid.DetailsViewDefinition.
Here, two relations are created from Sales
and Orders
collection property.
Manually defining Relations
You can define the Master-Details View’s relation manually using SfDataGrid.DetailsViewDefinition, when the SfDataGrid.AutoGenerateRelations is false
.
To define Master-Details View relations, create GridViewDefinition and set the name of IEnumerable
type property (from data object) to ViewDefinition.RelationalColumn. Then, add the GridViewDefinition
to the SfDataGrid.DetailsViewDefinition.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
ColumnWidthMode="Star"
GridLinesVisibility="Both"
AutoGenerateRelations="False"
ItemsSource="{Binding Employees}">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<!-- FirstLevelNestedGrid1 is created here -->
<dataGrid:GridViewDefinition RelationalColumn="Sales">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid1"
GridLinesVisibility="Both"
AutoGenerateColumns="True"/>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
<!-- FirstLevelNestedGrid2 is created here -->
<dataGrid:GridViewDefinition RelationalColumn="Orders">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid2"
GridLinesVisibility="Both"
AutoGenerateColumns="True"/>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
dataGrid.AutoGenerateRelations = false;
var gridViewDefinition1 = new GridViewDefinition();
gridViewDefinition1.RelationalColumn = "Sales";
gridViewDefinition1.DataGrid = new SfDataGrid() { Name = "FirstLevelNestedGrid1", AutoGenerateColumns = true, GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both };
var gridViewDefinition2 = new GridViewDefinition();
gridViewDefinition2.RelationalColumn = "Orders";
gridViewDefinition2.DataGrid = new SfDataGrid() { Name = "FirstLevelNestedGrid2", AutoGenerateColumns = true, GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both };
dataGrid.DetailsViewDefinition.Add(gridViewDefinition1);
dataGrid.DetailsViewDefinition.Add(gridViewDefinition2);
In the same way, you can define relations for first level nested grids by defining relations to the ViewDefinition.DataGrid of first level nested grid.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
ItemsSource="{Binding Path=Source,Mode=TwoWay}"
GridLinesVisibility="Both"
ColumnWidthMode="Star">
<dataGrid:SfDataGrid.DetailsViewDefinition >
<dataGrid:GridViewDefinition RelationalColumn="Sales" >
<dataGrid:GridViewDefinition.DataGrid >
<dataGrid:SfDataGrid x:Name="FirstDetailsViewGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
GridLinesVisibility="Both">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="Orders">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="SecondDetailsViewGrid"
GridLinesVisibility="Both"
AutoGenerateColumns="True" >
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
dataGrid.AutoGenerateRelations = false;
// GridViewDefinition for parent DataGrid
var gridViewDefinition1 = new GridViewDefinition();
gridViewDefinition1.RelationalColumn = "Sales";
var firstLevelNestedGrid = new SfDataGrid() { Name = "FirstLevelNestedGrid", AutoGenerateColumns = true, GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both };
firstLevelNestedGrid.AutoGenerateRelations = false;
// GridViewDefinition for FirstLevelNestedGrid
var gridViewDefinition = new GridViewDefinition();
gridViewDefinition.RelationalColumn = "Orders";
gridViewDefinition.DataGrid = new SfDataGrid() { Name = "SecondLevelNestedGrid", AutoGenerateColumns = true, GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both };
firstLevelNestedGrid.DetailsViewDefinition.Add(gridViewDefinition);
gridViewDefinition1.DataGrid = firstLevelNestedGrid;
dataGrid.DetailsViewDefinition.Add(gridViewDefinition1);
Populating Master-Details view through events
You can load ItemsSource
for DetailsViewDataGrid asynchronously by handling SfDataGrid.DetailsViewExpanding. You can set ItemsSource in on-demand when expanding record through GridDetailsViewExpandingEventArgs.DetailsViewItemsSource property in the SfDataGrid.DetailsViewExpanding event handler.
<dataGrid:SfDataGrid x:Name="dataGrid"
ColumnWidthMode="Star"
GridLinesVisibility="Both"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
DetailsViewExpanding="dataGrid_DetailsViewExpanding"
ItemsSource="{Binding Employees}" />
private void dataGrid_DetailsViewExpanding(object sender, GridDetailsViewExpandingEventArgs e)
{
e.DetailsViewItemsSource.Clear();
var itemsSource = GetItemSource();
e.DetailsViewItemsSource.Add("ProductDetails", itemsSource);
}
private ObservableCollection<OrderInfo> GetItemSource()
{
var products = new ObservableCollection<OrderInfo>();
products.Add(new OrderInfo() { OrderID = 1001, Quantity = 10 });
products.Add(new OrderInfo() { OrderID = 1002, Quantity = 20 });
products.Add(new OrderInfo() { OrderID = 1003, Quantity = 30 });
return products;
}
NOTE
This event will be trigged only when underlying data object contains relations. Otherwise, you have to define dummy relation to notify DataGrid to fire this event.
In the below code snippet, AutoGenerateRelations
set to false and also relation is defined with some name to RelationalColumn
. For example, ProductDetails
is the dummy relational column and underlying data object does not contain the IEnumerable
type property with name ProductDetails
.
<dataGrid:SfDataGrid x:Name="dataGrid"
ColumnWidthMode="Star"
GridLinesVisibility="Both"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
DetailsViewExpanding="dataGrid_DetailsViewExpanding"
ItemsSource="{Binding Employees}" >
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="ProductDetails">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid"
GridLinesVisibility="Both"
AutoGenerateColumns="True" />
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
Now the ItemsSource
for DetailsViewDataGrid can be supplied through DetailsViewExpanding event as mentioned above.
Defining properties for DetailsViewDataGrid
You can set properties like AllowEditing
, AllowFiltering
and AllowSorting
for DetailsViewDataGrid by using the GridViewDefinition.DataGrid property.
When AutoGenerateRelations is false
For manually defined relation, the properties can be directly set to the ViewDefinition.DataGrid.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
GridLinesVisibility="Both"
ItemsSource="{Binding Path=Source,Mode=TwoWay}"
ColumnWidthMode="Star">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="OrderDetails">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid"
AllowEditing="True"
AllowFiltering="True"
AllowResizingColumns="True"
AllowSorting="True"
GridLinesVisibility="Both"
AutoGenerateColumns="False" />
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
FirstLevelNestedGrid.AllowEditing = true;
FirstLevelNestedGrid.AllowFiltering = true;
FirstLevelNestedGrid.AllowResizingColumns = true;
FirstLevelNestedGrid.AllowSorting = true;
FirstLevelNestedGrid.GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both;
For two levels of nesting,
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
ItemsSource="{Binding Path=Source,Mode=TwoWay}"
ColumnWidthMode="Star">
<dataGrid:SfDataGrid.DetailsViewDefinition >
<dataGrid:GridViewDefinition RelationalColumn="OrderDetails" >
<dataGrid:GridViewDefinition.DataGrid >
<dataGrid:SfDataGrid x:Name="FirstDetailsViewGrid"
AutoGenerateColumns="True"
GridLinesVisibility="Both"
AutoGenerateRelations="False">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="SalesDetails">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="SecondDetailsViewGrid"
AllowEditing="True"
AllowFiltering="True"
GridLinesVisibility="Both"
AutoGenerateColumns="True" >
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
SecondDetailsViewGrid.AllowEditing = true;
SecondDetailsViewGrid.AllowFiltering = true;
SecondDetailsViewGrid.GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both;
When AutoGenerateRelations is true
When the relation is auto-generated, you can get the GridViewDefinition.DataGrid in the AutoGeneratingRelations event handler to set the properties.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
GridLinesVisibility="Both"
ItemsSource="{Binding Path=Source,Mode=TwoWay}"
ColumnWidthMode="Star">
</dataGrid:SfDataGrid>
dataGrid.AutoGeneratingRelations += DataGrid_AutoGeneratingRelations;
private void DataGrid_AutoGeneratingRelations(object sender, AutoGeneratingRelationsArgs e)
{
e.GridViewDefinition.DataGrid.AllowEditing = true;
e.GridViewDefinition.DataGrid.AllowFiltering = true;
e.GridViewDefinition.DataGrid.AllowSorting = true;
e.GridViewDefinition.DataGrid.AllowResizingColumns = true;
e.GridViewDefinition.DataGrid.GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both;
}
NOTE
When you make any change in one DetailsViewDataGrid, that change will be applied to all DetailsViewDataGrid in the same level. For example, when you resize the first column in one DetailsViewDataGrid, the same column width is applied to all DetailsViewDataGrid at that level. This is applicable for features like filtering, sorting, grouping and re ordering columns also.
NOTE
AllowFrozenGroupHeaders, FrozenRowsCount, FrozenFooterRowsCount, FooterColumnCount, FrozenColumnCount properties are not supported while using Master Details view.
Defining columns for DetailsViewDataGrid
The ViewDefinition.DataGrid columns can be generated either automatically or manually like parent DataGrid. You can refer Columns section to know more about columns.
Auto-generating columns
You can auto-generate the ViewDefinition.DataGrid’s columns by setting the GridViewDefinition.DataGrid.AutoGenerateColumns to true
. You can cancel or customize the column being created for ViewDefinition.DataGrid by handling GridViewDefinition.DataGrid.AutoGeneratingColumn event.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
GridLinesVisibility="Both"
ItemsSource="{Binding Employees}">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="Sales">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid"
AutoGenerateColumns="True"
GridLinesVisibility="Both"
AutoGeneratingColumn="FirstLevelNestedGrid_AutoGeneratingColumn" />
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
FirstLevelNestedGrid.AutoGeneratingColumn += FirstLevelNestedGrid_AutoGeneratingColumn1;
When relation is auto generated, you can set properties and wire GridViewDefinition.DataGrid.AutoGeneratingColumn event in SfDataGrid.AutoGeneratingRelations event handler.
void dataGrid_AutoGeneratingRelations(object sender, Syncfusion.UI.Xaml.Grid.AutoGeneratingRelationsArgs e)
{
e.GridViewDefinition.DataGrid.AutoGenerateColumns = true;
e.GridViewDefinition.DataGrid.AutoGeneratingColumn += FirstLevelNestedGrid_AutoGeneratingColumn;
}
Manually defining columns
You can directly define the columns to ViewDefinition.DataGrid when AutoGenerateColumns is false
. When relation is manually defined, you can define the columns directly to ViewDefinition.DataGrid
in XAML or C#, by adding desired column to the SfDataGrid.Columns collection.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
GridLinesVisibility="Both"
ItemsSource="{Binding Employees}">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="Sales">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid"
AutoGenerateColumns="False"
GridLinesVisibility="Both">
<dataGrid:SfDataGrid.Columns>
<dataGrid:GridTextColumn MappingName="OrderID" />
<dataGrid:GridTextColumn MappingName="ProductName" />
</dataGrid:SfDataGrid.Columns>
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
When relation is auto generated, you can define the ViewDefinition.DataGrid’s columns manually through the SfDataGrid.AutoGeneratingRelations event handler.
this.dataGrid.AutoGeneratingRelations += dataGrid_AutoGeneratingRelations;
void dataGrid_AutoGeneratingRelations(object sender, Syncfusion.UI.Xaml.Grid.AutoGeneratingRelationsArgs e)
{
e.GridViewDefinition.DataGrid.AutoGenerateColumns = false;
e.GridViewDefinition.DataGrid.GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both;
e.GridViewDefinition.DataGrid.Columns.Add(new GridTextColumn() { MappingName = "OrderID" });
e.GridViewDefinition.DataGrid.Columns.Add(new GridTextColumn() { MappingName = "ProductName" });
}
Handling events for DetailsViewDataGrid
You can handle DetailsViewDataGrid events by wiring events to ViewDefinition.DataGrid where sender is ViewDefinition.DataGrid. In another way, you can handle DetailsViewDataGrid events also through ParentDataGrid events by setting NotifyEventsToParentDataGrid property of ViewDefinition.DataGrid.
When AutoGenerateRelations is false
For manually defined relation, the events can be wired from ViewDefinition.DataGrid directly in XAML or C#.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
GridLinesVisibility="Both"
ItemsSource="{Binding Employees}">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="ProductDetails">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid"
AutoGenerateColumns="True"
GridLinesVisibility="Both"
CurrentCellBeginEdit="FirstLevelNestedGrid_CurrentCellBeginEdit"
FilterChanging="FirstLevelNestedGrid_FilterChanging"
SortColumnsChanging="FirstLevelNestedGrid_SortColumnsChanging" />
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
FirstLevelNestedGrid.CurrentCellBeginEdit += FirstLevelNestedGrid_CurrentCellBeginEdit;
FirstLevelNestedGrid.FilterChanging += FirstLevelNestedGrid_FilterChanging;
FirstLevelNestedGrid.SortColumnsChanging += FirstLevelNestedGrid_SortColumnsChanging;
For second level nested grid,
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
GridLinesVisibility="Both"
ItemsSource="{Binding Path=Source,Mode=TwoWay}"
ColumnWidthMode="Star">
<dataGrid:SfDataGrid.DetailsViewDefinition >
<dataGrid:GridViewDefinition RelationalColumn="OrderDetails" >
<dataGrid:GridViewDefinition.DataGrid >
<dataGrid:SfDataGrid x:Name="FirstDetailsViewGrid"
AutoGenerateColumns="True"
GridLinesVisibility="Both"
AutoGenerateRelations="False">
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="SalesDetails">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="SecondDetailsViewGrid"
GridLinesVisibility="Both"
CurrentCellBeginEdit="SecondDetailsViewGrid_CurrentCellBeginEdit"
FilterChanging="SecondDetailsViewGrid_FilterChanging"
AutoGenerateColumns="True" >
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
SecondDetailsViewGrid.FilterChanging += SecondDetailsViewGrid_FilterChanging;
SecondDetailsViewGrid.CurrentCellBeginEdit += SecondDetailsViewGrid_CurrentCellBeginEdit;
private void SecondDetailsViewGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs e)
{
}
private void SecondDetailsViewGrid_FilterChanging(object sender, GridFilterEventArgs e)
{
}
When AutoGenerateRelations is true
When the relation is auto-generated, you can get the GridViewDefinition.DataGrid in the AutoGeneratingRelations event handler to wire the events.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
GridLinesVisibility="Both"
ItemsSource="{Binding Employees}">
</dataGrid:SfDataGrid>
this.dataGrid.AutoGeneratingRelations += dataGrid_AutoGeneratingRelations;
private void dataGrid_AutoGeneratingRelations(object sender, AutoGeneratingRelationsArgs e)
{
e.GridViewDefinition.DataGrid.GridLinesVisibility = Syncfusion.UI.Xaml.Grids.GridLinesVisibility.Both;
e.GridViewDefinition.DataGrid.SortColumnsChanging += FirstLevelNestedGrid_SortColumnsChanging;
e.GridViewDefinition.DataGrid.CurrentCellBeginEdit += FirstLevelNestedGrid_CurrentCellBeginEdit;
e.GridViewDefinition.DataGrid.FilterChanging += FirstLevelNestedGrid_FilterChanging;
}
private void FirstLevelNestedGrid_FilterChanging(object sender, GridFilterEventArgs e)
{
}
private void FirstLevelNestedGrid_SortColumnsChanging(object sender, Syncfusion.UI.Xaml.Grids.GridSortColumnsChangingEventArgs e)
{
}
private void FirstLevelNestedGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs e)
{
}
Column sizing
SfDataGrid allows you to apply column sizer to DetailsViewDataGrid
by setting the GridViewDefinition.DataGrid.ColumnWidthMode like parent DataGrid. For more information, refer the Column Sizing section.
Selection
DetailsViewDataGrid allows you to select rows or cells based on the SelectionUnit property in its parent DataGrid.
Getting the selected DetailsViewDataGrid
You can get the currently selected DetailsViewDataGrid
by using the SelectedDetailsViewGrid property of parent DataGrid.
var detailsViewDataGrid = this.dataGrid.SelectedDetailsViewGrid;
For accessing nested level SelectedDetailsViewGrid ,
var detailsViewDataGrid = this.dataGrid.SelectedDetailsViewGrid.SelectedDetailsViewGrid;
Getting the SelectedItem, SelectedItems and SelectedIndex of DetailsViewDataGrid
You can access DetailsViewDataGrid’s SelectedItem, SelectedItems and SelectedIndex properties by using parent dataGrid’s SelectedDetailsViewGrid
property also.
int selectedIndex = this.dataGrid.SelectedDetailsViewGrid.SelectedIndex;
var selectedItem = this.dataGrid.SelectedDetailsViewGrid.SelectedItem;
var selectedItems = this.dataGrid.SelectedDetailsViewGrid.SelectedItems;
Getting the CurrentCell of DetailsViewDataGrid
You can get the CurrentCell of DetailsViewDataGrid
by using the SelectedDetailsViewGrid property of parent DataGrid or CurrentCellBeginEdit event of DetailsViewDataGrid
.
this.FirstLevelNestedGrid.CurrentCellBeginEdit += FirstLevelNestedGrid_CurrentCellBeginEdit;
void FirstLevelNestedGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs args)
{
var detailsViewDataGrid = args.OriginalSender as DetailsViewDataGrid;
var currentCell = detailsViewDataGrid.SelectionController.CurrentCellManager.CurrentCell;
}
Programmatic selection in DetailsViewDataGrid
In DetailsViewDataGrid
, you can add or remove the selection programmatically like parent DataGrid. You can get particular DetailsViewDataGrid
by using DetailsViewLoading event.
Selecting records
You can select the particular record by using SelectedIndex property.
this.dataGrid.DetailsViewLoading += dataGrid_DetailsViewLoading;
void dataGrid_DetailsViewLoading (object sender, DetailsViewLoadingAndUnloadingEventArgs e)
{
e.DetailsViewDataGrid.SelectedIndex = 1;
}
Here, the record in first position is selected in DetailsViewDataGrid
.
You can select multiple records by using SelectedItems property.
Customizing selection for DetailsViewDataGrid
You can also customize the selection behavior of DetailsViewDataGrid
like the parent DataGrid. You can refer selection for more information about customizing selection behavior.
Follow the steps mentioned in selection customization section to customize selection behavior of DetailsViewDataGrid
and set the customized selection controller to DetailsViewDataGrid.SelectionController in DetailsViewLoading event.
public class CustomSelectionController:GridSelectionController
{
public CustomSelectionController(SfDataGrid dataGrid)
:base(dataGrid)
{
}
}
this.dataGrid.DetailsViewLoading += dataGrid_DetailsViewLoading;
void dataGrid_DetailsViewLoading(object sender, DetailsViewLoadingAndUnloadingEventArgs e)
{
if (!(e.DetailsViewDataGrid.SelectionController is CustomSelectionController))
e.DetailsViewDataGrid.SelectionController = new CustomSelectionController(e.DetailsViewDataGrid);
}
NOTE
For customizing selection in second level nested grid, you can refer here.
Changing header appearance of DetailsViewDataGrid
You can customize the header appearance of DetailsViewDataGrid , through HeaderStyle
property of DetailsViewDataGrid
.
<Application.Resources>
<Style x:Key="headerStyle" TargetType="dataGrid:GridHeaderCellControl">
<Setter Property="Background" Value="Red"/>
</Style>
</Application.Resources>
<dataGrid:SfDataGrid x:Name="dataGrid"
AllowResizingColumns="True"
AutoGenerateColumns="True"
AutoGenerateRelations="False"
GridLinesVisibility="Both"
ItemsSource="{Binding Employees}" >
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="Sales">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstDetailsViewGrid"
AllowEditing="True"
AutoGenerateColumns="True"
GridLinesVisibility="Both"
HeaderStyle="{StaticResource headerStyle}" >
</dataGrid:SfDataGrid>
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
Hiding header row of Master-Details View
You can hide the header row of DetailsViewDataGrid
by setting HeaderRowHeight property.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
GridLinesVisibility="Both"
ItemsSource="{Binding Employees}" >
<dataGrid:SfDataGrid.DetailsViewDefinition>
<dataGrid:GridViewDefinition RelationalColumn="Sales">
<dataGrid:GridViewDefinition.DataGrid>
<dataGrid:SfDataGrid x:Name="FirstLevelNestedGrid"
AutoGenerateColumns="True"
GridLinesVisibility="Both"
HeaderRowHeight="0" />
</dataGrid:GridViewDefinition.DataGrid>
</dataGrid:GridViewDefinition>
</dataGrid:SfDataGrid.DetailsViewDefinition>
</dataGrid:SfDataGrid>
FirstLevelNestedGrid.HeaderRowHeight = 0;
Customizing padding of the DetailsViewDataGrid
The padding of DetailsViewDataGrid can be customized through the DetailsViewPadding property and it will be set to its corresponding parent DataGrid.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
GridLinesVisibility="Both"
DetailsViewPadding="15"
ItemsSource="{Binding Employees}" />
NOTE
For customizing appearance for second level nested grid, you can refer here.
Customize expander column width
You can customize the width of ExpanderColumn in SfDataGrid by using ExpanderColumnWidth property as like below.
<dataGrid:SfDataGrid x:Name="dataGrid"
GridLinesVisibility="Both"
ExpanderColumnWidth="50"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
ItemsSource="{Binding Employees}" />
this.dataGrid.ExpanderColumnWidth = 50;
Expanding and collapsing the DetailsViewDataGrid programmatically
SfDataGrid allows you to expand or collapse the DetailsViewDataGrid
programmatically in different ways.
Expand or collapse all the DetailsViewDataGrid
You can expand or collapse all the DetailsViewDataGrid
programmatically by using ExpandAllDetailsView and CollapseAllDetailsView methods.
this.dataGrid.ExpandAllDetailsView();
this.dataGrid.CollapseAllDetailsView();
Expand DetailsViewDataGrid based on level
You can expand all the DetailsViewDataGrid
programmatically based on level using ExpandAllDetailsView method.
this.dataGrid.ExpandAllDetailsView(2);
Here, all the DetailsViewDataGrids up to second level will be expanded.
Expand or collapse Details View based on record index
You can expand or collapse DetailsViewDataGrid
based on the record index by using ExpandDetailsViewAt and CollapseDetailsViewAt methods.
this.dataGrid.ExpandDetailsViewAt(0);
this.dataGrid.CollapseDetailsViewAt(0);
Hiding expander when parent record’s relation property has an empty collection or null
By default, the expander will be visible for all the data rows in parent DataGrid even if its RelationalColumn property has an empty collection or null. You can hide the expander from the view when corresponding RelationalColumn property has an empty collection or null, by setting HideEmptyGridViewDefinition property as true
.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
GridLinesVisibility="Both"
HideEmptyGridViewDefinition="True"
ItemsSource="{Binding Employees}" />
Hiding GridDetailsViewIndentCell
GridDetailsViewIndentCell is used to indicate the space between the expander and first column of the DetailsViewDataGrid. You can hide the GridDetailsViewIndentCell by setting SfDataGrid.ShowDetailsViewIndentCell property to False
for the respective parent grid.
<dataGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
GridLinesVisibility="Both"
ShowDetailsViewIndentCell="False"
ItemsSource="{Binding Employees}" />
dataGrid.ShowDetailsViewIndentCell= False;
Hiding the details view expander icon based on child items count
By default, the state of expander icon is visible for all the data rows in parent DataGrid even if its RelationalColumn
property has an empty collection or null.
You can customize hiding the details view expander icon by handling the SfDataGrid.QueryDetailsViewExpanderState
event. This event occurs when expander icon is changed on expanding or collapsing the details view. You can hide the expander icon by setting the ExpanderVisibility
property to false
in the SfDataGrid.QueryDetailsViewExpanderState
event based on condition.
dataGrid.DetailsViewExpanding += DataGrid_DetailsViewExpanding;
dataGrid.QueryDetailsViewExpanderState += DataGrid_QueryDetailsViewExpanderState;
private void DataGrid_DetailsViewExpanding(object sender, GridDetailsViewExpandingEventArgs e)
{
var employeeInfo = e.Record as Employee;
if (employeeInfo != null)
{
if (employeeInfo.Sales.Count == 0)
e.Cancel = true;
}
}
private void DataGrid_QueryDetailsViewExpanderState(object sender, QueryDetailsViewExpanderStateEventArgs e)
{
var employeeInfo = e.Record as Employee;
if (employeeInfo != null)
{
if (employeeInfo.Sales.Count == 0)
{
e.ExpanderVisibility = false;
}
}
}
The following screenshot illustrates hiding expander icon state based on child items count.
You can download the sample from the following link: Sample.
Change DetailsViewDataGrid ItemsSource at runtime using LiveDataUpdateMode property
ItemsSource for DetailsViewDataGrid is populated from the DataContext of parent row based on ViewDefinition.RelationalColumn. DetailsViewDataGrid doesn’t update its ItemsSource at runtime based on the property change, which is mapped the DetailsViewDataGrid ItemsSource. You can update the ItemsSource on the property change by setting SfDataGrid.LiveDataUpdateMode as AllowChildViewUpdate
.
<dataGrid:SfDataGrid Name="dataGrid"
AutoGenerateColumns="True"
AutoGenerateRelations="True"
GridLinesVisibility="Both"
ItemsSource="{Binding Source}"
LiveDataUpdateMode="AllowChildViewUpdate,AllowDataShaping">
this.dataGrid.LiveDataUpdateMode = LiveDataUpdateMode.AllowChildViewUpdate | LiveDataUpdateMode.AllowDataShaping;
You can get the sample from here.
Handling Events
DetailsViewLoading
The DetailsViewLoading event is raised, when the DetailsViewDataGrid is being loaded in to the view (such as scrolling, window size changed and expanding the record using an expander or programmatically). This event receives two arguments where sender as SfDataGrid and DetailsViewLoadingAndUnloadingEventArgs which contains the following member.
DetailsViewDataGrid
Gets the DetailsViewDataGrid which is loaded into view. You can set the customized Renderers, SelectionController, ResizingController, GridColumnDragDropController, and GridColumnSizer to this. But it is not preferable to change the value of the public properties like AllowFiltering, AllowSorting, SelectionUnit, AllowDeleting, etc., here.
this.dataGrid.DetailsViewLoading += dataGrid_DetailsViewLoading;
void dataGrid_DetailsViewLoading(object sender, DetailsViewLoadingAndUnloadingEventArgs e)
{
if (!(e.DetailsViewDataGrid.SelectionController is CustomSelectionController))
e.DetailsViewDataGrid.SelectionController = new CustomSelectionController(e.DetailsViewDataGrid);
}
DetailsViewUnLoading
The DetailsViewUnLoading event is raised when the DetailsViewDataGrid
is being unloaded from the view.
This event receives two arguments where sender as SfDataGrid and DetailsViewLoadingAndUnloadingEventArgs which contains the following member.
DetailsViewDataGrid
- Gets the DetailsViewDataGrid
which was unloaded from the view (such as scrolling, window size changed, Sorting, Grouping, Filtering and collapsing the DetailsViewDataGrid using expander or programmatically).
this.dataGrid.DetailsViewUnloading += DataGrid_DetailsViewUnloading;
private void DataGrid_DetailsViewUnloading(object sender, DetailsViewLoadingAndUnloadingEventArgs e)
{
}
DetailsViewExpanding
The DetailsViewExpanding event is raised when the DetailsViewDataGrid is being expanded by using an expander.
this.dataGrid.DetailsViewExpanding += DataGrid_DetailsViewExpanding;
private void DataGrid_DetailsViewExpanding(object sender, GridDetailsViewExpandingEventArgs e)
{
}
DetailsViewExpanded
The DetailsViewExpanded event is raised after the DetailsViewDataGrid is expanded by using an expander.
this.dataGrid.DetailsViewExpanded += DataGrid_DetailsViewExpanded;
private void DataGrid_DetailsViewExpanded(object sender, GridDetailsViewExpandedEventArgs e)
{
}
DetailsViewCollapsing
The DetailsViewCollapsing event is raised when the DetailsViewDataGrid
is being collapsed from the view by using an expander.
this.dataGrid.DetailsViewCollapsing += DataGrid_DetailsViewCollapsing;
private void DataGrid_DetailsViewCollapsing(object sender, GridDetailsViewCollapsingEventArgs e)
{
}
DetailsViewCollapsed
The DetailsViewCollapsed event is raised after the DetailsViewDataGrid
is collapsed by using an expander.
this.dataGrid.DetailsViewCollapsed += DataGrid_DetailsViewCollapsed;
private void DataGrid_DetailsViewCollapsed(object sender, GridDetailsViewCollapsedEventArgs e)
{
}
Cancel expanding or collapsing operations through events
You can cancel expanding operation while expanding the DetailsViewDataGrid
by using GridDetailsViewExpandingEventArgs.Cancel property in the DetailsViewExpanding event handler.
this.dataGrid.DetailsViewExpanding += DataGrid_DetailsViewExpanding;
private void dataGrid_DetailsViewExpanding(object sender, GridDetailsViewExpandingEventArgs e)
{
if ((e.Record as Employee).OrderID == 1002)
e.Cancel = true;
}
Similarly, the collapsing operation can be canceled through the GridDetailsViewCollapsingEventArgs.Cancel property in the DetailsViewCollapsing event handler.
this.dataGrid.DetailsViewCollapsing += DataGrid_DetailsViewCollapsing;
private void DataGrid_DetailsViewCollapsing(object sender, GridDetailsViewCollapsingEventArgs e)
{
if ((e.Record as Employee).OrderID == 1002)
e.Cancel = true;
}
Master-Details View limitations
Following are the limitations of Master-Details View in SfDataGrid.
-
DetailsViewDataGrid
does not have GroupDropArea. -
DetailsViewDataGrid
does not supportAutoGenerateColumnsMode.ResetAll
. Instead it works based onReset
. - Master-Details View doesn’t support Data Virtualization.
- Master-Details View doesn’t support AllowFrozenGroupHeader.
- Master-Details View doesn’t support Freeze Pane.
- Master-Details View doesn’t support AutoRowHeight.
- For
DetailsViewDataGrid
, SelectionMode, SelectionUnit, NavigationMode, DetailsViewPadding properties are assigned from its parent grid only. So both parent DataGrid andDetailsViewDataGrid
cannot have different values for these properties.