How to: Display and Configure Row Details in the DataGrid Control

Microsoft Silverlight will reach end of support after October 2021. Learn more.

Each row in the Silverlight DataGrid can be expanded to display a row details section. The row details section is defined by a DataTemplate that specifies the appearance of the section and the data to be displayed.

The row details section can be displayed for selected rows, displayed for all rows, or it can be collapsed. The row details section can also be frozen so that it does not scroll horizontally when the DataGrid is scrolled.

To display a row details section using inline XAML

  1. Create a DataTemplate that defines the appearance of the row details section.

    For more information, see How to: Customize Data Display with Data Templates.

  2. Place the DataTemplate inside the <DataGrid.RowDetailsTemplate> tags.

    <sdk:DataGrid x:Name="dataGrid1" Height="400" IsReadOnly="True" >
        <sdk:DataGrid.RowDetailsTemplate>
            <!-- Begin row details section. -->
            <DataTemplate>
                <Border BorderBrush="Black" BorderThickness="1" Background="Tan">
                    <StackPanel Orientation="Horizontal">
                        <StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <!-- Controls are bound to Task properties. -->
                                <TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding Name}" 
                                   Margin="0,0,10,0" VerticalAlignment="Bottom" />
                                <TextBlock FontSize="12" Text="Due Date: " VerticalAlignment="Bottom"/>
                                <sdk:DatePicker SelectedDate="{Binding DueDate, Mode=TwoWay}" VerticalAlignment="Bottom" />
                                <TextBlock FontSize="12" Text="    Complete:" VerticalAlignment="Bottom" />
                                <CheckBox IsChecked="{Binding Complete, Mode=TwoWay}" 
                                      VerticalAlignment="Center" />
                            </StackPanel>
                            <TextBlock FontSize="12" Text="Notes: " />
                            <TextBox FontSize="12" Text="{Binding Notes, Mode=TwoWay}" 
                                 Width="420" TextWrapping="Wrap"/>
                        </StackPanel>
                    </StackPanel>
                </Border>
            </DataTemplate>
            <!-- End row details section. -->
        </sdk:DataGrid.RowDetailsTemplate>
    </sdk:DataGrid>
    

To display a row details section using a DataTemplate resource

  1. Create a DataTemplate that defines the appearance of the row details section.

    For more information, see How to: Customize Data Display with Data Templates.

  2. Identify the DataTemplate by assigning a value to the x:Key Attribute.

  3. Bind the DataTemplate to the DataGrid's RowDetailsTemplate property.

    <data:DataGrid RowDetailsTemplate="{StaticResource templateReference}"/>
    

To change the visibility of a row details section

  • Set the RowDetailsVisibilityMode property to a value of the DataGridRowDetailsVisibilityMode enumeration.

    The following code example handles the SelectionChanged event of a ComboBox, and sets the RowDetailsVisibilityMode to the option selected in the ComboBox.

    ' Set the row details visibility to the option selected in the combo box.
    Private Sub cbRowDetailsVis_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
        Dim cb As ComboBox = sender
        Dim cbi As ComboBoxItem = cb.SelectedItem
        If Me.dataGrid1 IsNot Nothing Then
            If cbi.Content.ToString() = "Selected Row (Default)" Then
                Me.dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected
            ElseIf cbi.Content.ToString() = "None" Then
                Me.dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed
            ElseIf cbi.Content.ToString() = "All" Then
                Me.dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible
            End If
        End If
    End Sub
    
    // Set the row details visibility to the option selected in the combo box.
    private void cbRowDetailsVis_SelectionChanged(object sender, RoutedEventArgs e)
    {
        ComboBox cb = sender as ComboBox;
        ComboBoxItem cbi = cb.SelectedItem as ComboBoxItem;
        if (this.dataGrid1 != null)
        {
            if (cbi.Content.ToString() == "Selected Row (Default)")
                dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
            else if (cbi.Content.ToString() == "None")
                dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
            else if (cbi.Content.ToString() == "All")
                dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible;
        }
    }
    

To prevent a row details section from scrolling horizontally

Example

The following code example creates a List<T> of Task objects, and displays the task list in a DataGrid named dataGrid1. The DataGrid is read-only, but displays a row details section that allows editing. The row details section is bound to the same DataContext as the DataGridRow. In order for changes to the source data made in the row details section to be displayed in the DataGridRow, the Task class implements the INotifyPropertyChanged interface. A user interface is also provided for setting row details options.

Run this sample

<!-- NOTE: 
  By convention, the sdk prefix indicates a URI-based XAML namespace declaration 
  for Silverlight SDK client libraries. This namespace declaration is valid for 
  Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace 
  declarations for each CLR assembly and namespace combination outside the scope 
  of the default Silverlight XAML namespace. For more information, see the help 
  topic "Prefixes and Mappings for Silverlight Libraries". 
-->
<UserControl x:Class="DGRowDetails.Page"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <ScrollViewer VerticalScrollBarVisibility="Auto" BorderThickness="0" Padding="0">
        <StackPanel x:Name="LayoutRoot" Background="White" Margin="5">
            <StackPanel Margin="0,0,0,5">
                <StackPanel Orientation="Horizontal">
                    <Border BorderBrush="Black" BorderThickness="1" Padding="3" Width="180">
                        <StackPanel>
                            <TextBlock Text="Row Details Visibility" FontSize="12"/>
                            <ComboBox SelectionChanged="cbRowDetailsVis_SelectionChanged">
                                <ComboBoxItem Content="Selected Row (Default)" IsSelected="True" />
                                <ComboBoxItem Content="All"/>
                                <ComboBoxItem Content="None"/>
                            </ComboBox>
                            <CheckBox Content="Freeze Row Details" Margin="0,3,0,0"
                              Checked="cbFreezeRowDetails_Changed" 
                              Unchecked="cbFreezeRowDetails_Changed" />
                        </StackPanel>
                    </Border>
                </StackPanel>
            </StackPanel>
            <sdk:DataGrid x:Name="dataGrid1" Height="400" IsReadOnly="True" >
                <sdk:DataGrid.RowDetailsTemplate>
                    <!-- Begin row details section. -->
                    <DataTemplate>
                        <Border BorderBrush="Black" BorderThickness="1" Background="Tan">
                            <StackPanel Orientation="Horizontal">
                                <StackPanel>
                                    <StackPanel Orientation="Horizontal">
                                        <!-- Controls are bound to Task properties. -->
                                        <TextBlock FontSize="16" Foreground="MidnightBlue" Text="{Binding Name}" 
                                           Margin="0,0,10,0" VerticalAlignment="Bottom" />
                                        <TextBlock FontSize="12" Text="Due Date: " VerticalAlignment="Bottom"/>
                                        <sdk:DatePicker SelectedDate="{Binding DueDate, Mode=TwoWay}" VerticalAlignment="Bottom" />
                                        <TextBlock FontSize="12" Text="    Complete:" VerticalAlignment="Bottom" />
                                        <CheckBox IsChecked="{Binding Complete, Mode=TwoWay}" 
                                              VerticalAlignment="Center" />
                                    </StackPanel>
                                    <TextBlock FontSize="12" Text="Notes: " />
                                    <TextBox FontSize="12" Text="{Binding Notes, Mode=TwoWay}" 
                                         Width="420" TextWrapping="Wrap"/>
                                </StackPanel>
                            </StackPanel>
                        </Border>
                    </DataTemplate>
                    <!-- End row details section. -->
                </sdk:DataGrid.RowDetailsTemplate>
            </sdk:DataGrid>
        </StackPanel>
    </ScrollViewer>
</UserControl>
Partial Public Class Page
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
        ' Create a list to store task data.
        Dim taskList As List(Of Task) = New List(Of Task)
        Dim itemsCount As Integer = 15

        ' Generate some task data and add it to the task list.
        For index = 1 To itemsCount
            taskList.Add(New Task() With _
                         {.Name = "Task " & index.ToString(), _
                           .DueDate = Date.Now.AddDays(index), _
                           .Complete = (index Mod 3 = 0), _
                           .Notes = "Task " & index.ToString() & " is due on " & Date.Now.AddDays(index) & ". Lorum ipsum..." _
                         })
        Next
        Me.dataGrid1.ItemsSource = taskList
    End Sub
    ' Set the row details visibility to the option selected in the combo box.
    Private Sub cbRowDetailsVis_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
        Dim cb As ComboBox = sender
        Dim cbi As ComboBoxItem = cb.SelectedItem
        If Me.dataGrid1 IsNot Nothing Then
            If cbi.Content.ToString() = "Selected Row (Default)" Then
                Me.dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected
            ElseIf cbi.Content.ToString() = "None" Then
                Me.dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed
            ElseIf cbi.Content.ToString() = "All" Then
                Me.dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible
            End If
        End If
    End Sub
    '  Freeze the row details if the check box is checked.
    Private Sub cbFreezeRowDetails_Changed(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim cb As CheckBox = sender

        If Me.dataGrid1 IsNot Nothing Then
            Me.dataGrid1.AreRowDetailsFrozen = cb.IsChecked
        End If
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace DGRowDetails
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            // Create a list to store task data.
            List<Task> taskList = new List<Task>();
            int itemsCount = 15;

            // Generate some task data and add it to the task list.
            for (int i = 1; i <= itemsCount; i++)
            {
                taskList.Add(new Task()
                {
                    Name = "Task " + i.ToString(),
                    DueDate = DateTime.Now.AddDays(i),
                    Complete = (i % 3 == 0),
                    Notes = "Task " + i.ToString() + " is due on "
                          + DateTime.Now.AddDays(i) + ". Lorum ipsum..."
                });
            }
            this.dataGrid1.ItemsSource = taskList;
        }

        // Set the row details visibility to the option selected in the combo box.
        private void cbRowDetailsVis_SelectionChanged(object sender, RoutedEventArgs e)
        {
            ComboBox cb = sender as ComboBox;
            ComboBoxItem cbi = cb.SelectedItem as ComboBoxItem;
            if (this.dataGrid1 != null)
            {
                if (cbi.Content.ToString() == "Selected Row (Default)")
                    dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
                else if (cbi.Content.ToString() == "None")
                    dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
                else if (cbi.Content.ToString() == "All")
                    dataGrid1.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible;
            }
        }
        // Freeze the row details if the check box is checked.
        private void cbFreezeRowDetails_Changed(object sender, RoutedEventArgs e)
        {
            CheckBox cb = sender as CheckBox;
            if (this.dataGrid1 != null)
                this.dataGrid1.AreRowDetailsFrozen = (bool)cb.IsChecked;
        }
    }
}
Imports System.ComponentModel

Public Class Task
    Implements System.ComponentModel.INotifyPropertyChanged
    ' The Task class implements INotifyPropertyChanged so that 
    ' the datagrid row will be notified of changes to the data
    ' that are made in the row details section.

    ' Private task data.
    Private m_Name As String
    Private m_DueDate As Date
    Private m_Complete As Boolean
    Private m_Notes As String

    ' Define the public properties.
    Property Name() As String
        Get
            Return Me.m_Name
        End Get
        Set(ByVal value As String)
            If Not (value = Me.m_Name) Then
                Me.m_Name = value
                NotifyPropertyChanged("Name")
            End If
        End Set
    End Property

    Property DueDate() As Date
        Get
            Return Me.m_DueDate
        End Get
        Set(ByVal value As Date)
            If Not (value = Me.m_DueDate) Then
                Me.m_DueDate = value
                NotifyPropertyChanged("DueDate")
            End If
        End Set
    End Property

    Property Complete() As Boolean
        Get
            Return Me.m_Complete
        End Get
        Set(ByVal value As Boolean)
            If Not (value = Me.m_Complete) Then
                Me.m_Complete = value
                NotifyPropertyChanged("Complete")
            End If
        End Set
    End Property

    Property Notes() As String
        Get
            Return Me.m_Notes
        End Get
        Set(ByVal value As String)
            If Not (value = Me.m_Notes) Then
                Me.m_Notes = value
                NotifyPropertyChanged("Notes")
            End If
        End Set
    End Property

    ' Implement INotifyPropertyChanged interface.
    Public Event PropertyChanged As PropertyChangedEventHandler _
        Implements INotifyPropertyChanged.PropertyChanged


    Private Sub NotifyPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class
using System;
using System.ComponentModel;

namespace DGRowDetails
{
    public class Task : System.ComponentModel.INotifyPropertyChanged
    {
        // The Task class implements INotifyPropertyChanged so that 
        // the datagrid row will be notified of changes to the data
        // that are made in the row details section.

        // Private task data.
        private string m_Name;
        private DateTime m_DueDate;
        private bool m_Complete;
        private string m_Notes;

        // Define the public properties.
        public string Name
        {
            get { return this.m_Name; }
            set
            {
                if (value != this.m_Name)
                {
                    this.m_Name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }

        public DateTime DueDate
        {
            get { return this.m_DueDate; }
            set
            {
                if (value != this.m_DueDate)
                {
                    this.m_DueDate = value;
                    NotifyPropertyChanged("DueDate");
                }
            }
        }

        public bool Complete
        {
            get { return this.m_Complete; }
            set
            {
                if (value != this.m_Complete)
                {
                    this.m_Complete = value;
                    NotifyPropertyChanged("Complete");
                }
            }
        }

        public string Notes
        {
            get { return this.m_Notes; }
            set
            {
                if (value != this.m_Notes)
                {
                    this.m_Notes = value;
                    NotifyPropertyChanged("Notes");
                }
            }
        }

        // Implement INotifyPropertyChanged interface.
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}