Skip to content

ScrollView ExtentChanged Fires Unexpectedly with ItemsRepeater During Slow Thumb Scroll at DPI Scalings 125% and 175% on High-Resolution Displays #10477

@milk7add9

Description

@milk7add9

Describe the bug

The ExtentChanged event is unexpectedly triggered when manipulating the scroll thumb of a ScrollView containing an ItemsRepeater. This occurs intermittently under specific DPI scaling and resolution conditions.

Steps to reproduce the bug

  1. On Windows 11, set the display scaling to 125% or 175% and resolution to 3840x2160.
  2. Build and launch the sample code provided below.
  3. Slowly slide the scroll thumb to the bottom.
  4. Slowly slide the scroll thumb to the top.
  5. Repeat steps 3 and 4 approximately 10 times.
    Result: The debug output log shows that the ExtentChanged event is triggered unintentionally.

Expected behavior

The ExtentChanged event should not occur during scroll thumb operations. It is expected to fire only when the content's extent (size) changes, not during scrolling.

Screenshots

(No screenshots available; the issue is observable in the debug output log. See "Log Examples" in Additional Context for sample logs.)

NuGet package version

WinUI 3 - Windows App SDK 1.7.0: 1.7.250310001

Windows version

Windows 11 (24H2): Build 26100

Additional context

Actual Behavior:

  • At 3840x2160 resolution, with scaling at 125% (DPI 120) and 175% (DPI 168), the ExtentChanged event occurs intermittently during scrolling.
  • ExtentHeight exhibits slight fluctuations (e.g., 504000.0 ↔ 504000.8 at 125%, 502856.6 ↔ 502857.2 at 175%).
  • No occurrences at scaling 100%, 150%, 200%, 225%, 250%, 300%, 350% (tested at 3840x2160).
  • No occurrence at 1920x1080 resolution with 175% scaling.
  • Note: The issue is less likely to occur with fast thumb movement but is more frequent with slow movement. It may frequently occur at specific scroll positions (e.g., Offset around 502343.2).

Sample Code:
pch.h

#pragma once
#include <windows.h>
#include <unknwn.h>
#include <restrictederrorinfo.h>
#include <hstring.h>
#undef GetCurrentTime
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Xaml.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Microsoft.UI.Xaml.Data.h>
#include <winrt/Microsoft.UI.Xaml.Interop.h>
#include <winrt/Microsoft.UI.Xaml.Markup.h>
#include <winrt/Microsoft.UI.Xaml.Media.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
#include <winrt/Microsoft.UI.Dispatching.h>
#include <wil/cppwinrt_helpers.h>
#include <winrt/Microsoft.UI.Interop.h>
#include <microsoft.ui.xaml.window.h>
#include <winrt/Microsoft.UI.Windowing.h>

MainWindow.xaml.h

#pragma once
#include "MainWindow.g.h"
namespace winrt::ScrollViewDpiTest::implementation
{
    struct SimpleElementFactory : implements<SimpleElementFactory, Microsoft::UI::Xaml::IElementFactory>
    {
        Microsoft::UI::Xaml::UIElement GetElement(Microsoft::UI::Xaml::ElementFactoryGetArgs const& args);
        void RecycleElement(Microsoft::UI::Xaml::ElementFactoryRecycleArgs const& args) {}
    };
    struct MainWindow : MainWindowT<MainWindow>
    {
        MainWindow();
        void OnExtentChanged(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& args);
    private:
        Microsoft::UI::Xaml::Controls::ScrollView m_scrollView{ nullptr };
        HWND GetWindowHandle();
    };
}
namespace winrt::ScrollViewDpiTest::factory_implementation
{
    struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
    {
    };
}

MainWindow.xaml.cpp

#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
using namespace winrt;
using namespace Microsoft::UI::Xaml;
namespace winrt::ScrollViewDpiTest::implementation
{
    Microsoft::UI::Xaml::UIElement SimpleElementFactory::GetElement(Microsoft::UI::Xaml::ElementFactoryGetArgs const& args)
    {
        auto textBlock = Controls::TextBlock();
        textBlock.Height(50);
        textBlock.Text(args.Data().as<hstring>());
        return textBlock;
    }
    MainWindow::MainWindow()
    {
        InitializeComponent();
        auto rootGrid = Controls::Grid();
        m_scrollView = Controls::ScrollView();
        auto itemsRepeater = Controls::ItemsRepeater();
        auto items = winrt::single_threaded_observable_vector<hstring>();
        for (int i = 0; i < 10000; ++i)
        {
            items.Append(L"Item " + to_hstring(i));
        }
        itemsRepeater.ItemsSource(items);
        auto elementFactory = make<SimpleElementFactory>();
        itemsRepeater.ItemTemplate(elementFactory);
        m_scrollView.Content(itemsRepeater);
        m_scrollView.ExtentChanged({ this, &MainWindow::OnExtentChanged });
        rootGrid.Children().Append(m_scrollView);
        Content(rootGrid);
        HWND hWnd = GetWindowHandle();
        if (hWnd)
        {
            ShowWindow(hWnd, SW_MAXIMIZE);
        }
    }
    void MainWindow::OnExtentChanged(Windows::Foundation::IInspectable const& /*sender*/, Windows::Foundation::IInspectable const& /*args*/)
    {
        double extentHeight = m_scrollView.ExtentHeight();
        double viewportHeight = m_scrollView.ViewportHeight();
        double verticalOffset = m_scrollView.VerticalOffset();
        SYSTEMTIME st;
        GetLocalTime(&st);
        wchar_t buffer[256];
        swprintf_s(buffer, 256, L"[ExtentChanged] [%04d-%02d-%02d %02d:%02d:%02d.%03d] ExtentHeight: %.1f, ViewportHeight: %.1f, Offset: %.1f\n",
            st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
            extentHeight, viewportHeight, verticalOffset);
        OutputDebugStringW(buffer);
    }
    HWND MainWindow::GetWindowHandle()
    {
        auto windowNative = try_as<::IWindowNative>();
        HWND hWnd{ nullptr };
        if (windowNative)
        {
            windowNative->get_WindowHandle(&hWnd);
        }
        return hWnd;
    }
}

MainWindow.xaml

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

Log Examples:

  • Scale 125% (3840x2160):
[ExtentChanged] [2025-03-29 18:05:43.243] ExtentHeight: 504000.8, ViewportHeight: 1656.8, Offset: 294477.1
[ExtentChanged] [2025-03-29 18:05:43.287] ExtentHeight: 504000.0, ViewportHeight: 1656.8, Offset: 295230.2
[ExtentChanged] [2025-03-29 18:05:44.032] ExtentHeight: 504000.8, ViewportHeight: 1656.8, Offset: 502343.2
[ExtentChanged] [2025-03-29 18:05:44.371] ExtentHeight: 504000.0, ViewportHeight: 1656.8, Offset: 502343.2
  • Scale 175% (3840x2160):
[ExtentChanged] [2025-03-29 18:10:55.538] ExtentHeight: 502856.6, ViewportHeight: 1163.4, Offset: 178621.6
[ExtentChanged] [2025-03-29 18:10:55.573] ExtentHeight: 502857.2, ViewportHeight: 1163.4, Offset: 178621.6
[ExtentChanged] [2025-03-29 18:10:58.743] ExtentHeight: 502856.6, ViewportHeight: 1163.4, Offset: 501693.7
[ExtentChanged] [2025-03-29 18:10:58.775] ExtentHeight: 502857.2, ViewportHeight: 1163.4, Offset: 501693.7

Additional Information:

  • "The issue occurs at non-integer multiples of DPI 96 (125%, 175%) but not at integer or near-integer multiples (100%, 150%, 200%, etc.), though it may also depend on resolution."
  • "At 3840x2160, the issue occurs at 125% and 175%, but not at 1920x1080 with 175%. Comprehensive testing across other resolutions has not been conducted, so it may not occur at different resolutions."
  • "This could be due to a calculation error in scaling, potentially causing issues with the virtualization of ItemsRepeater and its interaction with ScrollView, particularly noticeable on high-resolution displays with slow scrolling."

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions