CmdPal: Ensure DockWindow property cleans up after itself (#46303)

## Summary of the Pull Request

This PR improves cleanup of DockWindow after itself (since it can be
created and destroyed multiple times during app lifetime).

- Disposes its ViewModel (which it creates).
- Unregisters itself explicitly from WeakReferenceMessenger.
- Ensures that ShellPage closes the dock window when disposed and can't
spawn more.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #46302
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Zach Teutsch <88554871+zateutsch@users.noreply.github.com>
This commit is contained in:
Jiří Polášek
2026-03-24 23:24:00 +01:00
committed by GitHub
parent 6cf1d32e5a
commit 1a9fcdcd1f
2 changed files with 60 additions and 7 deletions

View File

@@ -5,7 +5,6 @@
using System.Runtime.InteropServices;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Dock;
using Microsoft.CmdPal.UI.ViewModels.Messages;
@@ -27,7 +26,6 @@ using Windows.Win32.UI.WindowsAndMessaging;
using WinRT;
using WinRT.Interop;
using WinUIEx;
using WindowExtensions = Microsoft.CmdPal.UI.Helpers.WindowExtensions;
namespace Microsoft.CmdPal.UI.Dock;
@@ -62,6 +60,7 @@ public sealed partial class DockWindow : WindowEx,
private bool _isUpdatingBackdrop;
private BackdropParameters? _lastAppliedAcrylicBackdrop;
private DockSize _lastSize;
private bool _isDisposed;
// Store the original WndProc
private WNDPROC? _originalWndProc;
@@ -671,18 +670,38 @@ public sealed partial class DockWindow : WindowEx,
public void Dispose()
{
DisposeAcrylic();
_windowViewModel.Dispose();
Cleanup();
GC.SuppressFinalize(this);
}
private void DockWindow_Closed(object sender, WindowEventArgs args)
{
_settingsService.SettingsChanged -= SettingsChangedHandler;
Dispose();
}
private void Cleanup()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
_settingsService?.SettingsChanged -= SettingsChangedHandler;
Activated -= DockWindow_Activated;
_themeService.ThemeChanged -= ThemeService_ThemeChanged;
WeakReferenceMessenger.Default.UnregisterAll(this);
DisposeAcrylic();
_windowViewModel.Dispose();
// Remove our app bar registration
DestroyAppBar(_hwnd);
if (_appBarData.hWnd != IntPtr.Zero)
{
DestroyAppBar(_hwnd);
}
// Unhook the window procedure
ShowDesktop.RemoveHook();

View File

@@ -73,6 +73,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
private CancellationTokenSource? _focusAfterLoadedCts;
private WeakReference<Page>? _lastNavigatedPageRef;
private bool _isDisposed;
public ShellViewModel ViewModel { get; private set; } = App.Current.Services.GetService<ShellViewModel>()!;
@@ -308,6 +309,11 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
{
_ = DispatcherQueue.TryEnqueue(() =>
{
if (_isDisposed)
{
return;
}
OpenSettings(message.SettingsPageTag);
});
}
@@ -528,6 +534,11 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
{
_ = DispatcherQueue.TryEnqueue(() =>
{
if (_isDisposed)
{
return;
}
if (message.ShowDock)
{
if (_dockWindow is null)
@@ -829,10 +840,33 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
public void Dispose()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
WeakReferenceMessenger.Default.UnregisterAll(this);
_focusAfterLoadedCts?.Cancel();
_focusAfterLoadedCts?.Dispose();
_focusAfterLoadedCts = null;
_dockWindow?.Dispose();
var dockWindow = _dockWindow;
_dockWindow = null;
if (dockWindow is not null)
{
if (DispatcherQueue.HasThreadAccess)
{
dockWindow.Close();
}
else
{
DispatcherQueue.TryEnqueue(dockWindow.Close);
}
}
GC.SuppressFinalize(this);
}
}