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


We can subclass the prior Windows Presentation Foundation (WPF) class called Browse that displays the results of a query. This subclass will add a WPF Popup to it.


 


The popup can easily be added to either a ListView or the ListBox sample I posted earlier (Use LINQ with WPF : Styles and DataTemplates in code)


 


The code sample below is exactly the same code as the prior Browse post except that it uses a subclass of the Browse class called BrowseWithPopup.


 


This class has a DispatcherTimer Class (System.Windows.Threading)  that delays the opening of the descriptive popup. The timer starts when an item is selected. If it’s been selected for, say 200 ms, then the popup displays. Thus, if the user is actively scrolling, no popup shows.


 


The System.Timers.Timer class runs in a different thread from the UI thread. WPF runs UI and rendering in different threads. See WPF Threading Model.


 


To add a movie to any UI element is quite simple. To make it repeat after playing once, you need to use the StoryBoard class to animate it. Try making it repeat forever!


 


 


 


 


<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


 


Class BrowseWithPopup


    Inherits Browse


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


        MyBase.new(Query)


        Dim sp As New StackPanel


        _lvPopUp.Child = sp


 


        _tbListViewItemDescription.Foreground = Brushes.Black


        _tbListViewItemDescription.Background = Brushes.LightYellow


        sp.Children.Add(_tbListViewItemDescription)


        ‘Return ‘ the rest of this method is fluff


 


        Dim btnTest As New Button   ‘ just for fun, let’s add a button to the popup tip.


        btnTest.Content = “foo”


        btnTest.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf HandleClick))


        sp.Children.Add(btnTest)


 


        Dim MyVid As New MediaElement ‘ For more fun, let’s add a movie to the popup tip


        MyVid.MaxHeight = 100


        MyVid.MaxWidth = 100


        MyVid.Source = New Uri(“d:\tshort.avi”) ‘ change to point to your movie file


        sp.Children.Add(MyVid)


    End Sub


    Sub HandleClick()


        MsgBox(“You Clicked me!”)


    End Sub


 


    Dim _lvPopUp As New Popup


    Dim WithEvents _lvTimer As New System.Windows.Threading.DispatcherTimer


    Dim _tbListViewItemDescription As New TextBlock


    Sub HandlePopupTimerTick() Handles _lvTimer.Tick


        Dim lbi As ListBoxItem = Me.ItemContainerGenerator.ContainerFromIndex(Me.SelectedIndex)


        If Not lbi Is Nothing Then


            _lvPopUp.PlacementTarget = lbi


            _tbListViewItemDescription.Text = “This is the data: “ + vbCrLf + lbi.Content.ToString


            _lvPopUp.IsOpen = 1


        End If


    End Sub


    Sub HandleRowSelected(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles Me.SelectionChanged


        Dim lb = CType(sender, ListBox)


        If Not lb Is Nothing AndAlso (lb.SelectedIndex >= 0) Then


            Dim lbi As ListBoxItem = lb.ItemContainerGenerator.ContainerFromIndex(lb.SelectedIndex)


            Dim lbi2 = lb.SelectedItem


            If _lvPopUp.IsOpen Then


                _lvPopUp.IsOpen = False


            End If


            _lvTimer.Stop()  ‘ stop prior timer, if any


            _lvTimer.Interval = TimeSpan.FromMilliseconds(500)


            _lvTimer.Start()


            _lvPopUp.AllowsTransparency = True


            _lvPopUp.PopupAnimation = PopupAnimation.Fade


            _lvPopUp.Placement = PlacementMode.Right


        End If


    End Sub


 


 


End Class


 


</Code Sample>


 


 

Comments (4)

  1. Using WPF, it’s great to use declarative XAML , but it’s also great to use dynamic code. With VB’s new

  2. Using WPF, it’s great to use declarative XAML , but it’s also great to use dynamic code. With VB’s new