Refactor ItemsControl container generation and implement smooth virtualization#9677
Refactor ItemsControl container generation and implement smooth virtualization#9677
Conversation
A lot still broken, in particular virtualization is completely removed.`ItemsPresenter` now no longer has an `Items` or `ItemTemplate` property; it detects when it's hosted in an `ItemsControl`. `IItemsPresenter` interface removed.
Currently only supports smooth scrolling.
If you want a different virtualization mode, use a different panel.
A bunch of tests still failing, and some code commented out, but outlines the new API shape.
`ListBox` now needs a root as it uses a `VirtualizingStackPanel`.
Use a slightly different approach to the one that was previously there: create an item template that contains `DisplayMemberBinding`. This is the approach that WPF broadly uses too.
When `ItemsControl.DisplayMemberBinding` or `ItemTemplate` changes, refresh the containers.
As `DisplayMemberBinding` should select the member for the header (in WPF it's `DisplayMemberPath`). Also set `TabItem.TabStripPlacement`.
Wasn't finding `RelativeSource` type at times.
…0618_ItemContainerGenerator fix: ItemContainerGenerator obsolete member after #9677
|
@grokys Hi, I noticed that after migrating to preview5, ListBox no longer wraps items in container. Is this an expected behavior? |
May I ask if there is some initiative to change it to higher precision, so virtualised panels with many elements (in millions) have no offset scattering at all? I think I found a way around it, but the implementation is quite difficult and not very nice, so before I spend much time with it I would like to know, if there is not tendency to (re)implement it in some of the coming releases. Thanks |
No, none as for now.
Worth to create a new enhancement issue with details. |
My solution is probably not generally applicable so I will describe it there, and if someone find that approach acceptable to be part of framework, he can create proper issue for it. For now I will describe it here. Basically I kind of overlapped virtualised ItemsControl positioned inside Panel with my own Control (StackPanel of elements of same type and style as ItemTemplate in ItemsControl and that one is replaced with just dummy Decorator with specific Height), so ItemsControl works basically just as positioning calculator (seems like positioning is calculated properly, just render positions are scattered), and those position changes are delegated to my own overlapped StackPanel.
I find it not very elegant way of doing ui (overlapping dummy controls), but at the end it works kind of well from user's perspective (at least in my simple use-case). But more elegant way may probably be to kind of extract virtualized positioning algorithm and omit rendering capability from ItemsControl and kind of bind it to something like described above. (did not dive deeply into how virtualisation is currently done) Hope I describe it understandably. I would provide minimal repro sample, but I have kind of too bloated project and not enough time to scrape it atm. Here is also video how it works compared to default positioning: [full video] |
|
@GabrielHalvonik a feature request doesn't mean you need to provide a pull request, even if for sure pull requests are welcome. If the issue is kept here, it will be lost. It would be really nice if you can transfer your idea into a new issue as a feature request. To your approach in general: I wonder if it would be possible to implement an own Happy coding |
|
I still think it's not wise to discuss it in a merged PR. |
|
Yes, please open an issue to discuss this; please don't try to fix it before its discussed as there are various different ways in which this could be fixed and we need to decide where to fix it first. |



What does the pull request do?
This PR refactors item container generation for
ItemsControlas discussed in #9497 to be more similar to the WPF/UWP model. Given that this required a major refactoring of our virtualization logic, I also rewroteVirtualizingStackPanelto use "smooth" virtualization based on the code fromTreeDataGrid. Now allListBoxes etc use smooth virtualization and handle variable-sized items by default. There should now no longer be a problem with using virtualization in combination with with inline items (#4265 and others).XCjM39nCrh.mp4
The main changes from the point of view of a regular consumer of
ItemsControl-related controls are:ListBox.VirtualizationModehas been removed, the virtualization mode is changed by changing theItemsPanelCarousel.IsVirtualizinghas been removed, there is now only a "virtualizing" panelStackPanelshould be used if no virtualization is requiredVirtualizingStackPanelshould be used if "smooth" virtualization is requiredItemsControlas in UWP (old methods are left onItemContainerGeneratormarked with[Obsolete]):ItemsControl.ContainerFromIndex(object item)ItemsControl.IndexFromContainer(Control container)The main changes from the point of view of a virtualizing panel author are:
ItemsPresenteris no longer responsible for generating containers, the responsibility is moved toVirtualizingPanelItemContainerGeneratorshould no longer be inherited to modify the container type for anItemsControl, instead override methods onItemsControlA more detailed list of changes is listed in the Breaking Changes section.
Now the procedure to create a virtualizing panel is as follows:
VirtualizingPaneland implement the abstract membersItemContainerGeneratorfromVirtualizingPanel:ItemContainerGenerator.IsItemItsOwnContainer(Control)should first be called if the item is derived from theControlclass. If this method returns true then theitem itself should be used as the container.
IsItemItsOwnContainer(Control) returns false thenItemContainerGenerator.CreateContainer()` should be called to create a new container.VirtualizingPanel.AddInternalChild(Control)ItemContainerGenerator.ItemContainerPrepared(Control, object?, int)should be called.ItemContainerGenerator.ItemContainerIndexChanged(Control container, int oldIndex, int newIndex)should be calledClearItemContainer(Control)should be called if returned false.VirtualizingPanel-derived class.ItemContainerGeneratorDOES NOT store the realized containers anywhere, unlike WPF/UWPVirtualizingPanel.ContainerFromIndexandVirtualizingPanel.IndexFromContainershould be implemented to query this collection of realized containersVirtualizingPanel-derived class is also responsible for storing its own list of recyclcable containers and using these when necessaryIn addition, this PR finally fixes one of our oldest issues (#54):
ItemsControlnow exposes anItemsViewproperty which represents theItemswrapped in anItemsSourceView.Breaking Changes
ItemsControl
Item container generation/lifecycle API was changed to be similar to WPF/UWP.
ItemContainerGeneratorto change the container typeItemContainerGeneratorcannot be overridden from user code any moreTreeViewcreates aTreeItemContainerGeneratorfor backwards compatItemsControl.IsItemItsOwnContainerOverrideItemsControl.PrepareContainerForItemOverrideItemsControl.PrepareContainerForItemOverrideItemsControl.ContainerIndexChangedOverrideItemsControl.ClearContainerForItemOverrideItem container lookup was moved to
ItemsControlas in UWP (old methods are left onItemContainerGeneratormarked with[Obsolete])ItemsControl.ContainerFromIndex(object item)ItemsControl.ContainerFromItem(object item)ItemsControl.IndexFromContainer(Control container)ItemsControl.ItemFromContainer(Control container)ItemContainerGenerator
IsItemItsOwnContainerCreateContainerPrepareItemContainerItemContainerPreparedItemContainerIndexChangedClearItemContainerItemContainerGenerator<T>and move creation of containers to protected methods inItemsControl(above)ItemsControlare forwarded to theVirtualizingPanelIItemContainerGeneratorItemContainerInfoItemContainerGenerator.Containersis nowItemsControl.GetRealizedContainers()ItemContainerGenerator.ContainerFromIndexandItemContainerGenerator.IndexFromContainer, which now just call relevant methods onItemsControlQuestions
ItemFromContainerreturnsUnsetValueif the container was not found, in UWP it unhelpfully returnsSystem.__ComObject. Not a big fan of either of these. Currently it's implemented here as returningnullbutnullis actually a valid item. Should we change this method tobool TryGetItemFromContainer(out object? item)?ItemContainerGeneratorevents are replaced byItemsControl.PrepareContainerForItemOverrideandItemsControl.ClearContainerForItemOverrideItemsPresenter
ItemsPresenternow only generates containers for simple (non-virtualizing) panelsIItemsPresenterHostinterfaceIItemsPresenterinterfaceItemspropertyItemContainerGeneratorpropertyItemsPresentercan now only be used in anItemsControltemplate, not standalone (same as in WPF/UWP etc)VirtualizingPanel`
IVirtualizingPanelinterface has been removed. Virtualizing panels should inherit fromVirtualizingPanelTabControl
HeaderDisplayMemberBinding:DisplayMemberBindingshould select the member for the header (in WPF it'sDisplayMemberPath)Carousel
Panel;Carouselsyncs the horizontal scroll offset with the currently selected itemVirtualizingCarouselPanelcurrently implementedProblems:
Rotate3DTransitionwe see the old item flicker up when the transition finishesItemsSourceView
ItemsSourceView.GetOrCreate. This will allow addressing For Avalonia.Controls.ListBox for Items Property, Support for IList<T> without non-generic IList #8764 in future.IDisposable- this caused problems when sharingItemsSourceViewsFixed Issues
Fixes #54
Fixes #2177
Fixes #2259
Fixes #2868
Fixes #2936
Fixes #3506
Fixes #4265
Fixes #4701
Fixes #6480
Fixes #7467
#7706 is partially fixed. The stack overflow is fixed, but the
ScrollBargets confused, that part will need to be fixed separatelyFixes #8086
Fixes #8181
Fixes #8344
Fixes #8495
Fixes #9269