防止ToolStripMenuItems跳转到第二个屏幕

时间:2014-10-27 12:43:25

标签: c# winforms multiple-monitors notifyicon toolstripdropdown

我有一个主要通过NotifyIcon的ContextMenuStrip操作的应用程序 ToolStripMenuItems有多个级别,用户可以浏览它们 问题是,当用户有两个屏幕时,当没有可用空间时,MenuItems跳转到第二个屏幕。像这样:

enter image description here

如何强迫他们留在同一个屏幕上?我试图通过网络搜索但无法找到合适的答案。

以下是我用来测试此场景的一段示例代码:

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);
    }
}

3 个答案:

答案 0 :(得分:3)

这并不容易,但您可以在DropDownOpening事件中编写代码来查看菜单所在的位置(其边界),当前屏幕,然后设置ToolStripMenuItem的DropDownDirection

    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;
        }
    }

另外,请确保已连接DropDownOpening事件(您确实需要将其添加到每个菜单项):

level1ToolStripMenuItem += submenu_DropDownOpening;

答案 1 :(得分:0)

如果在第二个屏幕的左侧 打开菜单,则@David 的代码无法修复。我已经改进了该代码以在所有屏幕角上工作。

private void subMenu_DropDownOpening(object sender, EventArgs e)
    {
        ToolStripMenuItem mnuItem = sender as ToolStripMenuItem;
        if (mnuItem.HasDropDownItems == false)
        {
            return; // not a drop down item
        }

        //get position of current menu item
        var pos = new Point(mnuItem.GetCurrentParent().Left, mnuItem.GetCurrentParent().Top);

        // Current bounds of the current monitor
        Rectangle bounds = Screen.GetWorkingArea(pos);
        Screen currentScreen = Screen.FromPoint(pos);

        // Find the width of sub-menu
        int maxWidth = 0;
        foreach (var subItem in mnuItem.DropDownItems)
        {
            if (subItem.GetType() == typeof(ToolStripMenuItem))
            {
                var mnu = (ToolStripMenuItem) subItem;
                maxWidth = Math.Max(mnu.Width, maxWidth);
            }
        }
        maxWidth += 10; // Add a little wiggle room


        int farRight = pos.X + mnuMain.Width + maxWidth;
        int farLeft = pos.X - maxWidth;

        //get left and right distance to compare
        int leftGap = farLeft - currentScreen.Bounds.Left;
        int rightGap = currentScreen.Bounds.Right - farRight;


        if (leftGap >= rightGap)
        {
            mnuItem.DropDownDirection = ToolStripDropDownDirection.Left;
        }
        else
        {
            mnuItem.DropDownDirection = ToolStripDropDownDirection.Right;
        }
    }

答案 2 :(得分:0)

我已经这样解决了:

  1. 为使ContextMenuStrip本身在所需的屏幕上打开,我使用以下方法创建了ContextMenuStripEx:

    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
    {
        Rectangle dropDownBounds = new Rectangle(x, y, width, height);
    
        dropDownBounds = ConstrainToBounds(Screen.FromPoint(dropDownBounds.Location).Bounds, dropDownBounds);
    
        base.SetBoundsCore(dropDownBounds.X, dropDownBounds.Y, dropDownBounds.Width, dropDownBounds.Height, specified);
    }
    
    internal static Rectangle ConstrainToBounds(Rectangle constrainingBounds, Rectangle bounds)
    {
        if (!constrainingBounds.Contains(bounds))
        {
            bounds.Size = new Size(Math.Min(constrainingBounds.Width - 2, bounds.Width), Math.Min(constrainingBounds.Height - 2, bounds.Height));
            if (bounds.Right > constrainingBounds.Right)
            {
                bounds.X = constrainingBounds.Right - bounds.Width;
            }
            else if (bounds.Left < constrainingBounds.Left)
            {
                bounds.X = constrainingBounds.Left;
            }
            if (bounds.Bottom > constrainingBounds.Bottom)
            {
                bounds.Y = constrainingBounds.Bottom - 1 - bounds.Height;
            }
            else if (bounds.Top < constrainingBounds.Top)
            {
                bounds.Y = constrainingBounds.Top;
            }
        }
        return bounds;
    }
    

(ConstrainToBounds方法通过反射器从基类ToolStripDropDown中获取)

  1. 为使嵌套的MenuItem与ContextMenuStrip在同一屏幕上打开,我创建了一个ToolStripMenuItemEx(从ToolStripMenuItem派生)。就我而言,它看起来像这样:

    private ToolStripDropDownDirection? originalToolStripDropDownDirection;
    
    protected override void OnDropDownShow(EventArgs e)
    {
        base.OnDropDownShow(e);
    
        if (!Screen.FromControl(this.Owner).Equals(Screen.FromPoint(this.DropDownLocation)))
        {
            if (!originalToolStripDropDownDirection.HasValue)
                originalToolStripDropDownDirection = this.DropDownDirection;
    
            this.DropDownDirection = originalToolStripDropDownDirection.Value == ToolStripDropDownDirection.Left ? ToolStripDropDownDirection.Right : ToolStripDropDownDirection.Left;
        }
    }