Click here to Skip to main content
6,305,776 members and growing! (15,268 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » Windows Presentation Foundation » General     Beginner License: The Code Project Open License (CPOL)

LINQ, group by and WPF Data Binding

By r.stropek

WPF Data Binding works great with LINQ! This article shows how to create a hierarchical result set by using LINQ's group by clause and how to consume it in WPF data binding.
C# (C# 1.0, C# 2.0, C# 3.0), Windows (WinXP, Vista), .NET (.NET 3.0, .NET 3.5), WPF, LINQ, Silverlight, Dev
Posted:14 Oct 2008
Views:9,579
Bookmarked:16 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
6 votes for this article.
Popularity: 3.50 Rating: 4.50 out of 5

1

2
1 vote, 16.7%
3
1 vote, 16.7%
4
4 votes, 66.7%
5

Introduction

LINQ introduced great things to the C# programming language, things that database developers have known for year. In some areas LINQ goes far beyond what has been known from SQL. One example is the group by clause. In SQL the result of a group operation returns a table - nothing else is possible since SQL does not know the notion of classes. In contrast LINQ's group by operation can create hierarchical result structures. This article shows how to write a LINQ query using group by and - even more important - demonstrates how you can consume the result in WPF using data binding.

The LINQ Query

In the sample we assume that we collect status events from programs running on a computer. For every event we could collect the process ID, a process description (e.g. the file name of the exe) and the event time. The following class acts as a container for events:

public class Event
{
  public int PID { get; set; }
  public string Desc { get; set; }
  public DateTime EventTime { get; set; }
}

The following line of code generates some demo data:

var data = new List<Event>() 
{
  new Event() { PID = 1, Desc="Process A", EventTime = DateTime.Now.AddHours(-1) },
  new Event() { PID = 1, Desc="Process A", EventTime = DateTime.Now.AddHours(-2) },
  new Event() { PID = 2, Desc="Process B", EventTime = DateTime.Now.AddHours(-3) },
  new Event() { PID = 2, Desc="Process B", EventTime = DateTime.Now.AddHours(-4) },
  new Event() { PID = 3, Desc="Process C", EventTime = DateTime.Now.AddHours(-5) }
};

As you can see the master data about the processes is stored multiple times (i.e. data structure is not in normal form). Our LINQ query should return a hierarchical result in which every process is included only once. Additionally for every process we want to have a collection of the corresponding events. The LINQ query solving this problem looks like this:

var result =
  from d in data
  group d by new { d.PID, d.Desc } into pg
  select new { Process = pg.Key, Items = pg };

Note how the group by clause is written and how the result (anonymous type) is built. The query groups the result by process ID and process description. Both fields together make up the composite group expression (new { d.PID, d.Desc }). pg is of type IGrouping<TKey, TElement>. TKey represents the group expression mentioned before. IGrouping implementens IEnumerable. Therefore pg can be used in the select clause to embed the list of corresponding Event objects for each group.

Note that the anonymous type for the result contains names for each column (Process = pg.Key, Items = pg). This is important because without the names data binding in WPF is much harder (in fact I do not know whether it is possible without names at all).

Here is how the result looks like in the Visual Studio debugger:

ResultInVsWatch.png

WPF Data Binding

In my example I want to represent the hierarchical result structure in the user interface, too. Therefore the following sample should create an expander control for each key. Inside the expander it should display a listbox with the event details for the corresponding key. Here is a screen shot of the result I want to achieve:

LinqGroupWpfScreen.png

Using data binding to create the expander controls is quite straight forward:

<Window x:Class="WpfApplication5.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1" Height="300" Width="550">
<Grid>
 <ItemsControl x:Name="TopLevelListBox" ItemsSource="{Binding}">
  <ItemsControl.ItemsPanel>
   <ItemsPanelTemplate>
    <StackPanel Orientation="Horizontal" />
   </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemTemplate>
   <DataTemplate>
    <Expander ExpandDirection="Down" Width="175">
     <Expander.Header>
      <StackPanel Orientation="Horizontal">
       <TextBlock Text="{Binding Path=Process.PID}" Margin="0,0,5,0"/>
       <TextBlock Text="{Binding Path=Process.Desc}" />
      </StackPanel>
     </Expander.Header>
    </Expander>
   </DataTemplate>
  </ItemsControl.ItemTemplate>
 </ItemsControl>
</Grid>
</Window>

As you can see I use a custom ItemsPanel to display the expander controls horizontally. The data template converts each result object into the expander control. To make data binding work we must not forget to set the data context for the ItemsControl:

var result =
  from d in data
  group d by new { d.PID, d.Desc } into pg
  select new { Process = pg.Key, Items = pg };
 
TopLevelListBox.DataContext = result;

Based on that we can use the hierarchical result generated by the LINQ query to insert the list of events per expander control without writing a single line of extra C# code:

<Window x:Class="WpfApplication5.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1" Height="300" Width="550">
<Grid>
 <ItemsControl x:Name="TopLevelListBox" ItemsSource="{Binding}">
  <ItemsControl.ItemsPanel>
   <ItemsPanelTemplate>
    <StackPanel Orientation="Horizontal" />
   </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemTemplate>
   <DataTemplate>
    <Expander ExpandDirection="Down" Width="175">
     <Expander.Header>
      <StackPanel Orientation="Horizontal">
       <TextBlock Text="{Binding Path=Process.PID}" Margin="0,0,5,0"/>
       <TextBlock Text="{Binding Path=Process.Desc}" />
      </StackPanel>
     </Expander.Header>
     <ListBox x:Name="SubListBox" ItemsSource="{Binding Path=Items}">
      <ListBox.ItemTemplate>
       <DataTemplate>
        <StackPanel Orientation="Horizontal">
         <TextBlock Text="{Binding Path=EventTime}" />
        </StackPanel>
       </DataTemplate>
      </ListBox.ItemTemplate>
     </ListBox>
    </Expander>
   </DataTemplate>
  </ItemsControl.ItemTemplate>
 </ItemsControl>
</Grid>
</Window>

Note that the listbox SubListBox is bould to Items. Items has been defined as a field in the result type of the LINQ query. It contains the list of events corresponding to each group key. By binding like this we can access the properties of Event inside the DataTemplate of the listbox.

Summary

In my opinion the important takeaways of this sample are:

  1. LINQ is a powerful tool that is not only useful in combination with databases. It makes it easier to handle in-memory object structure, too.
  2. LINQ query can create more complex result structures than SQL.
  3. WPF data binding works great with LINQ results even with hierarchical result structures.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

r.stropek


Member
Hi, my name is Rainer Stropek. I am living a small city named Traun in Austria. Since 1993 I have worked as a developer and IT consultant focusing on building database oriented solutions. After being a freelancer for more than six years I founded a small IT consulting company together with some partners in 1999. In 2007 my friend Karin and I decided that we wanted to build a business based on COTS (component off-the-shelf) software. As a result we founded "software architects". So today we work as IT consultants and software developers. If you want to know more about our companies or check out my blogs at http://www.software-architects.com and http://www.cubido.at (German) or take a look at my profile in XING (https://www.openbc.com/hp/Rainer_Stropek2/).

I graduated the Higher Technical School for MIS at Leonding (A) in 1993. After that I started to study MIS at the Johannes Kepler University Linz (A). Unfortunately I had to stop my study because at that time it was incompatible with my work. In 2005 I finally finished my BSc (Hons) in Computing at the University of Derby (UK). Currently I focus on IT consulting, development, training and giving speeches in the area of .NET and WPF, SQL Server and Data Warehousing.
Location: Austria Austria

Other popular Windows Presentation Foundation articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 2 of 2 (Total in Forum: 2) (Refresh)FirstPrevNext
QuestionLINQ Group By Pinmemberfedens13:08 2 Feb '09  
Generalgood PinmemberZajda8221:49 14 Oct '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 14 Oct 2008
Editor:
Copyright 2008 by r.stropek
Everything else Copyright © CodeProject, 1999-2009
Web10 | Advertise on the Code Project