Monday, May 20, 2013

WPF -DataGrid RowDetailsTemplate in WPF

RowDetailsTemplate is a great feature that to view details information about a bound row in a DataGrid on demand so that the details portion is displayed in place within the DataGrid.

The DataGrid.RowDetailsTemplate property accepts a data template  that can be used to display additional data in place, associated with a bound row. This feature comes handy in many scenarios—to provide master-detail data where multiple detail records need to be displayed for a top-level row or where additional information, not otherwise bound to top-level columns, needs to be displayed adjacent to a row.

The DataGrid.RowDetailsVisibilityMode property controls the visibility of the row details information at DataGrid scope. That is, setting it to Visible keeps it always visible for every bound row, whereas setting it to Visible When Selected makes the details portion of a row visible when the row is selected and collapsed back when selection moves off to another row. To control row details visibility in code, set this property to Collapsed, which hides row details for every row, and instead use the DataGridRow.DetailsVisibility property on the individual row.

Let See how to use RowDetailsTemplate in DataGrid.


Step 1
Download Northwind database from the following link.
http://www.microsoft.com/downloads/details.aspx?FamilyID=06616212-0356-46a0-8da2-eebc53a68034&displaylang=en

Step 2
Attach a Northwind database into MS-SQL server.

Step 3
Create a WPF Application and give solution name as SolDataGridRowDetailTemplate_ExpandCollapse_WPF.


Step 4
Create a New Folder in Solution and give the Folder Name as ORD,it is look like this



Click on Image for Better View

Step 5
Add a Linq to Sql class,Select the ORD folder,right click on Add new Item,select LINQ to SQL classes from installed Visual Studio templates and name it NorthwindDC and click on add button,it is look like this



Click on Image for Better View

Step 6

Open a O/R Designer by double click on NorthwindDC.dbml,it is look like this


Click on Image for Better View


Click on Image for Better View

Visual studio provides an object-relational mapping designer,called the O/R Designer which allows you to visually design the object to database mapping.

Step 7
Create a Employee object that will use LINQ to SQL to map to this table.go to the Server Explorer,select Northwind database,go to the Tables and select Employees table,it is look like this


Click on Image for Better View

Drag and drop Employees table from Server explorer onto the design surface of the O/R Designer,it is look like this


Click on Image for Better View

Step 8
Create a Employee Static Class for retrieving data from Employee Table using Linq or Lambda Expression,it is look like this
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SolDataGridRowDetailTemplate_ExpandCollapse_WPF
{
    public static class Employee
    {
        #region Methods
        /// <summary>
        /// Get Emplyee Data From Table
        /// </summary>
        /// <returns>List</returns>
        public static List<ORD.Employee> GetEmployeeData()
        {
            try
            {
                ORD.NorthwindDCDataContext DC = new ORD.NorthwindDCDataContext();

                // Using Linq 

                //var Query =( from Q in DC.Employees
                //            select new ORD.Employee
                //            {
                //                FirstName = Q.FirstName,
                //                LastName = Q.LastName,
                //                City = Q.City,
                //                Notes = Q.Notes
                //            }).ToList<ORD.Employee>();

                // Using Lambda Expression

                var Query = DC.Employees.Select(LE => new ORD.Employee() { FirstName=LE.FirstName,LastName=LE.LastName,City=LE.City,Notes=LE.Notes }).ToList<ORD.Employee>();

                return Query;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message); 
            }
        }

        #endregion
    }
}

Step 9
Now add DataGrid Control on window,it is look like this
<Grid>
        <DataGrid x:Name="dgEmployee" Grid.Row="0" Grid.Column="0" AutoGenerateColumns="False" IsReadOnly="True" CanUserAddRows="False" ItemsSource="{Binding}" RowDetailsVisibilityMode="Collapsed" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
           

        </DataGrid>
    </Grid>

Note: In the DataGrid set the RowDetailsVisibilityMode to collapse. This will ensure that row is not expanded when user clicks on the row.

Step 10
Select DataGrid and Add DataGridTemplateColumn in DataGrid where we add Button control in Column to expand or collapse the DetaileRow.,it is look like this
<Grid>
        <DataGrid x:Name="dgEmployee" Grid.Row="0" Grid.Column="0" AutoGenerateColumns="False" IsReadOnly="True" CanUserAddRows="False" ItemsSource="{Binding}" RowDetailsVisibilityMode="Collapsed" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button x:Name="btnExpandCollapse" Content="+" Click="btnExpandCollapse_Click_1"></Button>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>


Click on Image for Better View

Step 11
Select DataGrid Control and Add three DataGridTextColumn in DataGrid where we bind Employee details such as FirstName,LastName and City,it is look like this
<Grid>
        <DataGrid x:Name="dgEmployee" Grid.Row="0" Grid.Column="0" AutoGenerateColumns="False" IsReadOnly="True" CanUserAddRows="False" ItemsSource="{Binding}" RowDetailsVisibilityMode="Collapsed" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button x:Name="btnExpandCollapse" Content="+" Click="btnExpandCollapse_Click_1"></Button>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="FirstName" Binding="{Binding FirstName}"></DataGridTextColumn>
                <DataGridTextColumn Header="LastName" Binding="{Binding LastName}"></DataGridTextColumn>
                <DataGridTextColumn Header="City" Binding="{Binding City}"></DataGridTextColumn>
            </DataGrid.Columns>
            
        </DataGrid>
  </Grid>



Click on Image for Better View

Step 12
Now finally add RowDetailsTemplate in DataGrid to display addition data in place, associated with a bound row.it is look like this
<Grid>
        <DataGrid x:Name="dgEmployee" Grid.Row="0" Grid.Column="0" AutoGenerateColumns="False" IsReadOnly="True" CanUserAddRows="False" ItemsSource="{Binding}" RowDetailsVisibilityMode="Collapsed" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button x:Name="btnExpandCollapse" Content="+" Click="btnExpandCollapse_Click_1"></Button>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="FirstName" Binding="{Binding FirstName}"></DataGridTextColumn>
                <DataGridTextColumn Header="LastName" Binding="{Binding LastName}"></DataGridTextColumn>
                <DataGridTextColumn Header="City" Binding="{Binding City}"></DataGridTextColumn>
            </DataGrid.Columns>
            <DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Notes}" Width="400" HorizontalAlignment="Left" TextWrapping="Wrap"></TextBlock>
                </DataTemplate>
            </DataGrid.RowDetailsTemplate>
        </DataGrid>
    </Grid>


Click on Image for Better View

Full Code of XAML
<Window x:Class="SolDataGridRowDetailTemplate_ExpandCollapse_WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DataGrid RowDetailsTemplate" Height="350" Width="525" Loaded="Window_Loaded_1">
    <Grid>
        <DataGrid x:Name="dgEmployee" Grid.Row="0" Grid.Column="0" AutoGenerateColumns="False" IsReadOnly="True" CanUserAddRows="False" ItemsSource="{Binding}" RowDetailsVisibilityMode="Collapsed" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button x:Name="btnExpandCollapse" Content="+" Click="btnExpandCollapse_Click_1"></Button>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="FirstName" Binding="{Binding FirstName}"></DataGridTextColumn>
                <DataGridTextColumn Header="LastName" Binding="{Binding LastName}"></DataGridTextColumn>
                <DataGridTextColumn Header="City" Binding="{Binding City}"></DataGridTextColumn>
            </DataGrid.Columns>
            <DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Notes}" Width="400" HorizontalAlignment="Left" TextWrapping="Wrap"></TextBlock>
                </DataTemplate>
            </DataGrid.RowDetailsTemplate>
        </DataGrid>
    </Grid>
</Window>

Step 13
Now Bind the data in DataGrid Control in Code Behind on Window Load Event,it is look like this
#region Methods

        /// <summary>
        /// Bind Employee Data to DataGrid
        /// </summary>
        private void BindEmployeeData()
        {
            try
            {
                //Get Employee List
                List<ORD.Employee> ListEmployeeobj = Employee.GetEmployeeData();

                // Check the List is Null or Not
                if (ListEmployeeobj != null)
                {
                    // Check the List Count is grether than 0
                    if (ListEmployeeobj.Count >0)
                    {
                        // Bind List to DataGrid
                        dgEmployee.DataContext = ListEmployeeobj;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message); 
            }
        }

        #endregion

        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {
            try
            {
                // Call bind Employee Data Function
                BindEmployeeData();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message); 
            }
        }

Step 14
On Button ExapndCollapse Click event,add the following code,it is look like this 
private void btnExpandCollapse_Click_1(object sender, RoutedEventArgs e)
        {
            try
            {
                // Get the Selected Row Button Object
                Button ExpandCollapseObj = (Button)sender;

                // Check the Button Object is null or Not
                if (ExpandCollapseObj != null)
                {
                    // Return the Contains which specified element
                    DataGridRow DgrSelectedRowObj = DataGridRow.GetRowContainingElement(ExpandCollapseObj);

                    // Check the DataGridRow Object is Null or Not
                    if (DgrSelectedRowObj != null)
                    {
                        // if Button Content is "+" then Visible Row Details 
                        if (ExpandCollapseObj!=null && ExpandCollapseObj.Content.ToString() == "+")
                        {
                            DgrSelectedRowObj.DetailsVisibility = System.Windows.Visibility.Visible;
                            ExpandCollapseObj.Content = "-";
                        }
                        // else Collapsed row Details
                        else
                        {
                            DgrSelectedRowObj.DetailsVisibility = System.Windows.Visibility.Collapsed;
                            ExpandCollapseObj.Content = "+";
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message); 
            }
        }

Full Code 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SolDataGridRowDetailTemplate_ExpandCollapse_WPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


        #region Methods

        /// <summary>
        /// Bind Employee Data to DataGrid
        /// </summary>
        private void BindEmployeeData()
        {
            try
            {
                //Get Employee List
                List<ORD.Employee> ListEmployeeobj = Employee.GetEmployeeData();

                // Check the List is Null or Not
                if (ListEmployeeobj != null)
                {
                    // Check the List Count is grether than 0
                    if (ListEmployeeobj.Count >0)
                    {
                        // Bind List to DataGrid
                        dgEmployee.DataContext = ListEmployeeobj;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message); 
            }
        }

        #endregion

        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {
            try
            {
                // Call bind Employee Data Function
                BindEmployeeData();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message); 
            }
        }

        private void btnExpandCollapse_Click_1(object sender, RoutedEventArgs e)
        {
            try
            {
                // Get the Selected Row Button Object
                Button ExpandCollapseObj = (Button)sender;

                // Check the Button Object is null or Not
                if (ExpandCollapseObj != null)
                {
                    // Return the Contains which specified element
                    DataGridRow DgrSelectedRowObj = DataGridRow.GetRowContainingElement(ExpandCollapseObj);

                    // Check the DataGridRow Object is Null or Not
                    if (DgrSelectedRowObj != null)
                    {
                        // if Button Content is "+" then Visible Row Details 
                        if (ExpandCollapseObj!=null && ExpandCollapseObj.Content.ToString() == "+")
                        {
                            DgrSelectedRowObj.DetailsVisibility = System.Windows.Visibility.Visible;
                            ExpandCollapseObj.Content = "-";
                        }
                        // else Collapsed row Details
                        else
                        {
                            DgrSelectedRowObj.DetailsVisibility = System.Windows.Visibility.Collapsed;
                            ExpandCollapseObj.Content = "+";
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message); 
            }
        }

    }
}

Output



Click on Image for Better View

Download
Download Source Code

11 comments:

  1. hi, i have a problem with the example: when i reorder the rows clicking on a column heder the opened details become closed.

    ReplyDelete
  2. Hey my question is that when i expand the row how can i collapsed another expanded row.

    ReplyDelete