sbaeumlisberger / VirtualizingWrapPanel

Implementation of a comprehensive VirtualisingWrapPanel for WPF
MIT License
242 stars 33 forks source link

ScrollIntoView does not work when item is not within cache #49

Closed ArnauPrat closed 7 months ago

ArnauPrat commented 9 months ago

Describe your issue Hi all, and first of all, thanks for maintaining this package.

I have a ListBox whose panel is a VirtualizingWrapPanel, similar to the following one, with item grouping. The ItemsPanelTemplate is set in code, because we have different templates based on different options, but I leave here the one the uses the VirtualizingWrapPanel

 <ItemsPanelTemplate>
                            <vwp:VirtualizingWrapPanel Orientation="Vertical">
                                <vwp:VirtualizingWrapPanel.Resources>
                                    <DataTemplate DataType="{x:Type XXXl}">
                                    </DataTemplate>
                                </vwp:VirtualizingWrapPanel.Resources>
                            </vwp:VirtualizingWrapPanel>
  </ItemsPanelTemplate>

<ListBox 
                             ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                             SelectionMode="Extended"
                             SelectionChanged="DetailList_OnSelectionChanged"
                             VirtualizingPanel.IsVirtualizing="True"
                             VirtualizingPanel.IsVirtualizingWhenGrouping="True"
                 VirtualizingPanel.IsContainerVirtualizable="True"
                 VirtualizingPanel.CacheLengthUnit="Item"
                 VirtualizingPanel.CacheLength="15"
                 VirtualizingPanel.VirtualizationMode="Recycling">
            <ListBoxEx.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=Name}"
                                       Margin="0,5,0,5" 
                                       Padding="2,2,2,2"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </GroupStyle>
        </ListBox>

The issue is that when I call ListBox.ScrollToViewModel, it only works if the ViewModel is within the range of view models currently being cached. It works if:

I've also tested different parameter combinations unsuccessfully (e.g. different CacheLengthUnit, VirtualizationMode, etc.)

I've dived bit into the code, and this is what I've found so far. Calling ScrollToViewModel on a ViewModel outside cache range (this is not called on items within range), ends up calling protected override void BringIndexIntoView(int index)

https://github.com/sbaeumlisberger/VirtualizingWrapPanel/blob/f25434e1cc1996d3990eb75986842d9c8475dd98/src/VirtualizingWrapPanel/VirtualizingWrapPanel.cs#L374

which in turn calls SetVerticalOffset https://github.com/sbaeumlisberger/VirtualizingWrapPanel/blob/f25434e1cc1996d3990eb75986842d9c8475dd98/src/VirtualizingWrapPanel/VirtualizingPanelBase.cs#L384

In such a method, Offset is set to the desired offset to scroll and measure is invalidated so MeasureOverride is called.

In MeasureOverride, the problem is that my VirtualizingWrapPanel has an ItemsOwner that is a IHierarichicalAndVirtualizationScrollInfo https://github.com/sbaeumlisberger/VirtualizingWrapPanel/blob/f25434e1cc1996d3990eb75986842d9c8475dd98/src/VirtualizingWrapPanel/VirtualizingPanelBase.cs#L262

so Offset gets overwritten at https://github.com/sbaeumlisberger/VirtualizingWrapPanel/blob/f25434e1cc1996d3990eb75986842d9c8475dd98/src/VirtualizingWrapPanel/VirtualizingPanelBase.cs#L282

with the location of groupItem, which is not yet updated to the proper offset and contains the previous one.

What I suspect is that BringIndexIntoView is not correctly implemented. According to documentation (https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.virtualizingpanel.bringindexintoview?view=windowsdesktop-7.0), this method should "When implemented in a derived class, generates the item at the specified index location and makes it visible.". The current implementation is not doing this, but deferring the creation of the desired items to MeasureOverride (where things go wrong).

Looking at VirtualizingStackPanel source code, which works properly, https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/VirtualizingStackPanel.cs,1576 the desired items are created and made visible right there.

What do you think? Thanks for the help

Version Info 1.5.7 and 1.5.8 Net Framework 4.7.2 Microsoft Windows 10 Enterprise Version 10.0.19044 Build 19044

sbaeumlisberger commented 9 months ago

Thanks for reporting. I have been able to reproduce your issue and am working on a fix.

sbaeumlisberger commented 8 months ago

New preview package with a fix is available: https://www.nuget.org/packages/VirtualizingWrapPanel/2.0.0-preview5

The stable release will follow soon.

ArnauPrat commented 8 months ago

Many thanks @sbaeumlisberger ! I'll try it out asap.

sbaeumlisberger commented 8 months ago

The stable version has just been released: https://www.nuget.org/packages/VirtualizingWrapPanel/2.0.0

sbaeumlisberger commented 7 months ago

Closing this now. Feel free to open a new issue if there is any problem.