You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a ScrollToItemAsync(int itemIndex) method and an InitialItemIndex parameter to the Virtualize component, enabling programmatic scrolling to any item by index and setting the initial first-visible item without flash-of-wrong-content. Scroll should be instant (no animations).
Users are forced into fragile workarounds: JS interop to manually compute scroll offsets, which breaks unpredictably with variable-height items and virtualization window shifts.
Real-world scenarios blocked by this gap: table-of-contents navigation in document viewers, return-to-position after edit-and-back-navigate, chat jumping to a specific message, setting initial scroll position without visible flash-of-wrong-content.
In scope
ScrollToItemAsync(int itemIndex) — programmatically scroll to any item by zero-based index. Works with both Items and ItemsProvider. Returns a Task that completes when the target item is fully rendered, precisely aligned, and all IO/resize cycles have settled.
InitialItemIndex parameter — sets the initial scroll position on first render (latched once, ignored on re-renders). Implemented as a deferred internal call to ScrollToItemAsync once the item count is known; adds no new JS code path beyond what ScrollToItemAsync already needs.
Variable-height item support — the scroll uses actual DOM measurement, not pixel-offset estimation, for final alignment.
Async ItemsProvider support — convergence handles placeholder → real item transitions.
Race-safe rapid calls — monotonic request IDs ensure only the latest call wins.
Risks / unknowns
Reading the current first-visible item index. No known use cases for that; omitting it keeps the design free of any new [JSInvokable] surface (no JS→.NET pushes).
Smooth-scroll animation. Instant scroll only in v1; would interact poorly with convergence loops and IO suppression timing.
API surface
// New methodpublicTaskScrollToItemAsync(intitemIndex,CancellationTokencancellationToken=default);// New parameter — sets the initial scroll position on first render// (latched once; subsequent changes are ignored). Runtime scrolls go through// ScrollToItemAsync. Implemented internally as a deferred call to ScrollToItemAsync// once the item count is known.[Parameter]publicint?InitialItemIndex{get;set;}
Edge case behavior
InitialItemIndex is set to value higher than _itemCount returned by the itemProvider -> use _itemCount - 1 (last item)
InitialItemIndex is set to negative value -> treat it as 0. We want to avoid throwing in runtime for incorrect values.
ScrollToItemAsync(100) is mid-flight; consumer calls ScrollToItemAsync(200) -> the last call wins, cancel previos calls.
User scrolls during the ScrollToItemAsync is in flight -> programmatic scroll wins.
Prepend/append happens when we scroll to index -> the index is literal at call time but re-clamped on mutation.
Considered alternatives
Always rendering each item wrapped in a marker element (e.g., <div data-virtualize-item="{index}">) — rejected as a breaking change to existing DOM structure (CSS selectors, invalid HTML inside <table>/<tbody>, accessibility roles) with per-render overhead paid by every consumer. Unnecessary given that sibling-walk by target - _itemsBefore already gives JS a deterministic handle without any wrapper.
Wrapping items only during scroll-to-item operations (soft version of point 1) — rejected. Sibling-walk by DOM child index (matching restoreAnchorForShift) makes the wrapper unnecessary. Avoiding the wrapper removes the CSS-selector breakage (>, +, ~, :nth-child), accessibility-tree concerns, and table validity issues that would otherwise apply.
Dual-purpose FirstVisibleItemIndex parameter (setter + getter, with FirstVisibleItemIndexChanged event) — filed issues shows users want set capabilities (scroll-to, initial position), not real-time read of the value which would be complex due to timing of SignalR messages.
Summary
Add a
ScrollToItemAsync(int itemIndex)method and anInitialItemIndexparameter to theVirtualizecomponent, enabling programmatic scrolling to any item by index and setting the initial first-visible item without flash-of-wrong-content. Scroll should be instant (no animations).Motivation and goals
Virtualizehas no public API to scroll to a specific item. This is the most-requested Virtualize feature (Virtualize component Row Index Enhancements (API to scroll to item) #26943).In scope
ScrollToItemAsync(int itemIndex)— programmatically scroll to any item by zero-based index. Works with bothItemsandItemsProvider. Returns aTaskthat completes when the target item is fully rendered, precisely aligned, and all IO/resize cycles have settled.InitialItemIndexparameter — sets the initial scroll position on first render (latched once, ignored on re-renders). Implemented as a deferred internal call toScrollToItemAsynconce the item count is known; adds no new JS code path beyond whatScrollToItemAsyncalready needs.ItemsProvidersupport — convergence handles placeholder → real item transitions.Risks / unknowns
[JSInvokable]surface (no JS→.NET pushes).ScrollToItemAsync(TItem item)overload. Every concrete scenario in Virtualize component Row Index Enhancements (API to scroll to item) #26943 already has the index in hand; an item overload would also be unimplementable forItemsProviderwithout an extraIndexResolvercallback.API surface
Edge case behavior
InitialItemIndexis set to value higher than_itemCountreturned by theitemProvider-> use_itemCount - 1(last item)InitialItemIndexis set to negative value -> treat it as 0. We want to avoid throwing in runtime for incorrect values.ScrollToItemAsync(100)is mid-flight; consumer callsScrollToItemAsync(200)-> the last call wins, cancel previos calls.ScrollToItemAsyncis in flight -> programmatic scroll wins.Considered alternatives
Always rendering each item wrapped in a marker element (e.g.,
<div data-virtualize-item="{index}">) — rejected as a breaking change to existing DOM structure (CSS selectors, invalid HTML inside<table>/<tbody>, accessibility roles) with per-render overhead paid by every consumer. Unnecessary given that sibling-walk bytarget - _itemsBeforealready gives JS a deterministic handle without any wrapper.Wrapping items only during scroll-to-item operations (soft version of point 1) — rejected. Sibling-walk by DOM child index (matching
restoreAnchorForShift) makes the wrapper unnecessary. Avoiding the wrapper removes the CSS-selector breakage (>,+,~,:nth-child), accessibility-tree concerns, and table validity issues that would otherwise apply.Dual-purpose
FirstVisibleItemIndexparameter (setter + getter, withFirstVisibleItemIndexChangedevent) — filed issues shows users want set capabilities (scroll-to, initial position), not real-time read of the value which would be complex due to timing of SignalR messages.