diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs
index 893d5ad4c31..f4f638093f6 100644
--- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs
+++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs
@@ -3940,7 +3940,7 @@ internal override void ReleaseUiaProvider(HWND handle)
/// contains ToolStrip or ToolStripDropDown items to disconnect
internal virtual void ReleaseToolStripItemsProviders(ToolStripItemCollection items)
{
- ToolStripItem[] itemsArray = [..items.Cast()];
+ ToolStripItem[] itemsArray = [.. items.Cast()];
foreach (ToolStripItem toolStripItem in itemsArray)
{
if (toolStripItem is ToolStripDropDownItem dropDownItem && dropDownItem.DropDownItems.Count > 0)
@@ -4585,8 +4585,48 @@ protected override void WndProc(ref Message m)
{
SnapFocus((HWND)(nint)m.WParamInternal);
- // For fix https://github.com/dotnet/winforms/issues/12916
- ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this);
+ if (IsDropDown)
+ {
+ // Designer SmartTag panels are hosted in ToolStripDropDown. Keep these
+ // participating in modal menu tracking regardless of previous focus.
+ ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this);
+ }
+
+ // Only activate ModalMenuFilter when focus transfers
+ // between ToolStrips (to fix #12916).
+ // Do NOT activate on generic Tab focus (#14489).
+ else
+ {
+ HWND previousFocus = (HWND)(nint)m.WParamInternal;
+ Control? previousControl = FromChildHandle(previousFocus);
+ if (previousControl is ToolStrip || previousControl?.Parent is ToolStrip)
+ {
+ ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this);
+ }
+ }
+ }
+
+ if (m.MsgInternal == PInvokeCore.WM_KILLFOCUS)
+ {
+ // Clear modal menu tracking when focus leaves ToolStrip context so
+ // keyboard input is not incorrectly routed after tabbing away.
+ // Keep ToolStripDropDowns in menu tracking while they remain visible (for
+ // example, designer SmartTag panels that may temporarily transfer focus).
+ if (!IsDropDown)
+ {
+ HWND nextFocus = (HWND)(nint)m.WParamInternal;
+ Control? nextControl = FromChildHandle(nextFocus);
+ if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() == this
+ && nextControl is not ToolStrip
+ && nextControl?.Parent is not ToolStrip)
+ {
+ ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this);
+ if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() is null)
+ {
+ ToolStripManager.ModalMenuFilter.ExitMenuMode();
+ }
+ }
+ }
}
if (!AllowClickThrough && m.MsgInternal == PInvokeCore.WM_MOUSEACTIVATE)
diff --git a/src/test/integration/UIIntegrationTests/ToolStripTests.cs b/src/test/integration/UIIntegrationTests/ToolStripTests.cs
index 7a72344c41a..baacf2f4f7c 100644
--- a/src/test/integration/UIIntegrationTests/ToolStripTests.cs
+++ b/src/test/integration/UIIntegrationTests/ToolStripTests.cs
@@ -16,6 +16,66 @@ public ToolStripTests(ITestOutputHelper testOutputHelper)
_sharedImageList.Images.Add("square", Image.FromFile("./Resources/image.png"));
}
+ // Regression test for https://github.com/dotnet/winforms/issues/14489
+ [WinFormsFact]
+ public async Task ToolStrip_TabFromTreeView_ThenBack_ArrowKeysStillGoToTreeView()
+ {
+ await RunFormAsync(
+ () =>
+ {
+ Form form = new()
+ {
+ TopMost = true
+ };
+
+ TreeView treeView = new()
+ {
+ Dock = DockStyle.Fill,
+ TabIndex = 0,
+ HideSelection = false
+ };
+ treeView.Nodes.Add("Node 1");
+ treeView.Nodes.Add("Node 2");
+ treeView.SelectedNode = treeView.Nodes[0];
+
+ ToolStrip toolStrip = new()
+ {
+ Dock = DockStyle.Top,
+ TabStop = true,
+ TabIndex = 1
+ };
+ toolStrip.Items.Add(new ToolStripButton("Button"));
+
+ form.Controls.Add(treeView);
+ form.Controls.Add(toolStrip);
+
+ return (form, (treeView, toolStrip));
+ },
+ async (form, controls) =>
+ {
+ var (treeView, toolStrip) = controls;
+
+ treeView.Focus();
+ await WaitForIdleAsync();
+ Assert.True(treeView.Focused);
+ Assert.Equal(treeView.Nodes[0], treeView.SelectedNode);
+
+ Assert.True(form.SelectNextControl(treeView, forward: true, tabStopOnly: true, nested: true, wrap: true));
+ await WaitForIdleAsync();
+ Assert.True(toolStrip.Focused);
+
+ Assert.True(form.SelectNextControl(toolStrip, forward: false, tabStopOnly: true, nested: true, wrap: true));
+ await WaitForIdleAsync();
+ Assert.True(treeView.Focused);
+
+ treeView.Focus();
+ await WaitForIdleAsync();
+ Assert.True(treeView.Focused);
+ Assert.Same(treeView, form.ActiveControl);
+ Assert.Equal(treeView.Nodes[0], treeView.SelectedNode);
+ });
+ }
+
// Regression test for https://github.com/dotnet/winforms/issues/6881
[WinFormsFact]
public void ToolStrip_shared_imagelist_should_not_get_disposed_when_toolstrip_does()
diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ToolStripTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ToolStripTests.cs
index 399936d9432..e9a0821250c 100644
--- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ToolStripTests.cs
+++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ToolStripTests.cs
@@ -7226,6 +7226,38 @@ public void ToolStrip_WndProc_InvokeMouseHoverWithHandle_Success()
Assert.Equal(0, createdCallCount);
}
+ [WinFormsFact]
+ public void ToolStrip_WndProc_InvokeSetFocusFromToolStrip_ActivatesModalMenuFilter()
+ {
+ ToolStripManager.ModalMenuFilter.ExitMenuMode();
+
+ using Form form = new();
+ using SubToolStrip previousToolStrip = new() { TabStop = true };
+ using SubToolStrip currentToolStrip = new() { TabStop = true };
+
+ form.Controls.Add(previousToolStrip);
+ form.Controls.Add(currentToolStrip);
+
+ _ = form.Handle;
+ _ = previousToolStrip.Handle;
+ _ = currentToolStrip.Handle;
+
+ try
+ {
+ Message m = Message.Create(currentToolStrip.Handle, (int)PInvokeCore.WM_SETFOCUS, previousToolStrip.Handle, IntPtr.Zero);
+ currentToolStrip.WndProc(ref m);
+
+ Assert.Same(currentToolStrip, ToolStripManager.ModalMenuFilter.GetActiveToolStrip());
+ Assert.True(ToolStripManager.ModalMenuFilter.InMenuMode);
+ }
+ finally
+ {
+ ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(currentToolStrip);
+ ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(previousToolStrip);
+ ToolStripManager.ModalMenuFilter.ExitMenuMode();
+ }
+ }
+
[WinFormsFact]
public void ToolStrip_KeyboardAccelerators_ReturnsExpected()
{