【问题标题】:How to check if mouse is in ContextMenuStrip or in its items如何检查鼠标是否在 ContextMenuStrip 或其项目中
【发布时间】:2018-08-29 08:48:32
【问题描述】:

我在我的表单应用程序中使用 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。

【问题讨论】:

    标签: c# mouse mouseleave contextmenustrip


    【解决方案1】:

    添加一个区域变量,它将是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做同样的事情。

    【讨论】:

    • 非常感谢您的回答,但是当我尝试此解决方案时,CMS 永远不会自动关闭,因为在 ContextMenuStrip1_MouseLeave 中的 if 语句 rgn.IsVisible( pnt ) == false 对我来说总是正确的。我试图找出原因,但还没有线索。
    • @Blaato 确实是的!在Form1_Load 事件或构造函数中添加rgn.MakeEmpty();。我会编辑答案
    • 是的,就是这样,谢谢!奇迹般有效。 :) 几分钟前,我什至完成了自己的解决方案。例如,我明天也会在这里添加它,但你的更好
    【解决方案2】:

    正如我在 cmets 中所承诺的,我想针对这个问题发布另一个解决方案。

    首先有用于事件句柄的 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..

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-28
      • 1970-01-01
      • 2016-08-14
      • 2014-10-10
      相关资源
      最近更新 更多