Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VirtualizingStackPanel render only first item #12432

Open
Meloman19 opened this issue Aug 4, 2023 · 4 comments · May be fixed by #15124
Open

VirtualizingStackPanel render only first item #12432

Meloman19 opened this issue Aug 4, 2023 · 4 comments · May be fixed by #15124
Labels

Comments

@Meloman19
Copy link
Contributor

Describe the bug
VirtualizingStackPanel render only first item when move from out of bounds with rendertransform.

To Reproduce

  1. Create blank project.
2. On MainView.axaml insert:
<Border Width="300"
		ClipToBounds="True">
	<Grid RowDefinitions="Auto, *">
		<UniformGrid Rows="1">
			<Button Content="Left"
					Click="Left_Click"
					HorizontalAlignment="Center"/>
			<Button Content="Right"
					Click="Right_Click"
					HorizontalAlignment="Center"/>
		</UniformGrid>
			<StackPanel x:Name="Panel"
					Orientation="Horizontal"
					Grid.Row="1"
					Spacing="10">
			<Border Background="Beige"
					Width="300">
				<ItemsControl Theme="{StaticResource ItemsControlTheme}">
					<TextBlock Text="1111" />
					<TextBlock Text="2222" />
					<TextBlock Text="3333" />
				</ItemsControl>
			</Border>
			<Border Background="LightBlue"
					Width="300">
				<ItemsControl Theme="{StaticResource ItemsControlTheme}">
					<TextBlock Text="4444" />
					<TextBlock Text="5555" />
					<TextBlock Text="6666" />
				</ItemsControl>
			</Border>
		</StackPanel>
	</Grid>
</Border>
3. MainView code-behind:
private TranslateTransform _translate;

public MainView()
{
    InitializeComponent();

    Panel.RenderTransform = _translate = new TranslateTransform
    {
        Transitions = new Avalonia.Animation.Transitions
        {
            new DoubleTransition
            {
                Duration = System.TimeSpan.FromMilliseconds(300),
                Property = TranslateTransform.XProperty
            }
        }
    };
}

private void Left_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
    _translate.X = 0;
}

private void Right_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
    _translate.X = -310;
}
4. ItemsControlTheme:
<ControlTheme x:Key="ItemsControlTheme"
			  TargetType="ItemsControl">
	<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
	<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
	<Setter Property="ScrollViewer.IsScrollChainingEnabled" Value="True" />
	<Setter Property="ScrollViewer.IsScrollInertiaEnabled" Value="True" />
	<Setter Property="ItemsPanel">
		<ItemsPanelTemplate>
			<VirtualizingStackPanel />
		</ItemsPanelTemplate>
	</Setter>
	<Setter Property="Template">
		<ControlTemplate>
			<Border Name="border"
					ClipToBounds="{TemplateBinding ClipToBounds}"
					Background="{TemplateBinding Background}"
					BorderBrush="{TemplateBinding BorderBrush}"
					BorderThickness="{TemplateBinding BorderThickness}"
					CornerRadius="{TemplateBinding CornerRadius}">
				<ScrollViewer Name="PART_ScrollViewer"
							  VerticalSnapPointsType="{TemplateBinding (ScrollViewer.VerticalSnapPointsType)}"
							  HorizontalSnapPointsType="{TemplateBinding (ScrollViewer.HorizontalSnapPointsType)}"
							  HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
							  VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
							  IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
							  IsScrollInertiaEnabled="{TemplateBinding (ScrollViewer.IsScrollInertiaEnabled)}"
							  AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
							  BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
					<ItemsPresenter Name="PART_ItemsPresenter"
									ItemsPanel="{TemplateBinding ItemsPanel}"
									Margin="{TemplateBinding Padding}"/>
				</ScrollViewer>
			</Border>
		</ControlTemplate>
	</Setter>
</ControlTheme>

Expected behavior
All items from ItemsControls are rendered

Screenshots

2023-08-04.04-54-29.mp4

Desktop (please complete the following information):

  • OS: Windows
  • Version: 11.0.2

Additional context
VirtualizingStackPanel render other items when visual is invalidated. For example, button change state from pointerover to normal or window resized etc. Also all right if use StackPanel instead VirtualizingStackPanel.

@Meloman19 Meloman19 added the bug label Aug 4, 2023
@timunie
Copy link
Contributor

timunie commented Aug 4, 2023

@Meloman19 I think LayouTransform is what you need. Render transform will not have enough information about transformed bounds.

https://docs.avaloniaui.net/docs/next/reference/controls/layouttransformcontrol#more-information

@Meloman19
Copy link
Contributor Author

@timunie Yes, LayoutTransform is works, but losing the benefit of RenderTransform.

As I can see from the source code, RenderTransform used on bounds transform. But main problem in EffectiveViewport. When panel is out of bounds, value of calculated viewport becomes empty and VirtualizedStackPanel prepare only one entity. And after change render position not raised EffectiveViewport event, because it's part of layout pass.

Instead of using an EffectiveViewport event, VirtualizingPanel can use directly parent IScrollable interface. But in this case invalidating event is missing. Or move EffectiveViewport to render pass...

@timunie
Copy link
Contributor

timunie commented Sep 14, 2023

I guess this is by design, but will ask @grokys first

@grokys
Copy link
Member

grokys commented Sep 14, 2023

Yeah, not sure how to fix this. You've correctly diagnosed it I think: the listbox that is being transitioned in is arranged outside the effective viewport of the containing control, so the control believes that it doesn't need to realize any items; not knowing that it's going to be transitioned into view by a render transform.

Your suggestions for solving it have a few problems:

  1. Instead of using an EffectiveViewport event, VirtualizingPanel can use directly parent IScrollable interface: this is how virtualization used to work, but it doesn't allow for multiple virtualized lists to be stacked for e.g. grouping and makes smooth scrolling a lot more difficult
  2. Move EffectiveViewport to render pass: EffectiveViewportChanged is a layout-level event, and we can't raise it on render unless we want to run a layout pass on each render frame (we don't)

I think some sort of hint where a transition can communicate to the virtualizing items control that it's being transitioned might be the best solution, but we'd need to think about how that would work. Alternatively we could just ignore render transforms when calculating the viewport for items controls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants