Welcome to MSDN Blogs Sign in | Join | Help

Use LINQ with WPF : Styles and DataTemplates in code

Using Windows Presentation Foundation (WPF) and LINQ is easy in code. We'll create a query and display it in a WPF ListBox

 

Using XAML is harder: the ObjectDataProvider class works with non-anonymous types.

 

Start Visual Studio 2008 (or 2005 with Visual Studio 2005 extensions for .NET Framework 3.0. Linq is only in 2008)

 

Choose File->New Project->Visual Basic->WPF Application

You can use the WPF Forms designer, or you can write your code in a program.

 

Double click the form designer to bring up the Window1.xaml.vb file. Replace the contents with the code below.

                                                                                                                 

 

Class Window1

 

    Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        Me.Width = 800

        Me.Height = 800

 

        Dim Query = From proc In System.Diagnostics.Process.GetProcesses _

                    Select proc.Id, proc.ProcessName, ThreadCount = proc.Threads.Count, proc.MainWindowTitle

        Dim MyListBox As New ListBox

        MyListBox.ItemsSource = Query

        '        lb.ItemsSource = New String() {"one", "two"}     ' or a simple array

        Me.Content = MyListBox

 

    End Sub

 

End Class

 

 

 

Hit F5, and now you have a ListBox on a form which displays multiple rows like so:

 

{ Id = 2312, ProcessName = notepad, ThreadCount =1, MainWindowTitle = t.txt – Notepad, }

 

Let's make  the display a little more friendly by using a DataTemplate.

 

Add this code before the End Sub:

 

        Dim dt As New DataTemplate

        Dim factSP = New FrameworkElementFactory(GetType(StackPanel))

        dt.VisualTree = factSP

        factSP.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal)

 

        Dim factTb = New FrameworkElementFactory(GetType(TextBlock))

        factTb.SetBinding(TextBlock.TextProperty, New Binding("Id"))

        factTb.SetValue(TextBlock.WidthProperty, 40.0)

        factTb.SetValue(TextBlock.FontWeightProperty, FontWeights.Bold)

        factSP.AppendChild(factTb)

 

        factTb = New FrameworkElementFactory(GetType(TextBlock))

        factTb.SetBinding(TextBlock.TextProperty, New Binding("ProcessName"))

        factTb.SetValue(TextBlock.FontStyleProperty, FontStyles.Italic)

        factTb.SetValue(TextBlock.WidthProperty, 80.0)

        factSP.AppendChild(factTb)

 

        factTb = New FrameworkElementFactory(GetType(TextBlock))

        factTb.SetBinding(TextBlock.TextProperty, New Binding("ThreadCount"))

        factTb.SetValue(TextBlock.WidthProperty, 30.0)

        factSP.AppendChild(factTb)

 

        factTb = New FrameworkElementFactory(GetType(TextBlock))

        factTb.SetBinding(TextBlock.TextProperty, New Binding("MainWindowTitle"))

        factSP.AppendChild(factTb)

 

        MyListBox.ItemTemplate = dt

 

 

 

This code uses a Data Template to map data names with UI elements.

Because there are multiple rows, the template declaratively specifies how the data is presented.

 

In this case, the multiple rows of the query mapped to multiple ListBoxItems in the ListBox. Each ListBoxItem displays as a factory generated Horizontal StackPanel.  The StackPanel contains a factory generated TextBlock for each field. WPF allows all sorts of controls inside other controls, so we could have a movie or even another listbox inside the listbox item.

Some of the TextBlocks have their width or FontStyle set. However, this code is hardcoded for the field names and data types of the query. Let's make it more generic. We can use refection to get the data types and field names of the anonymous type created by the query:

 

            Dim QueryAnonType = _

            Query.GetType().GetInterface(GetType(IEnumerable(Of )).FullName).GetGenericArguments()(0)

            For Each mem In QueryAnonType.GetMembers

                If mem.MemberType = Reflection.MemberTypes.Property Then

                    Select Case CType(mem, Reflection.PropertyInfo).PropertyType.Name

                        Case "Int32"

                            Dim factTb = New FrameworkElementFactory(GetType(TextBlock))

                            factTb.SetBinding(TextBlock.TextProperty, New Binding(mem.Name))

                            factTb.SetValue(TextBlock.WidthProperty, 40.0)

                            factTb.SetValue(TextBlock.FontWeightProperty, FontWeights.Bold)

                            factSP.AppendChild(factTb)

                        Case "String"

                            Dim factTb = New FrameworkElementFactory(GetType(TextBlock))

                            factTb.SetBinding(TextBlock.TextProperty, New Binding(mem.Name))

                            factTb.SetValue(TextBlock.WidthProperty, 130.0)

                            factTb.SetValue(TextBlock.FontStyleProperty, FontStyles.Italic)

                            factTb.SetValue(TextBlock.ForegroundProperty, Brushes.CornflowerBlue)

                            factSP.AppendChild(factTb)

                    End Select

                End If

 

            Next

 

Putting it all together, adding some style triggers for fun, the entire code sample is below.

 

Next time, we'll add headers that sort the columns when clicked and some

 

<Code Sample>

 

Class Window1

 

    Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        Me.Width = 800

        Me.Height = 800

 

        Dim Query = From proc In System.Diagnostics.Process.GetProcesses _

                    Select proc.Id, proc.ProcessName, ThreadCount = proc.Threads.Count, proc.MainWindowTitle

        Dim MyListBox As New ListBox

        MyListBox.ItemsSource = Query

        '        lb.ItemsSource = New String() {"one", "two"}     ' or a simple array

        Me.Content = MyListBox

 

        Dim dt As New DataTemplate

        Dim factSP = New FrameworkElementFactory(GetType(StackPanel))

        dt.VisualTree = factSP

        factSP.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal)

        Dim QueryAnonType = _

            Query.GetType().GetInterface(GetType(IEnumerable(Of )).FullName).GetGenericArguments()(0)

        For Each mem In QueryAnonType.GetMembers

            If mem.MemberType = Reflection.MemberTypes.Property Then

                Select Case CType(mem, Reflection.PropertyInfo).PropertyType.Name

                    Case "Int32"

                        Dim factTb = New FrameworkElementFactory(GetType(TextBlock))

                        factTb.SetBinding(TextBlock.TextProperty, New Binding(mem.Name))

                        factTb.SetValue(TextBlock.WidthProperty, 40.0)

                        factTb.SetValue(TextBlock.FontWeightProperty, FontWeights.Bold)

                        factSP.AppendChild(factTb)

                    Case "String"

                        Dim factTb = New FrameworkElementFactory(GetType(TextBlock))

                        factTb.SetBinding(TextBlock.TextProperty, New Binding(mem.Name))

                        factTb.SetValue(TextBlock.WidthProperty, 130.0)

                        factTb.SetValue(TextBlock.FontStyleProperty, FontStyles.Italic)

                        factTb.SetValue(TextBlock.ForegroundProperty, Brushes.CornflowerBlue)

                        factSP.AppendChild(factTb)

                End Select

            End If

 

        Next

 

        MyListBox.ItemTemplate = dt

 

        Dim MyListboxStyle As New Style(GetType(ListBoxItem))

 

        Dim tr = New Trigger

        tr.Property = ListBoxItem.IsSelectedProperty

        tr.Value = True

        tr.Setters.Add(New Setter(ListBoxItem.ForegroundProperty, Brushes.Bisque))

        MyListboxStyle.Triggers.Add(tr)

 

        tr = New Trigger

        tr.Property = ListBoxItem.IsMouseOverProperty

        tr.Value = True

        tr.Setters.Add(New Setter(ListBoxItem.ForegroundProperty, Brushes.Red))

 

        tr = New Trigger

        tr.Property = ListBoxItem.IsMouseOverProperty

        tr.Value = True

        tr.Setters.Add(New Setter(ListBoxItem.BackgroundProperty, Brushes.Aquamarine))

 

        MyListboxStyle.Setters.Add(New Setter(ListBoxItem.ForegroundProperty, Brushes.Black))

        MyListboxStyle.Triggers.Add(tr)

 

        MyListBox.ItemContainerStyle = MyListboxStyle

 

    End Sub

End Class

 

 

</Code Sample>

 

Published Monday, November 12, 2007 10:28 AM by Calvin_Hsia
Filed under: , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# MSDN Blog Postings &raquo; Use LINQ with WPF : Styles and DataTemplates in code

# re: Use LINQ with WPF : Styles and DataTemplates in code

Tuesday, November 13, 2007 10:13 AM by Jeff T

You need to add the triggers for the MouseOver properties to MyListBoxStyle:

tr = New Trigger

tr.Property = ListBoxItem.IsMouseOverProperty

tr.Value = True

tr.Setters.Add(New Setter(ListBoxItem.ForegroundProperty, Brushes.Red))

MyListboxStyle.Triggers.Add(tr)

tr = New Trigger

tr.Property = ListBoxItem.IsMouseOverProperty

tr.Value = True

MyListboxStyle.Triggers.Add(tr)

# Use DataTemplates and WPF in code to create a general purpose LINQ Query results display

Thursday, November 15, 2007 4:31 PM by Calvin Hsia's WebLog

In my last post, Use LINQ with WPF : Styles and DataTemplates in code , I showed how to use DataTemplates

# re: Use LINQ with WPF : Styles and DataTemplates in code

Thursday, November 15, 2007 4:37 PM by Calvin_Hsia

Thank Jeff T: yes somehow an unimportant line got left out of this post. The correct code is in the next post, which shows setting both fore and back properties in 1 Setter.

# MSDN Blog Postings &raquo; Use DataTemplates and WPF in code to create a general purpose LINQ Query results display

# MSDN Blog Postings &raquo; Use DataTemplates and WPF in code to create a general purpose LINQ Query results display

# MSDN Blog Postings &raquo; Use DataTemplates and WPF in code to create a general purpose LINQ Query results display

# MSDN Blog Postings &raquo; Use DataTemplates and WPF in code to create a general purpose LINQ Query results display

# Use the DispatcherTimer, Popup and even a movie to enhance your data presentation

Wednesday, November 21, 2007 4:51 PM by Calvin Hsia's WebLog

We can subclass the prior Windows Presentation Foundation (WPF) class called Browse that displays the

# Use the DispatcherTimer, Popup and even a movie to enhance your data presentation

Wednesday, November 21, 2007 5:26 PM by Noticias externas

We can subclass the prior Windows Presentation Foundation (WPF) class called Browse that displays the

# re: Use LINQ with WPF : Styles and DataTemplates in code

Tuesday, October 21, 2008 5:13 PM by clue

Thanks for the code. It helped me a lot. ;)

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker