Как проверить, находится ли мышь в ContextMenuStrip или в ее элементах - PullRequest
0 голосов
/ 29 августа 2018

Я использую ContextMenuStrip в своем приложении Form в качестве раскрывающегося меню. Точно, когда я нажимаю на кнопку, ContextMenuStrip показывает прямо под ней. Все в порядке, но я действительно хочу автоматически закрыть ContextMenuStrip после того, как мышь покинет его область. Итак, я пытаюсь использовать событие MouseLeave. Еще раз, все в порядке, но когда я добавляю выпадающие элементы к некоторому ToolStripItem в ContextMenuStrip, событие 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, когда я покину любую из его областей.

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Как я обещаю в комментариях, я хочу опубликовать другое решение этой проблемы.

Во-первых, есть метод 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;
    }

Последнее, что нам нужно сделать, - это обработать событие mouseLeave для каждого DropDownMenu и ContextmenuStrip

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

В этом примере у меня все работает нормально, поэтому, если вы обнаружите ошибку, пожалуйста, дайте мне знать. Это всего лишь пример, поэтому я не пишу здесь Try / Catch ..

0 голосов

Добавьте переменную региона, которая будет 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.

...