microsoft / microsoft/microsoft-ui-xaml

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

Open
#10,477 0 comments 0 reactions 0 assignees View on GitHub
bug needs-repro
Dominant language
C++
Stars
8.4k
Forks
942
Avg merge
2d 7h
Merged PRs (30d)
105

Description

### 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
```cpp
#pragma once
#include
#include
#include
#include
#undef GetCurrentTime
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
```
MainWindow.xaml.h
```cpp
#pragma once
#include "MainWindow.g.h"
namespace winrt::ScrollViewDpiTest::implementation
{
struct SimpleElementFactory : implements
{
Microsoft::UI::Xaml::UIElement GetElement(Microsoft::UI::Xaml::ElementFactoryGetArgs const& args);
void RecycleElement(Microsoft::UI::Xaml::ElementFactoryRecycleArgs const& args) {}
};
struct MainWindow : MainWindowT
{
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.xaml.cpp
```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());
return textBlock;
}
MainWindow::MainWindow()
{
InitializeComponent();
auto rootGrid = Controls::Grid();
m_scrollView = Controls::ScrollView();
auto itemsRepeater = Controls::ItemsRepeater();
auto items = winrt::single_threaded_observable_vector();
for (int i = 0; i < 10000; ++i)
{
items.Append(L"Item " + to_hstring(i));
}
itemsRepeater.ItemsSource(items);
auto elementFactory = make();
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
```xaml

```

**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."

Contributor guide

Open the contributing guide

Research direction

Start with the sample entry points in MainWindow.xaml.cpp and MainWindow.xaml, reproduce the issue with ScrollView and ItemsRepeater at 125% and 175% scaling on a 3840x2160 display, and observe ExtentHeight during slow thumb movement. Compare the behavior with the unaffected scaling and resolution combinations listed in the report. Done means ExtentChanged no longer fires solely because scrolling causes the extent to fluctuate.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.