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

Layout rounding can cause DataGrid to hang #9944

Open
cwensley opened this issue Oct 11, 2024 · 4 comments · May be fixed by #9983
Open

Layout rounding can cause DataGrid to hang #9944

cwensley opened this issue Oct 11, 2024 · 4 comments · May be fixed by #9983
Labels
🚧 work in progress Investigate Requires further investigation by the WPF team.

Comments

@cwensley
Copy link

Description

When UseLayoutRounding=true, the DataGrid can hang in an endless loop trying to get a cell into view after resetting the data then setting DataGrid.CurrentCell.

It appears to happen particularly when the column expands beyond the width of the DataGrid, and only on certain screen scales.

Reproduction Steps

  1. Set screen scale to 125% in System > Display Settings
  2. Open and run this project: TestWpfColumnFreeze.zip
  3. Double click on one of the cells that says "Double click on me"

Expected behavior

Should not hang at any screen scale or layout rounding setting

Actual behavior

The application hangs and does not respond to input.

The offending method appears to be in DataGridCellsPanel.BringIndexIntoView, where it gets stuck in this loop:

while (!IsChildInView(index, out newHorizontalOffset) && !DoubleUtil.AreClose(value, newHorizontalOffset))
{
	retryRequested = true;
	scrollInfo.SetHorizontalOffset(newHorizontalOffset);
	UpdateLayout();
	value = newHorizontalOffset;
}

Stack Trace:

 	System.Private.CoreLib.dll!System.Threading.Monitor.Enter(object obj, ref bool lockTaken)	Unknown
 	PresentationCore.dll!System.Windows.Media.Visual.GetDpi()	Unknown
 	PresentationCore.dll!System.Windows.UIElement.Arrange(System.Windows.Rect finalRect)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.DataGridColumnHeadersPresenter.ArrangeOverride(System.Windows.Size finalSize) Line 168	C#
 	PresentationFramework.dll!System.Windows.FrameworkElement.ArrangeCore(System.Windows.Rect finalRect) Line 3718	C#
 	PresentationCore.dll!System.Windows.UIElement.Arrange(System.Windows.Rect finalRect)	Unknown
 	PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout()	Unknown
>	PresentationFramework.dll!System.Windows.Controls.DataGridCellsPanel.BringIndexIntoView(int index) Line 1580	C#
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	System.Private.CoreLib.dll!System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(object obj, System.Span<object> copyOfArgs, System.Reflection.BindingFlags invokeAttr)	Unknown
 	System.Private.CoreLib.dll!System.Reflection.MethodBaseInvoker.InvokeWithOneArg(object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, object[] parameters, System.Globalization.CultureInfo culture)	Unknown
 	System.Private.CoreLib.dll!System.Delegate.DynamicInvokeImpl(object[] args)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Line 83	C#
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Line 24	C#
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() Line 424	C#
 	WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj) Line 99	C#
 	System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	Unknown
 	WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 85	C#
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() Line 350	C#
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() Line 1250	C#
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) Line 1367	C#
 	WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) Line 192	C#
 	WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Line 223	C#
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Line 73	C#
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Line 24	C#
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) Line 990	C#
 	WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(nint hwnd, int msg, nint wParam, nint lParam) Line 180	C#
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) Line 1281	C#
 	PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)	C#
 	PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)	C#
 	TestWpfColumnFreeze.dll!TestWpfColumnFreeze.App.Main()	Unknown

Regression?

Also happens with latest .NET Framework, not sure about earlier versions.

Known Workarounds

Turning off layout rounding for the DataGridRow appears to avoid the issue.

Impact

No response

Configuration

Running on Windows 11 23H2, .NET runtime 8.0.8 or 7.0.0

Reproducible on both x64 and ARM64 architectures.

Other information

No response

@lindexi
Copy link
Member

lindexi commented Oct 12, 2024

Yeah, I can repro your issues and I think it is the WPF's bug.

The loop in BringIndexIntoView will never return:

		while (!IsChildInView(index, out newHorizontalOffset) && !DoubleUtil.AreClose(value, newHorizontalOffset))
		{
			retryRequested = true;
			scrollInfo.SetHorizontalOffset(newHorizontalOffset);
			UpdateLayout();
			value = newHorizontalOffset;
		}

@himgoyalmicro himgoyalmicro added Investigate Requires further investigation by the WPF team. .NET Framework labels Oct 14, 2024
@lindexi
Copy link
Member

lindexi commented Oct 14, 2024

The computational logic here is too complicated for me. I have made no progress on this issue.

@himgoyalmicro I want to remove the .NET Framework label. Because I can repro this issues in .NET 6 7 8 9...

@h3xds1nz
Copy link
Contributor

h3xds1nz commented Oct 15, 2024

@lindexi Since I had an hour now, I've taken a look.

The fix for the hang itself is kinda easy.
@cwensley Thanks for the repro.

Because of layout rounding, GetParentCellsPanelHorizontalOffset can return negative (e.g. -0.4), which happens here. Doing simple Math.Abs on the result fixes the hang. (I can't decide whether I should do Math.Max(0, result) instead. Need to run some more tests and then I can PR.)

CellsPanelHorizontalOffset = DataGridHelper.GetParentCellsPanelHorizontalOffset(cell);

Also since CellsPanelHorizontalOffset is used for Width of the property of the button handling DataGrid.SelectAllCommand, it shall never be less than 0, otherwise the binding will explode as well.

The last question to investigate is whether there's a non-DPI adjusted value returned somewhere (or likely, adjusted twice), since the difference between being 0.4, and spacing between DataGrid and ScrollViewer was 1.6 in ActualWidth, which multiplied by 1.25 (I've tested it on 125% DPI only) is 2 so that gives diff of 0.4.

@cwensley
Copy link
Author

Hey @h3xds1nz,

Thanks for taking a look at the problem! I'm glad it wasn't too hard to get around the issue.

Looking forward to this landing in a release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🚧 work in progress Investigate Requires further investigation by the WPF team.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants