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

Offset Issue with items in ItemsRepeater inside a ScrollViewer when modifying Visibility #9775

Open
torleifat opened this issue Jun 28, 2024 · 1 comment
Labels
bug Something isn't working needs-triage Issue needs to be triaged by the area owners

Comments

@torleifat
Copy link

Describe the bug

So this is a bit of a convoluted one, but the gist of it is the following:

If you have an ItemsRepater contained within a ScrollViewer, with enough items in the repeater that the ScrollViewer can be scrolled. If you then modify the Visibility of the items, you can end up with items being offset outside the visible area of the ItemsRepeater/ScrollViewer

As an example
This is how things should work:
itemsrepeater_item_offset_issue_1
Clicking the respective buttons modifies the Visibility of the items in the ItemsRepeater.

However, if you have scrolled a little and try to do the same, then this will happen:

itemsrepeater_item_offset_issue_2

Now the orangutang appears to be gone, but it is only offset:
image
The blue square under ItemsRepeater is the actual Item. So it is there, and seemingly Visible, but offset so far that it cannot be seen.

It seems this will only happen when collapsing the later items in the collection. Also if there are so many items being made visible that the ScrollViewer can scroll then this will happen:
itemsrepeater_item_offset_issue_3
Here the first item is offset at first, but once we scroll it snaps back into the correct position.

Steps to reproduce the bug

  1. Created an empty project, and added a page that I navigate to from the MainWindow.
  2. The rest is pretty straight forward. The important bits is to have an ItemsRepeater with enough items that the ScrollViewer Containing it will be activated. And then a way to modify visibility.

Here's the code I had for page I used to make the images with:

MainPage.idl

namespace ScrollViewerAndItemsRepeaterIssue
{
    [default_interface]
    runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
    {
        MainPage();
        Windows.Foundation.Collections.IObservableVector<String> Items{ get; };
    }
}

MainPage.xaml

<?xml version="1.0" encoding="utf-8"?>
<Page
    x:Class="ScrollViewerAndItemsRepeaterIssue.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ScrollViewerAndItemsRepeaterIssue"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Background="Aquamarine">

    <Page.Resources>
        <ResourceDictionary>
            <StackLayout x:Name="HorizontalStackLayout" Orientation="Horizontal" Spacing="0"/>
            <NonVirtualizingLayout x:Name="nvl" ></NonVirtualizingLayout>

            <DataTemplate x:Key="ItemTemplate" x:DataType="x:String">
                <Border Background="Red" BorderBrush="Black" BorderThickness="1" Width="64" Height="64">
                    <TextBlock FontSize="38" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{x:Bind}"></TextBlock>
                </Border>
            </DataTemplate>

        </ResourceDictionary>
    </Page.Resources>

    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <ScrollViewer x:Name="Scroller" Height="64" Width="300" Background="Green" HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Visible">
            <ItemsRepeater x:Name="Repeater" Layout="{StaticResource HorizontalStackLayout}" ItemsSource="{x:Bind Items}" ItemTemplate="{StaticResource ItemTemplate}"/>
        </ScrollViewer>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Tag="orangutang" Click="Button_Click">Show only 🍌</Button>
            <Button Tag="banana" Click="Button_Click">Show only 🦧</Button>
        </StackPanel>
    </StackPanel>
</Page>

MainPage.xaml.h

#pragma once

#include "MainPage.g.h"

namespace winrt::ScrollViewerAndItemsRepeaterIssue::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        void FilterOutBanana();
        void FilterOutOrangutang();
        void Button_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);

        winrt::Windows::Foundation::Collections::IObservableVector<hstring> Items();
        winrt::Windows::Foundation::Collections::IObservableVector<hstring> m_Items;
    };
}

namespace winrt::ScrollViewerAndItemsRepeaterIssue::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>{};
}

MainPage.xaml.cpp

#include "pch.h"
#include "MainPage.xaml.h"
#if __has_include("MainPage.g.cpp")
#include "MainPage.g.cpp"
#endif

using namespace winrt;
using namespace Microsoft::UI::Xaml;
using namespace Windows::Foundation;

namespace winrt::ScrollViewerAndItemsRepeaterIssue::implementation
{
    MainPage::MainPage()
    {
        m_Items = winrt::single_threaded_observable_vector<hstring>();

        m_Items.Append(L"🦧");
        m_Items.Append(L"🍌");
        m_Items.Append(L"🍌");
        m_Items.Append(L"🍌");
        m_Items.Append(L"🍌");
        m_Items.Append(L"🍌");
    }

    winrt::Windows::Foundation::Collections::IObservableVector<hstring> MainPage::Items()
    {
        return m_Items;
    }

    void MainPage::Button_Click(IInspectable const& sender, RoutedEventArgs const& e)
    {
        auto toFilterOut = winrt::unbox_value<hstring>(sender.as<Controls::Button>().Tag());
        if (toFilterOut == L"orangutang")
        {
            FilterOutOrangutang();
        }
        else if (toFilterOut == L"banana")
        {
            FilterOutBanana();
        }
    }

    void MainPage::FilterOutBanana()
    {
        for (uint32_t i = 0; i < m_Items.Size(); i++)
        {
            auto element = Repeater().TryGetElement(i);
            if (i == 0)
            {
                element.Visibility(Visibility::Visible);
            }
            else
            {
                element.Visibility(Visibility::Collapsed);
            }
        }
    }

    void MainPage::FilterOutOrangutang()
    {
        for (uint32_t i = 0; i < m_Items.Size(); i++)
        {
            auto element = Repeater().TryGetElement(i);
            if (i == 0)
            {
                element.Visibility(Visibility::Collapsed);
            }
            else
            {
                element.Visibility(Visibility::Visible);
            }
        }
    }
}

Expected behavior

The Item not to be offset out of view.

Screenshots

No response

NuGet package version

WinUI 3 - Windows App SDK 1.5.4: 1.5.240607001

Windows version

Windows 11 (22H2): Build 22621

Additional context

No response

@torleifat torleifat added the bug Something isn't working label Jun 28, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added the needs-triage Issue needs to be triaged by the area owners label Jun 28, 2024
Copy link

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one. Thank you!

Open similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs-triage Issue needs to be triaged by the area owners
Projects
None yet
Development

No branches or pull requests

1 participant