@@ -14,6 +14,7 @@ file sealed record ItemsViewVerticalScrollControllerElement(
1414 Element Child ,
1515 ElementRef < WinUIAnnotatedScrollBar > ScrollBarRef ,
1616 int InitialTailIndex ,
17+ int ItemCount ,
1718 string InitialTailRequestKey ,
1819 string ? DisplayedTailKey ) : Element
1920{
@@ -41,6 +42,7 @@ public UIElement Mount(MountContext context, ItemsViewVerticalScrollControllerEl
4142 Positioners . Add ( itemsView , positioner ) ;
4243 positioner . Request (
4344 element . InitialTailIndex ,
45+ element . ItemCount ,
4446 element . InitialTailRequestKey ,
4547 element . DisplayedTailKey ) ;
4648 return itemsView ;
@@ -59,11 +61,13 @@ public UIElement Update(
5961 && Positioners . TryGetValue ( itemsView , out var positioner ) )
6062 positioner . Request (
6163 newElement . InitialTailIndex ,
64+ newElement . ItemCount ,
6265 newElement . InitialTailRequestKey ,
6366 newElement . DisplayedTailKey ) ;
6467 else if ( Positioners . TryGetValue ( itemsView , out var existingPositioner ) )
6568 existingPositioner . UpdateTail (
6669 newElement . InitialTailIndex ,
70+ newElement . ItemCount ,
6771 newElement . DisplayedTailKey ) ;
6872 return itemsView ;
6973 }
@@ -87,13 +91,14 @@ public V1UnmountDisposition Unmount(UnmountContext context, ItemsViewVerticalScr
8791 private string ? _requestKey ;
8892 private string ? _displayedTailKey ;
8993 private int _tailIndex ;
94+ private int _itemCount ;
9095 private int _version ;
9196 private bool _valid ;
9297 private bool _awaitingLayout ;
9398 private WinUIScrollView ? _awaitingScrollView ;
9499 private WinUIScrollView ? _scrollView ;
95100 private bool _following ;
96- private bool _tailRequestQueued ;
101+ private readonly TailNavigationQueue _tailNavigationQueue = new ( ) ;
97102 private bool _disposed ;
98103
99104 public InitialTailPositioner ( WinUIItemsView itemsView )
@@ -103,33 +108,42 @@ public InitialTailPositioner(WinUIItemsView itemsView)
103108 itemsView . Unloaded += OnUnloaded ;
104109 }
105110
106- public void Request ( int tailIndex , string requestKey , string ? displayedTailKey )
111+ public void Request ( int tailIndex , int itemCount , string requestKey , string ? displayedTailKey )
107112 {
108113 if ( _disposed || string . Equals ( _requestKey , requestKey , StringComparison . Ordinal ) )
109114 return ;
110115
111116 _requestKey = requestKey ;
112117 _version ++ ;
113118 DetachLayout ( ) ;
114- _valid = tailIndex >= 0 ;
115- if ( ! _valid )
116- return ;
117-
118119 _tailIndex = tailIndex ;
120+ _itemCount = itemCount ;
119121 _displayedTailKey = displayedTailKey ;
120122 _following = true ;
123+ _valid = TailNavigationPolicy . TryCapture ( tailIndex , displayedTailKey , itemCount , out _ ) ;
124+ if ( ! _valid )
125+ return ;
126+
121127 if ( itemsView . IsLoaded )
122128 AwaitLayout ( ) ;
123129 }
124130
125- public void UpdateTail ( int tailIndex , string ? displayedTailKey )
131+ public void UpdateTail ( int tailIndex , int itemCount , string ? displayedTailKey )
126132 {
127133 var changed = ! string . Equals ( _displayedTailKey , displayedTailKey , StringComparison . Ordinal ) ;
128134 _tailIndex = tailIndex ;
135+ _itemCount = itemCount ;
129136 _displayedTailKey = displayedTailKey ;
130- if ( changed && _following && tailIndex >= 0 && itemsView . IsLoaded && displayedTailKey is not null )
137+ _valid = TailNavigationPolicy . TryCapture ( tailIndex , displayedTailKey , itemCount , out var request ) ;
138+ if ( ! _valid )
139+ {
140+ _tailNavigationQueue . Clear ( ) ;
141+ return ;
142+ }
143+
144+ if ( changed && _following && itemsView . IsLoaded )
131145 {
132- QueueTailRequest ( _version ) ;
146+ QueueTailRequest ( _version , request ) ;
133147 }
134148 }
135149
@@ -174,7 +188,9 @@ private void OnLayoutUpdated(object? sender, object args)
174188 }
175189
176190 var version = _version ;
177- var index = _tailIndex ;
191+ if ( ! TailNavigationPolicy . TryCapture ( _tailIndex , _displayedTailKey , _itemCount , out var request ) )
192+ return ;
193+
178194 itemsView . DispatcherQueue . TryEnqueue ( ( ) =>
179195 {
180196 if ( _disposed || ! _valid || ! itemsView . IsLoaded || version != _version
@@ -186,7 +202,8 @@ private void OnLayoutUpdated(object? sender, object args)
186202 }
187203
188204 AttachScrollView ( ) ;
189- StartTailRequest ( index ) ;
205+ if ( ! StartTailRequest ( request ) && ! _disposed && _valid )
206+ AwaitLayout ( ) ;
190207 } ) ;
191208 }
192209
@@ -210,38 +227,51 @@ private void OnViewChanged(WinUIScrollView sender, object args)
210227 _following = IsNearBottom ( sender ) ;
211228 }
212229
213- private void QueueTailRequest ( int version )
230+ private void QueueTailRequest ( int version , TailNavigationRequest request )
214231 {
215- if ( _tailRequestQueued )
232+ if ( ! _tailNavigationQueue . Enqueue ( version , request ) )
216233 return ;
217234
218- _tailRequestQueued = true ;
219235 if ( ! itemsView . DispatcherQueue . TryEnqueue ( ( ) =>
220236 {
221- _tailRequestQueued = false ;
222- if ( _disposed || ! _valid || ! itemsView . IsLoaded || version != _version || ! _following )
237+ if ( ! _tailNavigationQueue . TryDequeue ( _version , out var queuedRequest )
238+ || _disposed
239+ || ! _valid
240+ || ! itemsView . IsLoaded
241+ || ! _following )
223242 {
224243 return ;
225244 }
226245
227- StartTailRequest ( _tailIndex ) ;
246+ StartTailRequest ( queuedRequest ) ;
228247 } ) )
229248 {
230- _tailRequestQueued = false ;
249+ _tailNavigationQueue . SchedulingFailed ( ) ;
231250 _following = false ;
232251 }
233252 }
234253
235- private void StartTailRequest ( int index )
254+ private bool StartTailRequest ( TailNavigationRequest request )
236255 {
237256 if ( itemsView . ScrollView is not { IsLoaded : true } )
238- return ;
257+ return false ;
258+
259+ if ( ! TailNavigationPolicy . CanExecute (
260+ request ,
261+ _tailIndex ,
262+ _displayedTailKey ,
263+ _itemCount ) )
264+ {
265+ return false ;
266+ }
267+
239268 _following = true ;
240- itemsView . StartBringItemIntoView ( index , new BringIntoViewOptions
269+ itemsView . StartBringItemIntoView ( request . Index , new BringIntoViewOptions
241270 {
242271 AnimationDesired = false ,
243272 VerticalAlignmentRatio = 1.0 ,
244273 } ) ;
274+ return true ;
245275 }
246276
247277 private static bool IsNearBottom ( WinUIScrollView scrollView ) =>
@@ -250,6 +280,7 @@ private static bool IsNearBottom(WinUIScrollView scrollView) =>
250280 private void OnUnloaded ( object sender , RoutedEventArgs args )
251281 {
252282 _version ++ ;
283+ _tailNavigationQueue . Clear ( ) ;
253284 DetachLayout ( ) ;
254285 DetachScrollView ( ) ;
255286 }
@@ -287,6 +318,7 @@ public void Dispose()
287318
288319 _disposed = true ;
289320 _version ++ ;
321+ _tailNavigationQueue . Clear ( ) ;
290322 DetachLayout ( ) ;
291323 DetachScrollView ( ) ;
292324 itemsView . Loaded -= OnLoaded ;
@@ -300,12 +332,14 @@ public static Element BindVerticalScrollController<T>(
300332 this ItemsViewElement < T > itemsView ,
301333 ElementRef < WinUIAnnotatedScrollBar > scrollBarRef ,
302334 int initialTailIndex ,
335+ int itemCount ,
303336 string initialTailRequestKey ,
304337 string ? displayedTailKey ) =>
305338 new ItemsViewVerticalScrollControllerElement (
306339 itemsView ,
307340 scrollBarRef ,
308341 initialTailIndex ,
342+ itemCount ,
309343 initialTailRequestKey ,
310344 displayedTailKey ) ;
311345}
0 commit comments