Search code examples
c#winformsmultiple-monitorsnotifyicontoolstripdropdown

Prevent ToolStripMenuItems from jumping to second screen


I have an application that is mostly operated through NotifyIcon's ContextMenuStrip
There are multiple levels of ToolStripMenuItems and the user can go through them.
The problem is, that when the user has two screen, the MenuItems jump to second screen when no space is available. like so:

enter image description here

How can I force them to stay on the same screen? I've tried to search through the web but couldn't find an appropriate answer.

Here is a sample piece of code i'm using to test this senario:

public partial class Form1 : Form
{

    public Form1()
    {
        InitializeComponent();

        var resources = new ComponentResourceManager(typeof(Form1));
        var notifyIcon1 = new NotifyIcon(components);
        var contextMenuStrip1 = new ContextMenuStrip(components);
        var level1ToolStripMenuItem = new ToolStripMenuItem("level 1 drop down");
        var level2ToolStripMenuItem = new ToolStripMenuItem("level 2 drop down");
        var level3ToolStripMenuItem = new ToolStripMenuItem("level 3 drop down");

        notifyIcon1.ContextMenuStrip = contextMenuStrip1;
        notifyIcon1.Icon = ((Icon)(resources.GetObject("notifyIcon1.Icon")));
        notifyIcon1.Visible = true;

        level2ToolStripMenuItem.DropDownItems.Add(level3ToolStripMenuItem);
        level1ToolStripMenuItem.DropDownItems.Add(level2ToolStripMenuItem);
        contextMenuStrip1.Items.Add(level1ToolStripMenuItem);
    }
}

Solution

  • It is not easy, but you can write code in the DropDownOpening event to look at where the menu is at (its bounds), the current screen, and then set the DropDownDirection of the ToolStripMenuItem:

    private void submenu_DropDownOpening(object sender, EventArgs e)
    {
        ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
        if (menuItem.HasDropDownItems == false)
        {
            return; // not a drop down item
        }
        // Current bounds of the current monitor
        Rectangle Bounds = menuItem.GetCurrentParent().Bounds;
        Screen CurrentScreen = Screen.FromPoint(Bounds.Location);
        // Look how big our children are:
        int MaxWidth = 0;
        foreach (ToolStripMenuItem subitem in menuItem.DropDownItems)
        {
            MaxWidth = Math.Max(subitem.Width, MaxWidth);
        }
        MaxWidth += 10; // Add a little wiggle room
    
        int FarRight = Bounds.Right + MaxWidth;
        int CurrentMonitorRight = CurrentScreen.Bounds.Right;
    
        if (FarRight > CurrentMonitorRight)
        {
            menuItem.DropDownDirection = ToolStripDropDownDirection.Left;
        }
        else
        {
            menuItem.DropDownDirection = ToolStripDropDownDirection.Right;
        }
    }
    

    Also, make sure you have the DropDownOpening event hooked up (you would really need to add this to every menu item):

    level1ToolStripMenuItem += submenu_DropDownOpening;