Reputation: 129
I´m using ContextMenuStrip in my Form app as my drop down menu. Exactly, when I click on button, ContextMenuStrip shows right under it. Everithing is OK, but I really want to auto-close ContextMenuStrip after mouse leave its area. Ok, so I´m try to use MouseLeave event. Once again, everything is OK, but when I add dropdown items to some ToolStripItem in ContextMenuStrip, the mouseLeave event donť recognize this new area as a part of ContextMenuStrip. This is my newest attempt, but it is not finished. Any idea, how to resolve this problem?
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(); }
}
}
This works fine, when I leave CMS to dropDown items, but it is not working, when I leave them too after. I need to close whole CMS, when I leave any of his areas.
Upvotes: 1
Views: 360
Reputation: 129
As I promise in comments, I want to post another solution for this problem.
Firstly there is MouseLeave method for event handle. This method is common to ContextMenuStrip (CMS) and 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();
}
}
}
}
Above you can see IsMouseOnDropDown and GetPrimaryOwner methods, so there is the code:
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;
}
The last thing what we need to do, is handle the mouseLeave event for every DropDownMenu an ContextmenuStrip
this.ContextMenuStrip1.MouseLeave += new System.EventHandler(ContextMenuStrip_MouseLeave);
this.StripMenuItem1.DropDown.MouseLeave += new System.EventHandler(ContextMenuStrip_MouseLeave);
With this example everything works fine to me, so if you find an error, please let me know. It is just an example, so I dont write Try/Catch there..
Upvotes: 0
Reputation: 7214
Add a region variable which will be ContextMenuStrip
plus the DropDownMenus
.
private Region rgn = new Region();
Initialize region:
public Form1() {
InitializeComponent();
rgn.MakeEmpty();
}
When ContextMenuStrip
opens update region:
private void contextMenuStrip1_Opened( object sender, EventArgs e ) {
rgn.Union( contextMenuStrip1.Bounds );
}
In leave event check if mouse is inside this region:
private void contextMenuStrip1_MouseLeave( object sender, EventArgs e ) {
Point pnt = Cursor.Position;
if( rgn.IsVisible( pnt ) == false ) {
rgn.MakeEmpty();
contextMenuStrip1.Close();
}
}
When you create a new ToolStripDropDownMenu
adding items to eg toolStripMenuItem0
, add these event handlers:
//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();
}
}
Do the same for every DropDownMenu you create
.
Upvotes: 1