diff --git a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs index e517b77bc46..55faa35b0cc 100644 --- a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs +++ b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs @@ -65,7 +65,7 @@ public enum CursorState private PortViewModel snappedPort; private double currentNodeCascadeOffset; private Point inCanvasSearchPosition; - private List hitResultsList = new List(); + private Window ownerWindow; static internal event Action RequestShowNodeAutoCompleteBar; private double currentRenderScale = -1; @@ -109,6 +109,8 @@ public WorkspaceView() InitializeComponent(); DataContextChanged += OnWorkspaceViewDataContextChanged; + Loaded += WorkspaceView_Loaded; + Unloaded += WorkspaceView_Unloaded; // view of items to drag draggedSelectionTemplate = (DataTemplate)FindResource("DraggedSelectionTemplate"); @@ -912,6 +914,29 @@ private void OnCanvasMouseDown(object sender, MouseButtonEventArgs e) ViewModel.RequestTogglePanMode(); } } + private void WorkspaceView_Loaded(object sender, RoutedEventArgs e) + { + if (ownerWindow != null) + ownerWindow.Deactivated -= OwnerWindow_Deactivated; + + ownerWindow = Window.GetWindow(this); + if (ownerWindow != null) + ownerWindow.Deactivated += OwnerWindow_Deactivated; + } + + private void WorkspaceView_Unloaded(object sender, RoutedEventArgs e) + { + if (ownerWindow != null) + { + ownerWindow.Deactivated -= OwnerWindow_Deactivated; + ownerWindow = null; + } + } + + private void OwnerWindow_Deactivated(object sender, EventArgs e) + { + DestroyPortContextMenu(); + } /// /// Closes the port's context menu and sets its references to null. @@ -1260,6 +1285,13 @@ public void Dispose() { RemoveViewModelsubscriptions(ViewModel); DataContextChanged -= OnWorkspaceViewDataContextChanged; + Loaded -= WorkspaceView_Loaded; + Unloaded -= WorkspaceView_Unloaded; + if (ownerWindow != null) + { + ownerWindow.Deactivated -= OwnerWindow_Deactivated; + ownerWindow = null; + } } } } diff --git a/test/DynamoCoreWpfTests/CoreUITests.cs b/test/DynamoCoreWpfTests/CoreUITests.cs index 4d925643974..87ec2214446 100644 --- a/test/DynamoCoreWpfTests/CoreUITests.cs +++ b/test/DynamoCoreWpfTests/CoreUITests.cs @@ -1504,5 +1504,44 @@ private void RightClick(IInputElement element) //Wait 3 seconds until the Click Right context menu is opened Task.WaitAll(new Task[] { Task.Delay(2000) }); } + + [Test] + [Category("UnitTests")] + public void PortContextMenu_ClosesWhenOwnerWindowIsDeactivated() + { + // Open a new workspace. + ViewModel.NewHomeWorkspaceCommand.Execute(null); + DispatcherUtil.DoEvents(); + + // Grab the WorkspaceView of the new workspace (it must be loaded so it has + // subscribed to the owner window's Deactivated event). + var currentWs = View.ChildOfType(); + Assert.IsNotNull(currentWs, "DynamoView does not have any WorkspaceView"); + DispatcherUtil.DoEvents(); + + // The port context menu should not be open on a fresh workspace. + Assert.IsFalse(currentWs.PortContextMenu.IsOpen, "Port context menu should not be open on a new workspace"); + + // Open the port context menu popup. + currentWs.PortContextMenu.IsOpen = true; + DispatcherUtil.DoEvents(); + Assert.IsTrue(currentWs.PortContextMenu.IsOpen, "Port context menu popup was not opened"); + + // Simulate the Dynamo window losing focus (e.g. the user switching to another application). + var ownerWindow = Window.GetWindow(currentWs); + Assert.IsNotNull(ownerWindow, "WorkspaceView does not have an owner window"); + RaiseDeactivatedEvent(ownerWindow); + DispatcherUtil.DoEvents(); + + // The port context menu popup must close so it is not left visible on top of other applications. + Assert.IsFalse(currentWs.PortContextMenu.IsOpen, "Port context menu should close when the owner window is deactivated"); + } + + private static void RaiseDeactivatedEvent(Window window) + { + System.Reflection.MethodInfo onDeactivated = typeof(Window).GetMethod("OnDeactivated", + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); + onDeactivated.Invoke(window, new object[] { EventArgs.Empty }); + } } }