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

Workaround Multiple Calls to Activate #14152

Merged
merged 1 commit into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Controls/tests/DeviceTests/ControlsHandlerTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ await SetupWindowForTests<THandler>(window, async () =>
await OnLoadedAsync(content as VisualElement);
#if !WINDOWS
if (window is Window controlsWindow)
{
if (!controlsWindow.IsActivated)
Expand All @@ -174,6 +175,8 @@ await SetupWindowForTests<THandler>(window, async () =>
controlsWindow = null;
window.Activated();
}
#endif
#if WINDOWS
await Task.Delay(10);
#endif
Expand All @@ -187,6 +190,7 @@ await SetupWindowForTests<THandler>(window, async () =>
throw new Exception($"I can't work with {typeof(THandler)}");
#if !WINDOWS
bool isActivated = controlsWindow?.IsActivated ?? false;
bool isDestroyed = controlsWindow?.IsDestroyed ?? false;
Expand All @@ -195,6 +199,8 @@ await SetupWindowForTests<THandler>(window, async () =>
if (!isDestroyed)
window.Destroying();
#endif
}, mauiContext);
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Graphics.Win2D;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Platform;
using Xunit;
using WPanel = Microsoft.UI.Xaml.Controls.Panel;
Expand Down Expand Up @@ -134,5 +135,41 @@ await CreateHandlerAndAddToWindow<WindowHandlerStub>(window, async (handler) =>
}
});
}

[Collection(ControlsHandlerTestBase.RunInNewWindowCollection)]
public class WindowTestsRunInNewWindowCollection : ControlsHandlerTestBase
{
[Fact]
public async Task MinimizeAndThenMaximizingWorks()
{
var window = new Window(new ContentPage());

int activated = 0;
int deactivated = 0;
int resumed = 0;

window.Activated += (_, _) => activated++;
window.Deactivated += (_, _) => deactivated++;
window.Resumed += (_, _) => resumed++;

await CreateHandlerAndAddToWindow<IWindowHandler>(window, async (handler) =>
{
var platformWindow = window.Handler.PlatformView as UI.Xaml.Window;
await Task.Yield();
for (int i = 0; i < 2; i++)
{
platformWindow.Restore();
await Task.Yield();
platformWindow.Minimize();
}
});

Assert.Equal(2, activated);
Assert.Equal(1, resumed);
Assert.Equal(2, deactivated);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,17 @@ void OnClosed(object sender, UI.Xaml.WindowEventArgs args)

void OnActivated(object sender, UI.Xaml.WindowActivatedEventArgs args)
{
if (Window is not null)
if (args.WindowActivationState != UI.Xaml.WindowActivationState.Deactivated)
{
if (!Window.IsActivated)
if (Window is not null)
{
if (!Window.IsActivated)
_window.Activated();
}
else
{
_window.Activated();
}
else
{
_window.Activated();
}
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/Core/src/Platform/Windows/ApplicationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public static void CreatePlatformWindow(this UI.Xaml.Application platformApplica
if (application.Handler?.MauiContext is not IMauiContext applicationContext)
return;

var winuiWndow = new MauiWinUIWindow();
var winuiWindow = new MauiWinUIWindow();

var mauiContext = applicationContext!.MakeWindowScope(winuiWndow, out var windowScope);
var mauiContext = applicationContext!.MakeWindowScope(winuiWindow, out var windowScope);

applicationContext.Services.InvokeLifecycleEvents<WindowsLifecycle.OnMauiContextCreated>(del => del(mauiContext));

Expand All @@ -25,11 +25,11 @@ public static void CreatePlatformWindow(this UI.Xaml.Application platformApplica

var window = application.CreateWindow(activationState);

winuiWndow.SetWindowHandler(window, mauiContext);
winuiWindow.SetWindowHandler(window, mauiContext);

applicationContext.Services.InvokeLifecycleEvents<WindowsLifecycle.OnWindowCreated>(del => del(winuiWndow));

winuiWndow.Activate();
applicationContext.Services.InvokeLifecycleEvents<WindowsLifecycle.OnWindowCreated>(del => del(winuiWindow));
winuiWindow.SetWindow(window);
winuiWindow.Activate();
}
}
}
38 changes: 32 additions & 6 deletions src/Core/src/Platform/Windows/MauiWinUIWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class MauiWinUIWindow : UI.Xaml.Window, IPlatformSizeRestrictedWindow

IntPtr _windowIcon;
bool _enableResumeEvent;
bool _isActivated;
UI.Xaml.UIElement? _customTitleBar;

public MauiWinUIWindow()
Expand All @@ -42,13 +43,25 @@ protected virtual void OnActivated(object sender, UI.Xaml.WindowActivatedEventAr
{
if (args.WindowActivationState != UI.Xaml.WindowActivationState.Deactivated)
{
// We have to track isActivated calls because WinUI will call OnActivated Twice
// when maximizing a window
// https://github.com/microsoft/microsoft-ui-xaml/issues/7343
if (_isActivated)
return;

_isActivated = true;

if (_enableResumeEvent)
MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents<WindowsLifecycle.OnResumed>(del => del(this));
Services?.InvokeLifecycleEvents<WindowsLifecycle.OnResumed>(del => del(this));
else
_enableResumeEvent = true;
}
else
{
_isActivated = false;
}

MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents<WindowsLifecycle.OnActivated>(del => del(this, args));
Services?.InvokeLifecycleEvents<WindowsLifecycle.OnActivated>(del => del(this, args));
}

private void OnClosedPrivate(object sender, UI.Xaml.WindowEventArgs args)
Expand All @@ -60,23 +73,25 @@ private void OnClosedPrivate(object sender, UI.Xaml.WindowEventArgs args)
DestroyIcon(_windowIcon);
_windowIcon = IntPtr.Zero;
}

Window = null;
}

protected virtual void OnClosed(object sender, UI.Xaml.WindowEventArgs args)
{
MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents<WindowsLifecycle.OnClosed>(del => del(this, args));
Services?.InvokeLifecycleEvents<WindowsLifecycle.OnClosed>(del => del(this, args));
}

protected virtual void OnVisibilityChanged(object sender, UI.Xaml.WindowVisibilityChangedEventArgs args)
{
MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents<WindowsLifecycle.OnVisibilityChanged>(del => del(this, args));
Services?.InvokeLifecycleEvents<WindowsLifecycle.OnVisibilityChanged>(del => del(this, args));
}

public IntPtr WindowHandle => _windowManager.WindowHandle;

void SubClassingWin32()
{
MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents<WindowsLifecycle.OnPlatformWindowSubclassed>(
Services?.InvokeLifecycleEvents<WindowsLifecycle.OnPlatformWindowSubclassed>(
del => del(this, new WindowsPlatformWindowSubclassedEventArgs(WindowHandle)));

_windowManager.WindowMessage += OnWindowMessage;
Expand Down Expand Up @@ -120,7 +135,7 @@ void OnWindowMessage(object? sender, WindowMessageEventArgs e)
}
}

MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents<WindowsLifecycle.OnPlatformMessage>(
Services?.InvokeLifecycleEvents<WindowsLifecycle.OnPlatformMessage>(
m => m.Invoke(this, new WindowsPlatformMessageEventArgs(e.Hwnd, e.MessageId, e.WParam, e.LParam)));
}
}
Expand Down Expand Up @@ -153,6 +168,12 @@ void SetIcon()

SizeInt32 IPlatformSizeRestrictedWindow.MaximumSize { get; set; } = DefaultMaximumSize;

internal IWindow? Window { get; private set; }

internal IServiceProvider? Services =>
Window?.Handler?.GetServiceProvider() ??
MauiWinUIApplication.Current.Services;

internal UI.Xaml.UIElement? MauiCustomTitleBar
{
get => _customTitleBar;
Expand All @@ -178,6 +199,11 @@ internal void UpdateTitleOnCustomTitleBar()

[DllImport("user32.dll", SetLastError = true)]
static extern int DestroyIcon(IntPtr hIcon);

internal void SetWindow(IWindow window)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lifecycle hooks all reached the xplat window by iterating over the static applications windows. This doesn't work very well for the tests because the tests run their own stubbed app that windows get attached to. This removes the need to resort to static scope in order to get from the platform window to the xplat window

{
Window = window;
}
}

interface IPlatformSizeRestrictedWindow
Expand Down
24 changes: 24 additions & 0 deletions src/Core/src/Platform/Windows/WindowExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ public static void UpdateMaximumSize(this UI.Xaml.Window platformWindow, IWindow
return window;
}

if (platformWindow is MauiWinUIWindow mauiWindow)
return mauiWindow?.Window;

return null;
}

Expand All @@ -198,6 +201,27 @@ public static float GetDisplayDensity(this UI.Xaml.Window platformWindow)
return PlatformMethods.GetDpiForWindow(hwnd) / DeviceDisplay.BaseLogicalDpi;
}

internal static void Minimize(this UI.Xaml.Window platformWindow)
{
PlatformMethods
.ShowWindow(platformWindow.GetWindowHandle(),
PlatformMethods.ShowWindowFlags.SW_MINIMIZE);
}

internal static void Maximize(this UI.Xaml.Window platformWindow)
{
PlatformMethods
.ShowWindow(platformWindow.GetWindowHandle(),
PlatformMethods.ShowWindowFlags.SW_MAXIMIZE);
}

internal static void Restore(this UI.Xaml.Window platformWindow)
{
PlatformMethods
.ShowWindow(platformWindow.GetWindowHandle(),
PlatformMethods.ShowWindowFlags.SW_RESTORE);
}

public static UI.Windowing.AppWindow? GetAppWindow(this UI.Xaml.Window platformWindow)
{
var hwnd = platformWindow.GetWindowHandle();
Expand Down
20 changes: 20 additions & 0 deletions src/Essentials/src/Platform/PlatformMethods.uwp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public static long GetWindowLongPtr(IntPtr hWnd, WindowLongFlags nIndex)
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, SpecialWindowHandles hWndInsertAfter, int x, int y, int width, int height, SetWindowPosFlags uFlags);

[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowFlags uFlags);

[DllImport("comctl32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr DefSubclassProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);

Expand Down Expand Up @@ -145,6 +148,23 @@ public enum SetWindowPosFlags : uint
SWP_SHOWWINDOW = 0x0040,
}

[Flags]
public enum ShowWindowFlags : uint
{
SW_HIDE = 0,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
}

[Flags]
public enum ExtendedWindowStyles : uint
{
Expand Down