How to raise an end-of-list reached event so you can add more items.
Step 1: Subclass the standard Listbox with the following code
- Imports System.Windows.Controls.Primitives
- Public ClassExtendedListBox
- InheritsListBox
- Protected _isBouncy As Boolean = False
- PrivatealreadyHookedScrollEvents As Boolean = False
- PublicSub New()
- AddHandlerMe.Loaded, NewRoutedEventHandler(AddressOfListBox_Loaded)
- EndSub
- PrivateSub ListBox_Loaded(sender As Object, e As RoutedEventArgs)
- Dimsb As ScrollBar = Nothing
- Dimsv As ScrollViewer = Nothing
- IfalreadyHookedScrollEvents Then
- Return
- EndIf
- alreadyHookedScrollEvents = True
- Me.[AddHandler](ExtendedListBox.ManipulationCompletedEvent, DirectCast(AddressOf LB_ManipulationCompleted, EventHandler(OfManipulationCompletedEventArgs)), True)
- sb = DirectCast(FindElementRecursive(Me, GetType(ScrollBar)), ScrollBar)
- sv = DirectCast(FindElementRecursive(Me, GetType(ScrollViewer)), ScrollViewer)
- Ifsv IsNot Nothing Then
- ‘ Visual States are always on the first child of the control template
- Dim element As FrameworkElement = TryCast(VisualTreeHelper.GetChild(sv, 0), FrameworkElement)
- If element IsNot Nothing Then
- Dimvgroup As VisualStateGroup = FindVisualState(element, "VerticalCompression")
- Dimhgroup As VisualStateGroup = FindVisualState(element, "HorizontalCompression")
- Ifvgroup IsNot Nothing Then
- AddHandlervgroup.CurrentStateChanging, NewEventHandler(OfVisualStateChangedEventArgs)(AddressOfvgroup_CurrentStateChanging)
- EndIf
- Ifhgroup IsNot Nothing Then
- AddHandlerhgroup.CurrentStateChanging, NewEventHandler(OfVisualStateChangedEventArgs)(AddressOfhgroup_CurrentStateChanging)
- EndIf
- EndIf
- EndIf
- EndSub
- PublicDelegate Sub OnCompression(sender As Object, e AsCompressionEventArgs)
- PublicEvent Compression As OnCompression
- PrivateSub hgroup_CurrentStateChanging(sender AsObject, e AsVisualStateChangedEventArgs)
- If e.NewState.Name = "CompressionLeft" Then
- _isBouncy = True
- RaiseEvent Compression(Me, NewCompressionEventArgs(CompressionType.Left))
- EndIf
- If e.NewState.Name = "CompressionRight" Then
- _isBouncy = True
- RaiseEvent Compression(Me, NewCompressionEventArgs(CompressionType.Right))
- EndIf
- If e.NewState.Name = "NoHorizontalCompression" Then
- _isBouncy = False
- EndIf
- EndSub
- PrivateSub vgroup_CurrentStateChanging(sender AsObject, e AsVisualStateChangedEventArgs)
- If e.NewState.Name = "CompressionTop" Then
- _isBouncy = True
- RaiseEvent Compression(Me, NewCompressionEventArgs(CompressionType.Top))
- EndIf
- If e.NewState.Name = "CompressionBottom" Then
- _isBouncy = True
- RaiseEvent Compression(Me, NewCompressionEventArgs(CompressionType.Bottom))
- EndIf
- If e.NewState.Name = "NoVerticalCompression" Then
- _isBouncy = False
- EndIf
- EndSub
- PrivateSub LB_ManipulationCompleted(sender AsObject, e AsManipulationCompletedEventArgs)
- If _isBouncy Then
- _isBouncy = False
- EndIf
- EndSub
- PrivateFunction FindElementRecursive(parent AsFrameworkElement, targetType As Type) As UIElement
- DimchildCount As Integer = VisualTreeHelper.GetChildrenCount(parent)
- DimreturnElement As UIElement = Nothing
- IfchildCount > 0 Then
- For i As Integer = 0 To childCount – 1
- Dim element As [Object] = VisualTreeHelper.GetChild(parent, i)
- ‘If element.[GetType]() = targetType Then
- ‘If element.GetType.FullName = targetType.FullName Then
- If element.GetType.Equals(targetType) Then
- ReturnTryCast(element, UIElement)
- Else
- returnElement = FindElementRecursive(TryCast(VisualTreeHelper.GetChild(parent, i), FrameworkElement), targetType)
- EndIf
- Next
- EndIf
- ReturnreturnElement
- EndFunction
- PrivateFunction FindVisualState(element AsFrameworkElement, name As String) As VisualStateGroup
- If element Is Nothing Then
- ReturnNothing
- EndIf
- Dim groups As IList(OfVisualStateGroup) = VisualStateManager.GetVisualStateGroups(element)
- ForEach group As VisualStateGroupIn groups
- If group.Name = name Then
- Return group
- EndIf
- Next
- ReturnNothing
- EndFunction
- End Class
- Public ClassCompressionEventArgs
- InheritsEventArgs
- PublicProperty Type() As CompressionType
- Get
- Return m_Type
- EndGet
- ProtectedSet(value AsCompressionType)
- m_Type = value
- EndSet
- EndProperty
- Private m_Type As CompressionType
- PublicSub New(type__1 AsCompressionType)
- Type = type__1
- EndSub
- End Class
- Public EnumCompressionType
- Top
- Bottom
- Left
- Right
- End Enum
Step 2: Add the following XAML to the Application.Resources section of App.xaml
- <StyleTargetType="ScrollViewer">
- <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
- <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
- <Setter Property="Background" Value="Transparent"/>
- <Setter Property="Padding" Value="0"/>
- <Setter Property="BorderThickness" Value="0"/>
- <Setter Property="BorderBrush" Value="Transparent"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplateTargetType="ScrollViewer">
- <BorderBorderBrush="{TemplateBindingBorderBrush}"BorderThickness="{TemplateBindingBorderThickness}" Background="{TemplateBinding Background}">
- <VisualStateManager.VisualStateGroups>
- <VisualStateGroup x:Name="ScrollStates">
- <VisualStateGroup.Transitions>
- <VisualTransitionGeneratedDuration="00:00:00.5"/>
- </VisualStateGroup.Transitions>
- <VisualState x:Name="Scrolling">
- <Storyboard>
- <DoubleAnimation Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
- <DoubleAnimation Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
- </Storyboard>
- </VisualState>
- <VisualState x:Name="NotScrolling"/>
- </VisualStateGroup>
- <VisualStateGroup x:Name="VerticalCompression">
- <VisualState x:Name="NoVerticalCompression"/>
- <VisualState x:Name="CompressionTop"/>
- <VisualState x:Name="CompressionBottom"/>
- </VisualStateGroup>
- <VisualStateGroup x:Name="HorizontalCompression">
- <VisualState x:Name="NoHorizontalCompression"/>
- <VisualState x:Name="CompressionLeft"/>
- <VisualState x:Name="CompressionRight"/>
- </VisualStateGroup>
- </VisualStateManager.VisualStateGroups>
- <Grid Margin="{TemplateBinding Padding}">
- <ScrollContentPresenter x:Name="ScrollContentPresenter" Content="{TemplateBinding Content}"ContentTemplate="{TemplateBindingContentTemplate}"/>
- <ScrollBar x:Name="VerticalScrollBar"IsHitTestVisible="False" Height="Auto" Width="5"HorizontalAlignment="Right"VerticalAlignment="Stretch" Visibility="{TemplateBindingComputedVerticalScrollBarVisibility}"IsTabStop="False" Maximum="{TemplateBindingScrollableHeight}" Minimum="0" Value="{TemplateBindingVerticalOffset}" Orientation="Vertical"ViewportSize="{TemplateBindingViewportHeight}" />
- <ScrollBar x:Name="HorizontalScrollBar"IsHitTestVisible="False" Width="Auto" Height="5"HorizontalAlignment="Stretch"VerticalAlignment="Bottom" Visibility="{TemplateBindingComputedHorizontalScrollBarVisibility}"IsTabStop="False" Maximum="{TemplateBindingScrollableWidth}" Minimum="0" Value="{TemplateBindingHorizontalOffset}" Orientation="Horizontal"ViewportSize="{TemplateBindingViewportWidth}" />
- </Grid>
- </Border>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Style>
You can now simply replace your ListBox references with ExtendedListBox references in your code wherever a listbox is used.
- <local:ExtendedListBox x:Name="lbMyList"VerticalAlignment="Top"ItemsSource="{Binding}">
You might handle list-end / top scroll events in your code-behind as follows:
- Private SublbMyList_Compression(sender As Object, e As CompressionEventArgs) HandleslbMyList.Compression
- DimCompressionArgs = CType(e, CompressionEventArgs)
- SelectCase CompressionArgs.Type
- CaseCompressionType.Bottom ‘Bottom reached… add more list items
- …
- CaseCompressionType.Top ‘At top fetch and add earlier list items
- …
- EndSelect
- End Sub
Cheers.
Adapted from C# to VB, and for completeness and accuracy, from:
- Windows Phone Mango change, Listbox: How to detect compression(end of scroll) states ?http://blogs.msdn.com/b/slmperf/archive/2011/06/30/windows-phone-mango-change-listbox-how-to-detect-compression-end-of-scroll-states.aspx
- ExtendedListBox: custom WP7 Listbox control which detects end of scroll, allows infinite scrolling
http://nuncaalaprimera.com/2012/01/extendedlistbox-custom-wp7-listbox-control-which-detects-end-of-scroll-allows-infinite-scrolling/