How can I create a Complex View Like this one in WPF DataGridView C#?

Mesh Ka 200 Reputation points
2023-09-20T09:04:32.94+00:00

I created this Example and Tried to make it as Simple as Possible so that you can Understand Clearly.

I have SQL Server Database Named EmployeesDB that contains 4 Tables as following:

1.       Employees Table which is the Parent Table in this Example.

2.       EmployeesContacts Table Which is the Child Table of Employees holding employee Telephone Numbers because most of the Employees have more than One Telephone Numbers.

3.       EmployeesLanguages Table Which is the Child Table of Employees holding The Languages of the Employees and their Ability to Talk, Read and Write in those languages.

4.       EmployeesDuties Table Which is the Child Table of Employees holding the Duties of all the Employees in the Company.

DatabaseDiagram

 Here is the Database Script:

USE [EmployeesDB]
GO

/****** Object:  Table [dbo].[Employees] ******/

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Employees](
	[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
	[FirstName] [nvarchar](50) NULL,
	[LastName] [nvarchar](50) NULL,
	[Age] [int] NULL,
	[Gender] [nvarchar](10) NULL,
	[Email] [nvarchar](200) NULL,
 CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED 
(
	[EmployeeID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

/****** Object:  Table [dbo].[EmployeesContacts]  ******/

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[EmployeesContacts](
	[EmployeeID] [int] NOT NULL,
	[Telephone] [nvarchar](12) NOT NULL,
 CONSTRAINT [PK_EmployeesContacts] PRIMARY KEY CLUSTERED 
(
	[Telephone] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

/****** Object:  Table [dbo].[EmployeesDuties] ******/

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[EmployeesDuties](
	[EmployeeID] [int] NOT NULL,
	[EmployeeDutyID] [int] IDENTITY(1,1) NOT NULL,
	[Duty] [nvarchar](20) NOT NULL,
 CONSTRAINT [PK_EmployeesDuties] PRIMARY KEY CLUSTERED 
(
	[EmployeeDutyID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

/****** Object:  Table [dbo].[EmployeesLanguages] ******/

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[EmployeesLanguages](
	[EmployeeID] [int] NOT NULL,
	[EmployeeLanguageID] [int] IDENTITY(1,1) NOT NULL,
	[LanguageName] [nvarchar](50) NULL,
	[TalkingAbility] [nvarchar](20) NULL,
	[ReadingAbility] [nvarchar](20) NULL,
	[WritingAbility] [nvarchar](20) NULL,
 CONSTRAINT [PK_EmployeesLanguages] PRIMARY KEY CLUSTERED 
(
	[EmployeeLanguageID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[EmployeesContacts]  WITH CHECK ADD  CONSTRAINT [FK_EmployeesContacts_Employees] FOREIGN KEY([EmployeeID])
REFERENCES [dbo].[Employees] ([EmployeeID])
GO
ALTER TABLE [dbo].[EmployeesContacts] CHECK CONSTRAINT [FK_EmployeesContacts_Employees]
GO
ALTER TABLE [dbo].[EmployeesDuties]  WITH CHECK ADD  CONSTRAINT [FK_EmployeesDuties_Employees] FOREIGN KEY([EmployeeID])
REFERENCES [dbo].[Employees] ([EmployeeID])
GO
ALTER TABLE [dbo].[EmployeesDuties] CHECK CONSTRAINT [FK_EmployeesDuties_Employees]
GO
ALTER TABLE [dbo].[EmployeesLanguages]  WITH CHECK ADD  CONSTRAINT [FK_EmployeesLanguages_Employees] FOREIGN KEY([EmployeeID])
REFERENCES [dbo].[Employees] ([EmployeeID])
GO
ALTER TABLE [dbo].[EmployeesLanguages] CHECK CONSTRAINT [FK_EmployeesLanguages_Employees]
GO

 

The Impossible thing to me Came when I turned to the UI of which I wanted the whole Data to appear in One DataGridView so that I take Advantages of the Features of DataGridView like Filter, Sort, Search  e.t.c.

 Here is an Example of the View I Wanted which All The Rows are in Expanded Mode:

Expanded

ColumnDetails

 

The Behaviours and Features  I wanted are:

1.       When the Rows have Sub-Rows inside (like an Employee with Many Telephone Numbers) :

a)       To show this row header CollapsedIcon

(Row Collapsed Icon) at the Left Side when there is no Mouse Over and Expand All ToggleButton is false as in the Picture Below (Row 1, 2).

b)      To Show this row header ExpandedIcon

 (Row Expanded Icon) at the Left Side when they got Mouse Over or when Expand All ToggleButton is True as in the Picture Below (Row 3).

c)       To Show this row header NoSubRowsIcon

 (Row Has no Sub-Rows Icon) at the Left Side when the Rows doesn’t have Sub-Rows as in the Picture Below (Row 3, 4).

MouseOver

 

2.       To Show “Click To Add New Row” at the End of each Sub-Rows and at The End of Parent Rows like in the pictures above.

3.       When I Filter for Example Employees by Duty “Advertiser”, the Filter to Show full Data with All Sub-Rows like in the Picture below (Filtered by Duties Column).

Filtered

4.       When the DataGridView is Filtered, Sorted or Searched, the Expand All ToggleButton to be True like in the picture above.

5.       The Parent Rows to Have a unique border like in the Picture above.

6.       The Empty Spaces where there are nothing written like Below FirstName and LastName to be Whitespace instead of Cells like in the picture above.

With all my efforts I searched google for anything similar and found Nothing, Tried SQL Server View but with no success … I will really appreciate any help in this question.

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,562 questions
Visual Studio
Visual Studio
A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.
4,119 questions
SQL Server
SQL Server
A family of Microsoft relational database management and analysis systems for e-commerce, line-of-business, and data warehousing solutions.
11,640 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
9,504 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Hui Liu-MSFT 23,601 Reputation points Microsoft Vendor
    2023-09-20T10:03:46.5433333+00:00

    Hi,@Mesh Ka.Welcome Microsoft Q&A.

    1.For sorting, filtering and grouping data in DataGrid, you could try to refer to How to: Group, sort, and filter data in the DataGrid control.

    2.For the problem of clicking to expand row details, you can try to use something about DataGrid.RowDetailsTemplate You can also refer to my solution in the thread Convert DataGrid.ItemsSource to DataTable.

    <DataGrid Name="dg" Width="300" AutoGenerateColumns="False">  
                <DataGrid.Columns>  
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}" />  
                    <DataGridTextColumn Header="Birthday" Binding="{Binding Birthday}" />  
                </DataGrid.Columns>  
                <DataGrid.RowDetailsTemplate>  
                    <DataTemplate>  
                        <TextBlock Text="{Binding Details}" Margin="10" />  
                    </DataTemplate>  
                </DataGrid.RowDetailsTemplate>  
            </DataGrid>  
    
    

    3.For the problem of expanding or shrinking the RowDetails of the DataGrid through Button, you can set the DataGrid.RowDetailsVisibilityMode binding property that change by button. For more details, see How to: Display and Configure Row Details in the DataGrid Control

    By the way, it is recommended that you only post one question per thread. It's best to ask specific questions.

    Update:

    Right-click Dependencies under the project name and select Manage Nuget Packages... and Search Microsoft.EntityFrameworkCore.SqlServer(7.0.11)

    sql:

    CREATE TABLE [dbo].[EmployeesContacts] (
        [TelephoneID] INT           IDENTITY (1, 1) NOT NULL,
        [EmployeeID]  INT           NOT NULL,
        [Telephone]   NVARCHAR (12) NOT NULL,
        CONSTRAINT [PK_EmployeesContacts] PRIMARY KEY CLUSTERED ([TelephoneID] ASC),
        CONSTRAINT [FK_EmployeesContacts_Employees] FOREIGN KEY ([EmployeeID]) REFERENCES [dbo].[Employees] ([EmployeeID])
    );
    
    

    xaml:

     <Window.Resources>
            <CollectionViewSource x:Key="CvsKey">
                <CollectionViewSource.GroupDescriptions>
                    <PropertyGroupDescription PropertyName="Employee"/>
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>
            
        </Window.Resources>
    
        <Grid>
            <DataGrid AutoGenerateColumns="False" x:Name="dataGrid1" ItemsSource="{Binding Employees}" IsReadOnly="True" RowDetailsVisibilityMode="Collapsed"
                              
                      CanUserAddRows="True">
    
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding EmployeeID}" Header="EmployeeID"/>
                    <DataGridTextColumn Binding="{Binding FirstName}" Header="FirstName"/>
                    <DataGridTextColumn Binding="{Binding LastName}" Header="LastName"/>
                    <DataGridTextColumn Binding="{Binding Age}" Header="Age"/>
                    <DataGridTextColumn Binding="{Binding Gender}" Header="Gender"/>
                    <DataGridTextColumn Binding="{Binding Email}" Header="Email"/>
                    <DataGridTemplateColumn Header="Contects"  Width="75">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <DataGrid ItemsSource="{Binding Contacts}" IsReadOnly="True" AutoGenerateColumns="False" HeadersVisibility="None">
                                    <DataGrid.Columns>
                                        <DataGridTemplateColumn Width="*">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding Telephone}"/>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                    </DataGrid.Columns>
                                </DataGrid>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="Duties"  Width="75">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <DataGrid ItemsSource="{Binding Duties}" IsReadOnly="True" AutoGenerateColumns="False" HeadersVisibility="None">
                                    <DataGrid.Columns>
                                        <DataGridTemplateColumn Width="*">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding Duty}"/>
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                    </DataGrid.Columns>
                                </DataGrid>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="Languages"  Width="75">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <DataGrid ItemsSource="{Binding Languages}" IsReadOnly="True" AutoGenerateColumns="False" HeadersVisibility="None">
                                    <DataGrid.Columns>
                                        <DataGridTemplateColumn Width="*">
                                            <DataGridTemplateColumn.CellTemplate>
                                                <DataTemplate>
                                                    <TextBlock Text="{Binding LanguageName}"/>
                                                    
                                                </DataTemplate>
                                            </DataGridTemplateColumn.CellTemplate>
                                        </DataGridTemplateColumn>
                                    </DataGrid.Columns>
                                </DataGrid>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
    
            </DataGrid>
        </Grid>
    
    
    

    Codebedhind:

    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Windows;
    using System.Windows.Input;
    
    namespace WpfApp1
    {
        public partial class MainWindow : Window
        {
            private EmployeeViewModel _viewModel;
    
            public MainWindow()
            {
                InitializeComponent();
                _viewModel = new EmployeeViewModel();
                DataContext = _viewModel;
            }
        }
    
        public class EmployeeViewModel
        {
            private readonly EmployeeDbContext _context;
    
            public EmployeeViewModel()
            {
    
    
                using (var context = new EmployeeDbContext())
                {
                    Employees = context.Employees
                           .Include(e => e.Duties)
                        .Include(e => e.Contacts)
    
                        .Include(e => e.Languages)
                        .ToList();
    
    
                }
    
            }
    
    
            public List<Employee> Employees { get; set; } = new List<Employee>();
    
            public void SaveChanges()
            {
                _context.SaveChanges();
            }
    
            // Add methods for CRUD operations as needed
        }
      
        public class EmployeeDbContext : DbContext
        {
            public DbSet<Employee> Employees { get; set; }
            public DbSet<EmployeesContact> EmployeesContacts { get; set; }
            public DbSet<EmployeesDuty> EmployeesDuties { get; set; }
            public DbSet<EmployeesLanguage> EmployeesLanguages { get; set; }
    
    
    
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                // Configure your database connection here
                optionsBuilder.UseSqlServer("Data Source=(localdb)\\ProjectModels;Initial Catalog=EmployeesDB;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False");
    
            }
        }
        public class Employee
        {
            public int EmployeeID { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Age { get; set; }
            public string Gender { get; set; }
            public string Email { get; set; }
    
            public ObservableCollection<EmployeesContact> Contacts { get; set; }
            public ObservableCollection<EmployeesDuty> Duties { get; set; }
            public ObservableCollection<EmployeesLanguage> Languages { get; set; }
        }
    
        public class EmployeesContact
        {
            [Key] // Define TelephoneID as the primary key
            public int TelephoneID { get; set; }
    
            public int EmployeeID { get; set; }
            public string Telephone { get; set; }
    
            // Other properties and relationships
        }
    
        public class EmployeesDuty
        {
            public int EmployeeID { get; set; }
    
            [Key]
            public int EmployeeDutyID { get; set; }
            public string Duty { get; set; }
    
        }
    
        public class EmployeesLanguage
        {
            public int EmployeeID { get; set; }
    
            [Key]
            public int EmployeeLanguageID { get; set; }
            public string LanguageName { get; set; }
            public string TalkingAbility { get; set; }
            public string ReadingAbility { get; set; }
            public string WritingAbility { get; set; }
    
        }
    
    
    }
    

    The result:

    User's image


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.