Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3940,7 +3940,7 @@ internal override void ReleaseUiaProvider(HWND handle)
/// <param name="items">contains ToolStrip or ToolStripDropDown items to disconnect</param>
internal virtual void ReleaseToolStripItemsProviders(ToolStripItemCollection items)
{
ToolStripItem[] itemsArray = [..items.Cast<ToolStripItem>()];
ToolStripItem[] itemsArray = [.. items.Cast<ToolStripItem>()];
foreach (ToolStripItem toolStripItem in itemsArray)
{
if (toolStripItem is ToolStripDropDownItem dropDownItem && dropDownItem.DropDownItems.Count > 0)
Expand Down Expand Up @@ -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)
Expand Down
60 changes: 60 additions & 0 deletions src/test/integration/UIIntegrationTests/ToolStripTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Loading