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>