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


In my last post, Use LINQ with WPF : Styles and DataTemplates in code, I showed how to use DataTemplates in code to show the results of a query in a ListBox.


 


Let’s make a reusable class called Browse which creates WPF content as a ListView from a LINQ query and generates columns and headers. It also uses DataTemplates to databind


 


The headers will sort the columns when clicked. Also, you can resize and change the order of columns by clicking/dragging.


 


(For a Windows Forms version of Browse (not WPF) , see Cool Linq Query)


 


 


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.


 


<Code Sample>                                                                                          


 


Imports System.Windows.Controls.Primitives  ‘ for Popup


 


Partial Public 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


        Me.Content = New Browse(Query, Me) ‘ the Browse will fill the entire window


        ‘Me.Content = New BrowseWithPopup(Query, Me)


    End Sub


End Class


 


 


Class Browse


    Inherits ListView


    Sub New(ByVal Query As Object, Optional ByVal Parent As Object = Nothing)


        Dim gv As New GridView


        Me.View = gv


        Me.ItemsSource = Query


        If Not Parent Is Nothing Then


            If Parent.GetType.BaseType Is GetType(Window) Then


                CType(Parent, Window).Title = “# items = “ + Me.Items.Count.ToString


 


            End If


        End If


        Me.AddHandler(GridViewColumnHeader.ClickEvent, New RoutedEventHandler(AddressOf HandleHeaderClick))


 


        For Each mem In From mbr In _


                        Query.GetType().GetInterface(GetType(IEnumerable(Of )).FullName) _


                            .GetGenericArguments()(0).GetMembers _


                        Where mbr.MemberType = Reflection.MemberTypes.Property


            Dim coltype = CType(mem, Reflection.PropertyInfo).PropertyType.Name


            Select Case coltype


                Case “Int32”, “String”


                    Dim gvc As New GridViewColumn


                    gvc.Header = mem.Name


                    gv.Columns.Add(gvc)


                    If coltype = “Int32” Then


                        gvc.Width = 80


                        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(mem.Name))


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


                                                factTb.SetValue(TextBlock.BackgroundProperty, Brushes.SpringGreen)


                        factSP.AppendChild(factTb)


 


                        ‘factTb = New FrameworkElementFactory(GetType(Button))


                        ‘factTb.SetValue(Button.ContentProperty, “hit me”)


                        ‘factSP.AppendChild(factTb)


                        gvc.CellTemplate = dt


                    Else


                        gvc.DisplayMemberBinding = New Binding(mem.Name)


                        gvc.Width = 180


 


                    End If


 


            End Select


        Next


        Dim MyListboxStyle As New Style(GetType(ListBoxItem))


 


        Dim tr = New Trigger


        tr.Property = ListBoxItem.IsSelectedProperty    ‘ when Selected


        tr.Value = True                                 ‘ is true


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


        MyListboxStyle.Triggers.Add(tr)


 


        tr = New Trigger


        tr.Property = ListBoxItem.IsMouseOverProperty   ‘ when mouseover


        tr.Value = True                                 ‘ is true


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


        tr.Setters.Add(New Setter(ListBoxItem.BackgroundProperty, Brushes.Aquamarine))  ‘ this trigger sets both fore and back properties


 


        MyListboxStyle.Setters.Add(New Setter(ListBoxItem.ForegroundProperty, Brushes.RoyalBlue))    ‘ set style for all items


        MyListboxStyle.Triggers.Add(tr)


        Me.ItemContainerStyle = MyListboxStyle


    End Sub


    Dim _Lastdir As System.ComponentModel.ListSortDirection = ComponentModel.ListSortDirection.Ascending


    Dim _LastHeaderClicked As GridViewColumnHeader = Nothing


    Sub HandleHeaderClick(ByVal sender As Object, ByVal e As RoutedEventArgs)


        If e.OriginalSource.GetType Is GetType(GridViewColumnHeader) Then


            Dim gvh = CType(e.OriginalSource, GridViewColumnHeader)


            Dim dir As System.ComponentModel.ListSortDirection = ComponentModel.ListSortDirection.Ascending


            If Not gvh Is Nothing AndAlso Not gvh.Column Is Nothing Then


                Dim hdr = gvh.Column.Header


                If gvh Is _LastHeaderClicked Then


                    If _Lastdir = ComponentModel.ListSortDirection.Ascending Then


                        dir = ComponentModel.ListSortDirection.Descending


                    End If


                End If


                Sort(hdr, dir)


                _LastHeaderClicked = gvh


                _Lastdir = dir


            End If


        End If


    End Sub


    Sub Sort(ByVal sortby As String, ByVal dir As System.ComponentModel.ListSortDirection)


        Me.Items.SortDescriptions.Clear()


        Dim sd = New System.ComponentModel.SortDescription(sortby, dir)


        Me.Items.SortDescriptions.Add(sd)


        Me.Items.Refresh()


    End Sub


End Class


 


 


</Code Sample>