-
Notifications
You must be signed in to change notification settings - Fork 802
Description
Describe the bug
This was an issue a while ago but after the last update it started happening again.
I have a WinUI App. After the window launches, I save the position and presenter state to the app settings. Then the next time the window opens before I attach the changed listener, I restore the position, size, and state from the settings.
This is the code in the constructor for the MainWindow
RestoreWindowPositionAndSize(); this.AppWindow.Changed += AppWindow_Changed;
This is the functions:
private void RestoreWindowPositionAndSize()
{
try
{
var localSettings = ApplicationData.Current.LocalSettings.Values;
object xSetting = localSettings.TryGetValue(X_POSITION, out object? xValue) ? xValue : 0;
int x = int.TryParse(xSetting?.ToString(), out int xPosition) ? xPosition : 0;
object ySetting = localSettings.TryGetValue(Y_POSITION, out object? yValue) ? yValue : 0;
int y = int.TryParse(ySetting?.ToString(), out int yPosition) ? yPosition : 0;
object widthSetting = localSettings.TryGetValue(WIDTH, out object? widthValue) ? widthValue : 800;
int width = int.TryParse(widthSetting?.ToString(), out int widthValueInt) ? widthValueInt : 800;
object heightSetting = localSettings.TryGetValue(HEIGHT, out object? heightValue) ? heightValue : 600;
int height = int.TryParse(heightSetting?.ToString(), out int heightValueInt) ? heightValueInt : 600;
Debug.WriteLine($"Restoring window position and size: {x}, {y}, {width}, {height}");
AppWindow?.MoveAndResize(new Windows.Graphics.RectInt32(x, y, width, height), DisplayArea.Primary);
if (IsWindowOffScreen(this))
{
AppWindow?.MoveAndResize(new Windows.Graphics.RectInt32(0, 0, 800, 600), DisplayArea.Primary);
}
Debug.WriteLine("Restored window position");
if (localSettings.TryGetValue(PRESENTER_STATE, out var stateValue) && AppWindow?.Presenter is OverlappedPresenter presenter)
{
var state = (OverlappedPresenterState)Enum.ToObject(typeof(OverlappedPresenterState), stateValue);
if (state == OverlappedPresenterState.Maximized)
presenter.Maximize();
}
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to restore window position and size: {ex.Message}");
}
}
private enum MonitorFlag : uint
{
/// <summary>Returns NULL.</summary>
MONITOR_DEFAULTTONULL = 0,
/// <summary>Returns a handle to the primary display monitor.</summary>
MONITOR_DEFAULTTOPRIMARY = 1,
/// <summary>Returns a handle to the display monitor that is nearest to the window.</summary>
MONITOR_DEFAULTTONEAREST = 2
}
[LibraryImport("user32.dll")]
private static partial IntPtr MonitorFromWindow(IntPtr hwnd, MonitorFlag flag);
public static bool IsWindowOffScreen(Window window)
{
try
{
// get pointer from window
var hWnd = WindowNative.GetWindowHandle(window);
// get monitor from pointer and default to null if none is found
var monitor = MonitorFromWindow(hWnd, MonitorFlag.MONITOR_DEFAULTTONULL);
// will return true if no monitor is found for the window
return monitor == IntPtr.Zero;
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to determine if window is off screen: {ex.Message}");
return true;
}
}
private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
// save position, size, and presenter so it can be restored on next start
if (args.DidSizeChange)
{
ApplicationData.Current.LocalSettings.Values[WIDTH] = sender.Size.Width;
ApplicationData.Current.LocalSettings.Values[HEIGHT] = sender.Size.Height;
}
if (args.DidPositionChange)
{
ApplicationData.Current.LocalSettings.Values[X_POSITION] = sender.Position.X;
ApplicationData.Current.LocalSettings.Values[Y_POSITION] = sender.Position.Y;
}
if (args.DidPresenterChange)
{
if (sender.Presenter is OverlappedPresenter presenter)
{
ApplicationData.Current.LocalSettings.Values[PRESENTER_STATE] = (int)presenter.State;
}
Debug.WriteLine("Presenter changed to " + sender.Presenter.Kind);
ApplicationData.Current.LocalSettings.Values[PRESENTER_KIND] = (int)sender.Presenter.Kind;
}
}
This was working correctly and works correctly most of the time, but it has some trouble on 4K monitors with certain scaling.
The example I can reproduce on my computer is my monitor with a 3840 x 2160 resolution works if I have scaling set to 150% in windows. Anything less than this (125% or 100%) causes the following error with a crash.
Removing the move and resize doesn't fix the issue but commenting out presenter.Maximize() fixes the issue. Is there a workaround for this?
Microsoft.UI.Xaml.dll!00007FF814CBD5D8: 88000FA8 - AG_E_LAYOUT_CYCLE Layout Iteration Countdown: 7. Launching EffectiveViewport Pass. Layout Iteration Countdown: 6. Raising SizeChanged Events. Layout Iteration Countdown: 5. Launching Measure Pass. Layout Iteration Countdown: 4. Launching Arrange Pass. Layout Iteration Countdown: 3. Launching EffectiveViewport Pass. Layout Iteration Countdown: 2. Raising SizeChanged Events. Layout Iteration Countdown: 1. Launching Measure Pass. Layout Iteration Countdown: 0. Launching Arrange Pass. Exception thrown at 0x00007FF9354EAB6A (KernelBase.dll) in AcSYS Tools UWP.exe: WinRT originate error - 0x802B0014 : 'Layout cycle detected. Layout could not complete.'.
Steps to reproduce the bug
On a 4K monitor with no scaling, call AppWindow.Presenter.Maximize() in the constructor for the MainWindow.
Expected behavior
It should be able to go fullscreen from the code. Maximizing manually doesn't result in the error.
Screenshots
No response
NuGet package version
WinUI 3 - Windows App SDK 1.6.6: 1.6.250228001
Windows version
Windows 11 (24H2): Build 26100
Additional context
No response