5

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

在此处输入图像描述

我怎样才能强迫他们留在同一个屏幕上?我试图通过网络搜索,但找不到合适的答案。

这是我用来测试此场景的示例代码:

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

4 回答 4

6

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

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;
于 2015-11-13T19:16:09.240 回答
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 方法通过 Reflector 取自基类 ToolStripDropDown)

  1. 为了让嵌套的 MenuItems 在与 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;
        }
    }
    
于 2018-08-21T10:07:12.420 回答
-1

如果菜单在第二个屏幕的左侧打开,@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;
        }
    }
于 2018-04-03T09:24:13.433 回答
-2

我没有尝试tombam的解决方案。但由于其他人似乎没有工作,我想出了这个简单的解决方案:

        private void MenuDropDownOpening(object sender, EventArgs e)
    {
        var menuItem = sender as ToolStripDropDownButton;
        if (menuItem == null || menuItem.HasDropDownItems == false)
            return; // not a drop down item

        // Current bounds of the current monitor
        var upperRightCornerOfMenuInScreenCoordinates = menuItem.GetCurrentParent().PointToScreen(new Point(menuItem.Bounds.Right, menuItem.Bounds.Top));
        var currentScreen = Screen.FromPoint(upperRightCornerOfMenuInScreenCoordinates);

        // Get width of widest child item (skip separators!)
        var maxWidth = menuItem.DropDownItems.OfType<ToolStripMenuItem>().Select(m => m.Width).Max();

        var farRight = upperRightCornerOfMenuInScreenCoordinates.X + maxWidth;
        var currentMonitorRight = currentScreen.Bounds.Right;

        menuItem.DropDownDirection = farRight > currentMonitorRight ? ToolStripDropDownDirection.Left :
            ToolStripDropDownDirection.Right;
    }

请注意,在我的世界中,我并不关心多个级别的级联菜单(如在 OP 中),因此我没有在那种情况下测试我的解决方案。但这适用于 ToolStrip 上的单个 ToolStripDropDownButton。

于 2021-08-17T16:32:20.943 回答