mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 01:36:31 +02:00
CmdPal: Fix dock popup XamlRoot handling on DockControl (#46305)
## Summary of the Pull Request This PR handles situations when app can crash because a popup control is being touched before XamlRoot is set. - Registers message handlers in DockControl only while controls are loaded. - Guards the edit-mode TeachingTip until the dock is rooted. - Sets XamlRoot for dock flyouts and tooltips before showing. - Ensures that tooltips in DockItemControl are set only after XamlRoot is explicitly set. - Unregisteres messages and CenterItems_CollectionChanged when unloaded. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #46228 <!-- - [ ] 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
This commit is contained in:
@@ -67,15 +67,52 @@ public sealed partial class DockControl : UserControl, IRecipient<CloseContextMe
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
InitializeComponent();
|
||||
WeakReferenceMessenger.Default.Register<CloseContextMenuMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<EnterDockEditModeMessage>(this);
|
||||
|
||||
ViewModel.CenterItems.CollectionChanged += CenterItems_CollectionChanged;
|
||||
Loaded += DockControl_Loaded;
|
||||
Unloaded += DockControl_Unloaded;
|
||||
|
||||
// Start with edit mode disabled - normal click behavior
|
||||
UpdateEditMode(false);
|
||||
}
|
||||
|
||||
private void DockControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
WeakReferenceMessenger.Default.Register<CloseContextMenuMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<EnterDockEditModeMessage>(this);
|
||||
|
||||
ViewModel.CenterItems.CollectionChanged -= CenterItems_CollectionChanged;
|
||||
ViewModel.CenterItems.CollectionChanged += CenterItems_CollectionChanged;
|
||||
|
||||
UpdateEditModeTeachingTip();
|
||||
}
|
||||
|
||||
private void DockControl_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
|
||||
ViewModel.CenterItems.CollectionChanged -= CenterItems_CollectionChanged;
|
||||
|
||||
if (EditButtonsTeachingTip.IsOpen)
|
||||
{
|
||||
EditButtonsTeachingTip.IsOpen = false;
|
||||
}
|
||||
|
||||
if (ContextMenuFlyout.IsOpen)
|
||||
{
|
||||
ContextMenuFlyout.Hide();
|
||||
}
|
||||
|
||||
if (AddBandFlyout.IsOpen)
|
||||
{
|
||||
AddBandFlyout.Hide();
|
||||
}
|
||||
|
||||
if (EditModeContextMenu.IsOpen)
|
||||
{
|
||||
EditModeContextMenu.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void CenterItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateCenterVisibility();
|
||||
@@ -125,7 +162,38 @@ public sealed partial class DockControl : UserControl, IRecipient<CloseContextMe
|
||||
};
|
||||
}
|
||||
|
||||
EditButtonsTeachingTip.IsOpen = isEditMode;
|
||||
UpdateEditModeTeachingTip();
|
||||
}
|
||||
|
||||
private void UpdateEditModeTeachingTip()
|
||||
{
|
||||
if (XamlRoot is null || ContentGrid.XamlRoot is null || EditButtonsTeachingTip.Parent is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsEditMode)
|
||||
{
|
||||
if (EditButtonsTeachingTip.IsOpen)
|
||||
{
|
||||
EditButtonsTeachingTip.IsOpen = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EditButtonsTeachingTip.IsOpen)
|
||||
{
|
||||
EditButtonsTeachingTip.IsOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PreparePopupForShow(FlyoutBase popup, FrameworkElement placementTarget)
|
||||
{
|
||||
if (placementTarget.XamlRoot is not null && popup.XamlRoot != placementTarget.XamlRoot)
|
||||
{
|
||||
popup.XamlRoot = placementTarget.XamlRoot;
|
||||
}
|
||||
}
|
||||
|
||||
internal void EnterEditMode()
|
||||
@@ -214,6 +282,7 @@ public sealed partial class DockControl : UserControl, IRecipient<CloseContextMe
|
||||
ShowTitlesMenuItem.IsChecked = _editModeContextBand.ShowTitles;
|
||||
ShowSubtitlesMenuItem.IsChecked = _editModeContextBand.ShowSubtitles;
|
||||
|
||||
PreparePopupForShow(EditModeContextMenu, dockItem);
|
||||
EditModeContextMenu.ShowAt(
|
||||
dockItem,
|
||||
new FlyoutShowOptions()
|
||||
@@ -232,6 +301,7 @@ public sealed partial class DockControl : UserControl, IRecipient<CloseContextMe
|
||||
{
|
||||
ContextControl.ViewModel.SelectedItem = item;
|
||||
ContextControl.ShowFilterBox = true;
|
||||
PreparePopupForShow(ContextMenuFlyout, dockItem);
|
||||
ContextMenuFlyout.ShowAt(
|
||||
dockItem,
|
||||
new FlyoutShowOptions()
|
||||
@@ -320,6 +390,7 @@ public sealed partial class DockControl : UserControl, IRecipient<CloseContextMe
|
||||
{
|
||||
ContextControl.ViewModel.SelectedItem = item;
|
||||
ContextControl.ShowFilterBox = false;
|
||||
PreparePopupForShow(ContextMenuFlyout, RootGrid);
|
||||
ContextMenuFlyout.ShowAt(
|
||||
this.RootGrid,
|
||||
new FlyoutShowOptions()
|
||||
@@ -516,6 +587,7 @@ public sealed partial class DockControl : UserControl, IRecipient<CloseContextMe
|
||||
AddBandListView.Visibility = hasAvailableBands ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
// Show the flyout
|
||||
PreparePopupForShow(AddBandFlyout, button);
|
||||
AddBandFlyout.ShowAt(button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,7 @@ public sealed partial class DockItemControl : Control
|
||||
{
|
||||
if (d is DockItemControl control)
|
||||
{
|
||||
// Collapse the tooltip when the string is null or empty so an
|
||||
// empty tooltip bubble doesn't appear on hover.
|
||||
var text = e.NewValue as string;
|
||||
ToolTipService.SetToolTip(control, string.IsNullOrEmpty(text) ? null : text);
|
||||
control.UpdateToolTip();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +88,7 @@ public sealed partial class DockItemControl : Control
|
||||
|
||||
private FrameworkElement? _iconPresenter;
|
||||
private DockControl? _parentDock;
|
||||
private ToolTip? _toolTip;
|
||||
private long _dockSideCallbackToken = -1;
|
||||
|
||||
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
@@ -184,9 +182,33 @@ public sealed partial class DockItemControl : Control
|
||||
{
|
||||
UpdateTextVisibility();
|
||||
UpdateIconVisibility();
|
||||
UpdateToolTip();
|
||||
UpdateAlignment();
|
||||
}
|
||||
|
||||
private void UpdateToolTip()
|
||||
{
|
||||
var text = ToolTip;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
ToolTipService.SetToolTip(this, null);
|
||||
_toolTip = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until the control is connected to a XamlRoot before creating
|
||||
// the tooltip popup; dock items are materialized very early in startup.
|
||||
if (XamlRoot is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_toolTip ??= new ToolTip();
|
||||
_toolTip.Content = text;
|
||||
_toolTip.XamlRoot = XamlRoot;
|
||||
ToolTipService.SetToolTip(this, _toolTip);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
@@ -232,6 +254,8 @@ public sealed partial class DockItemControl : Control
|
||||
DockControl.DockSideProperty,
|
||||
OnParentDockSideChanged);
|
||||
}
|
||||
|
||||
UpdateToolTip();
|
||||
}
|
||||
|
||||
private void DockItemControl_ActualThemeChanged(FrameworkElement sender, object args)
|
||||
@@ -250,6 +274,9 @@ public sealed partial class DockItemControl : Control
|
||||
_dockSideCallbackToken = -1;
|
||||
_parentDock = null;
|
||||
}
|
||||
|
||||
ToolTipService.SetToolTip(this, null);
|
||||
_toolTip = null;
|
||||
}
|
||||
|
||||
private void OnParentDockSideChanged(DependencyObject sender, DependencyProperty dp)
|
||||
|
||||
Reference in New Issue
Block a user