Blazor - support variable height items in Virtualize component
When create a listview of similarly sized items, it's very hard to virtualise this in most web frameworks currently. It'd be great for Blazor to provide an out of the box solution that is able to render a virtual list of items, and account for it's position in the list as items are rendered. Most of my implementations of lists are of items that don't vary in size all that greatly, i.e. an extra line or two of text per item due to some wrapping.
This would need to be able to handle data updates over time, so it shouldn't remember the size of the item when it goes back out of the view port and if the item is in the viewport it should account for the change in size after an update while assuming an approximate size for the not currently visible items.
My concept is to try to have the scroll bar managing the item number through the list rather than the absolute x position, then to show the item at the top that matches closest to that by %, if snapping the items makes this easier that may be an approach.
Basically you'd know the items sizes currently in the viewport, and as the user scrolls, the overall size of the view port would adjust based on the currently visible items actual size + the amount of items above * approx size + the amount of items below * approx size.
This is done well in Xamarin, but that is a non web framework so I guess it's a little easier to control the outcome.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
How could you know the sizes of items not currently in the DOM?
We wouldn't know the exact size but we could know an approx height of an average item, then when it enters the view you know the actual size. This wouldn't work well for highly varied items sizes, but if you have a known templated list where items are mostly similar sizes it may be possible. How does Xamarin do it on mobile?
How could you know the sizes of items not currently in the DOM?
You guess until you actually need to know. Then render them in a hidden div off screen and measure it.
How does Xamarin do it on mobile?
I believe they rely on the individual platforms for virtual scrolling. WPF and UWP both have built in virtualizing panels that support arbitrary heights. I have to assume iOS and Android do as well.
This could certainly be done in Blazor though. See the part of the best Microsoft UI framework ever made for inspiration:
https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizingStackPanel.cs
No, it's not easy. There's a complex relationship between the ItemsControl, the ItemsSource, ItemContainers and ItemContainerGenerator, and a lot of other moving parts. It also helps that WPF handles all measuring and layout, so it can determine the height of an offscreen container without actually rendering it (but you could do this with JS; see above).
All that said, if you didn't need smooth pixel scrolling, but instead were ok with scrolling in row-sized increments, the problem becomes an order of magnitude less complex.
In either case, though, you probably have to ditch the browser's built in scrolling mechanism and devise your own, which means your own scrollbar component with a thumb, track, etc. But there would be huge advantages to that anyway besides supporting virtual scrolling.
In any event, I don't think this is something we should reasonably expect the Microsoft team to implement, but I think the door is wide open for a component developer to do it.
@legistek I haven't seen any good component that implements this from the component vendors like Telerik, Syncfusion etc on their web frameworks, this would make Blazor really compelling to me as it would remove the biggest pain point I have with HTML5, if the base virtualize container was capable of this, it'd make a lot of UIs and other components much easier to build while maintaining that benefit of virtualizing the large lists.
It is base level functionality of a virtual scrolling system on mobile but I do understand that they are running native UI which enables more control over the resulting scroll behaviour.
I recently discovered the Microsoft Fluent UI React controls and they support virtualization with arbitrary and variable row heights:
https://developer.microsoft.com/en-us/fluentui#/controls/web/detailslist/variablerowheights
It's quite good. Not as good as the virtualizing panels in the native Windows XAML platforms but still very good. I haven't delved into the code to see how they work but by definition if it can be done in React it could be done in Blazor.
Hi, do you have a plan for this in the future? Because our friend claims that they have solved this problem for react. I am developing a social network and social network posts do not have a fixed height.
I have the same question
This implementation https://www.meziantou.net/infinite-scrolling-in-blazor.htm seems better.
@GioviQ infinite scrolling is not the same as virtualization
@realivanjx can you detail the differences, because to me both load items on demand.
@realivanjx can you detail the differences, because to me both load items on demand.
Virtualized only render in the DOM a subset of data available. infinite scroll load data from server as you scroll but add all loaded data in the DOM
So when you scroll virtual table @neckaros previously visible items are removed from DOM? Are you sure?
@GioviQ That is the definition of virtualization, yes. It will keep a fixed number of DOM elements, usually equal to the visible height + a few on top and bottom. These will then be reused for the content as the user scrolls. This ensures that we don't have massive memory use to render thousands of elements when no more than maybe 50 will be visible at any one time.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
Hi, this is a very important feature for reasons mentioned above among others. Hopefully this will be prioritized.
A very needed feature for performance with a lot of data being rendered. Majority of the times we are in need of different heights for each element, depending on the data that should be displayed.
I am guessing the solution to this would be something like...
[Previous page] <Current page> [Next page]
When the first item on [Next page] becomes visible you do the following 1: Set [Previous page] = <Current page> 2: Set <Current page> = [Next page] 3: Fetch P3 data and put into [Next page]
Very idle thoughts, most likely should be ignored :)
Is there any progress or work on this functionality?
Any update on this? Its a much needed feature.
According to the labels, this issue is not being considered for .NET 7. Our best bet is to upvote this issue, and share specific use-cases as a comment.
In terms of use cases, some were mentioned above. But in today's internet age, most content is scrollable content and having this limitation is a problem.
The reason why a workaround wont be enough is because, even after following the performance guidelines for blazor webassembly (standalone) from microsoft, it will still be a problem in terms of cpu/mem usage when having more than a few hundred items loaded, even when their UI is not too complex. (Pagination or other methods not an option).
I have a huge list of tables while each table has it's own fields. I also have an option to expand or collapse the fields. When the fields are collapsed and there are only rows with tables, the height of each row is the same, and everything is working as expected. On the other side, when the fields are expanded the height of each row is different, and causes strange scrolling issues. For example when more items are loaded the page scrolls to previous rows I have already passed.
In my case I have already all the tables and the fields in memory (in variable), so based on my css I could know the height of each row, for example RowHeight="@(table.fields.count() * 25)px;"
Not sure if a such property will cover everybody requirements, as other users may use Data Virtualization Loading (querying on scrolling) where in this case you could not know the items count and in this case this property should be filled after the items are queued.
Finally, such a mechanism, I think it can work :)
I think that @SteveSandersonMS has provided an excellent answer in this comment detailing much of the reasoning behind why this and similar issues may be difficult to tackle.
That said, I agree with @rf-0 in that this limitation is a problem. Think of a simple data card containing a title, an image, and some text. While they will all have a relatively similar size there may be slight variance due to the size of the image or amount of text.
Speaking to Blazor WebAssembly specifically, one of the really great things about it is you can scale an app with minimal server requirements by having a backend API while the user device takes care of the rendering and UI work. This allows running an app on almost any device. But this limitation of not being able to virtualize large data sets can easily consume all of the memory on older or less powerful devices (think of a certain online retailer selling tablets using the technology of yesteryear).
I think one possible solution, albeit not very elegant, would be to allow the ItemSize parameter to be used as an average item size. This would still provide the ability to have a near fixed size scrollbar. With some slight adjustments to the scrollbar size depending on the actual size of the currently loaded items. Thus the scroll position would still determine the start index and the amount of items loaded would be based on the average item size. Combined with the OverscanCount parameter I think this would be not too difficult to implement and add a great deal to the usability of the component.
I will say this, please, please, please remove the sealed keyword from Virtualize so that I, and I'm sure many others, can try these ideas out. Why bother having an open-source framework and make it intentionally difficult to extend?
@mrpmorris If you do have a solution please do share. We use infinite scrolling (have our own custom component) in quite a few areas of the web app we are currently building (also using your Fluxor library. brilliant library btw! thanks!) and this would be a match in heaven! At the moment loading/rendering so many items comes with quite a few performance costs when using webassembly, especially when navigating away to some other page or rerendering the items already loaded. Obviously, this isn't the case when using Virtualize which would be the ideal component for what we want to do. @thirstyape solution could work as well.
Being able to precompute the size of each item and let the Virtualize component know in advance before rendering could potentially work? We could precompute this easily based on the content. So in this case the Item size would be defined on the child component, per item, instead of at the parent level (Virtualize).
Also, this is important:
I will say this, please, please, please remove the sealed keyword from Virtualize so that I, and I'm sure many others, can try these ideas out. Why bother having an open-source framework and make it intentionally difficult to extend?
I came across the need for variable-height items today as well. Would love to see it "just work", though I understand the challenges noted above.
Our workaround is boost Overscan to 50 in the hopes that the scrollbar jumps around less with more of the nearby items already in the DOM. Not really a fix.
I am also looking for that feature. There are so many use cases that benefit from it. Chat apps, Social Apps, Forum apps.... Even this issue here is a use case. Many messages all of variable height. Its not always a huge number of items which requires virtualisation, Lists which render complex layouts also benefit a lot, as long as they need some scrolling.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
I tried to implement something like unlimited scroll with variable length of items per row and no matter the style of items, it is more like a hack rather than nicely done but it works for me it might just help you guys what virtualization is missing. Check it on stack overflow: here
Hi, any update on this topic with the variable height of items while using virtualize?