如何检查鼠标是否在 ContextMenuStrip 或其项目中

How to check if mouse is in ContextMenuStrip or in its items

我在我的表单应用程序中使用 ContextMenuStrip 作为我的下拉菜单。确切地说,当我单击按钮时,ContextMenuStrip 显示在它的正下方。一切都很好,但我真的想在鼠标离开其区域后自动关闭 ContextMenuStrip。好的,所以我尝试使用 MouseLeave 事件。再一次,一切正常,但是当我将下拉项添加到 ContextMenuStrip 中的某些 ToolStripItem 时,mouseLeave 事件没有将这个新区域识别为 ContextMenuStrip 的一部分。这是我最新的尝试,但还没有完成。知道如何解决这个问题吗?

    private void ContextMenuStrip_MouseLeave(object sender, EventArgs e)
    {
        ContextMenuStrip cms = (sender is ContextMenuStrip) ? sender as ContextMenuStrip : null;

        if (cms != null)
        {
            //List<Rectangle> cmsFullArea = new List<Rectangle>();
            //cmsFullArea.Add(new Rectangle(cms.Bounds.Location, cms.Bounds.Size));

            bool itemIsPressed = false;
            for (int i = 0; i < cms.Items.Count; i++)
            {
                if (cms.Items[i].Pressed) { itemIsPressed = true; break; }
            }

            if (!itemIsPressed) { cms.Close(); }
        }
    }

当我离开 CMS 以下拉项目时,这工作正常,但当我离开它们时,它不起作用。当我离开他的任何区域时,我需要关闭整个 CMS。

添加一个区域变量,它将 ContextMenuStrip 加上 DropDownMenus

private Region rgn = new Region();

初始化区域:

public Form1() {
    InitializeComponent();

    rgn.MakeEmpty();
}

ContextMenuStrip打开更新区域时:

private void contextMenuStrip1_Opened( object sender, EventArgs e ) {
    rgn.Union( contextMenuStrip1.Bounds );
}

在离开事件中检查鼠标是否在该区域内:

private void contextMenuStrip1_MouseLeave( object sender, EventArgs e ) {
    Point pnt = Cursor.Position;

    if( rgn.IsVisible( pnt ) == false ) {
        rgn.MakeEmpty();

        contextMenuStrip1.Close();
    }
}

当您创建一个新的 ToolStripDropDownMenu 添加项目到例如 toolStripMenuItem0 时,添加这些事件处理程序:

//toolStripMenuItem0 is an item of your ContextMenuStrip
toolStripMenuItem0.DropDown.MouseLeave += DropDown_MouseLeave;
toolStripMenuItem0.DropDown.Opened += DropDown_Opened;
toolStripMenuItem0.DropDown.Closed += DropDown_Closed;

private void DropDown_Closed( object sender, ToolStripDropDownClosedEventArgs e ) {
    ToolStripDropDownMenu tsddm = (ToolStripDropDownMenu)sender;

    rgn.Exclude( tsddm.Bounds ); //remove rect from region
}

private void DropDown_Opened( object sender, EventArgs e ) {
    ToolStripDropDownMenu tsddm = (ToolStripDropDownMenu)sender;

    rgn.Union( tsddm.Bounds ); //add rect to region
}

private void DropDown_MouseLeave( object sender, EventArgs e ) {
    Point pnt = Cursor.Position;

    if( rgn.IsVisible( pnt ) == false ) {
        rgn.MakeEmpty();

        contextMenuStrip1.Close();
    }
}

每个 DropDownMenu you create做同样的事情。

正如我在评论中承诺的那样,我想 post 解决此问题的另一种方法。

首先是事件句柄的MouseLeave方法。此方法对 ContextMenuStrip (CMS) 和 ToolStripDropDownMenu (TSDDM) 通用。

    private void ContextMenuStrip_MouseLeave(object sender, EventArgs e)
    {
        ContextMenuStrip cms = (sender is ContextMenuStrip) ? sender as ContextMenuStrip : null;

        //Recognize CMS or TSDDM, in this case we dont need anything else
        if (cms != null)
        {
            //Check, if mouse position is on any of CMS DropDownMenus. 
            //If false, close CMS. If true, we dont want to close it - CMS is actively in use
            if (!IsMouseOnDropDown(cms.Items)) { cms.Close(); }
        }
        else
        {
            ToolStripDropDownMenu ddm = (sender is ToolStripDropDownMenu) ? sender as ToolStripDropDownMenu : null;

            if (ddm != null)
            {
                //As above, check mouse position against items DropDownMenus
                if (IsMouseOnDropDown(ddm.Items)) { return; }

                //Declare our CMS
                cms = GetPrimaryOwner(ddm);

                //Get TSDDM owner
                //var is important here, because we dont know if it is CMS or another TSDDM!!!
                //Also TSDDM and CMS have the same properties for our purpose, so var is OK
                var owner = ddm.OwnerItem.Owner;

                Point pnt = Cursor.Position;

                //If owner doesn't contains mouse position, close whole CMS
                if (!owner.Bounds.Contains(pnt))
                {
                    cms.Close();
                }
                else
                {
                    //If does, we need to check if mouse position is exactly on parent item, 
                    //because its prevent to TSDDM unnecessary close/open 
                    //(explanation: Mouse leave TSDDM -> TSDDM close; 
                                  //Mouse is on parent item -> TSDDM open)
                    for (int i = 0; i < owner.Items.Count; i++)
                    {
                        //Define own rectangle, because item has its own bounds against the owner
                        //so we need to add up their X and Y to get the real one
                        int x = owner.Bounds.X + (owner.Items[i] as ToolStripMenuItem).Bounds.X;
                        int y = owner.Bounds.Y + (owner.Items[i] as ToolStripMenuItem).Bounds.Y;
                        Rectangle rect = new Rectangle(new Point(x, y), (pupik.Items[i] as ToolStripMenuItem).Bounds.Size);

                        //If its our DropDownMenu and mouse position is in there,
                        //we dont want to close ddm
                        if ((owner.Items[i] as ToolStripMenuItem).DropDown == ddm
                            && rect.Contains(pnt))
                        {
                            return;
                        }
                    }

                    ddm.Close();
                }
            }
        }
    }

上面可以看到IsMouseOnDropDown和GetPrimaryOwner方法,所以有代码:

    private static bool IsMouseOnDropDown(ToolStripItemCollection itemCollection)
    {
        Point pnt = Cursor.Position;

        //All what we do is check, if some of DropDownMenus from input collection is active (Visible) 
        //and if mouse position is in it
        for (int i = 0; i < itemCollection.Count; i++)
        {
            if ((itemCollection[i] as ToolStripMenuItem).DropDown.Visible
                && (itemCollection[i] as ToolStripMenuItem).DropDown.Bounds.Contains(pnt))
            {
                return true;
            }
        }

        return false;
    }

    private static ContextMenuStrip GetPrimaryOwner(ToolStripDropDownMenu dropDownMenu)
    {
        //All what we do is take owner by owner until we find our CMS,
        //which is the last one -> primary owner
        object cmsItems = dropDownMenu;

        while (!(cmsItems is ContextMenuStrip))
        {
            cmsItems = (cmsItems as ToolStripDropDownMenu).OwnerItem.Owner;
        }

        return cmsItems as ContextMenuStrip;
    }

我们需要做的最后一件事是处理每个 DropDownMenu 和 ContextmenuStrip 的 mouseLeave 事件

   this.ContextMenuStrip1.MouseLeave += new System.EventHandler(ContextMenuStrip_MouseLeave);
   this.StripMenuItem1.DropDown.MouseLeave += new System.EventHandler(ContextMenuStrip_MouseLeave);

对于这个例子,我觉得一切正常,所以如果您发现错误,请告诉我。 这只是一个例子,所以我没有在那里写 Try/Catch..