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.

WinUI DataGrid with Master Details View

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.

WinUI DataGrid displays Master Details View based on Auto Generated Relations

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);

WinUI DataGrid displays Master Details View based on Manually defined Relations

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);

WinUI DataGrid displays Master Details View with Manually defined Relations

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>

Customizing Header Appearance of Master Details View in WinUI DataGrid

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;

WinUI DataGrid displays Master Details View with Hidden Header Row

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}" />

WinUI DataGrid displays Padding Customization of Master Details View

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}" />

Hide Expander of Master Details View in WinUI DataGrid

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.

WinUI DataGrid displays Master Details View with Indent Cells

<dataGrid:SfDataGrid x:Name="dataGrid"
                       AutoGenerateColumns="True"
                       AutoGenerateRelations="True"
                       GridLinesVisibility="Both"
                       ShowDetailsViewIndentCell="False"
                       ItemsSource="{Binding Employees}" />
dataGrid.ShowDetailsViewIndentCell= False;

WinUI DataGrid display Master Details View without Indentation

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.
Hide Expander of Master Details in WinUI DataGrid
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.

  1. DetailsViewDataGrid does not have GroupDropArea.
  2. DetailsViewDataGrid does not support AutoGenerateColumnsMode.ResetAll. Instead it works based on Reset.
  3. Master-Details View doesn’t support Data Virtualization.
  4. Master-Details View doesn’t support AllowFrozenGroupHeader.
  5. Master-Details View doesn’t support Freeze Pane.
  6. Master-Details View doesn’t support AutoRowHeight.
  7. For DetailsViewDataGrid, SelectionMode, SelectionUnit, NavigationMode, DetailsViewPadding properties are assigned from its parent grid only. So both parent DataGrid and DetailsViewDataGrid cannot have different values for these properties.