Click here to Skip to main content
Licence CPOL
First Posted 29 Nov 2010
Views 9,634
Downloads 193
Bookmarked 12 times

Right Mouse Click Menu Control

By | 29 Nov 2010 | Article
A C# control that pops up in a cascade of submenus on a right-click

Why Another Mouse Control?

There are plenty of ways to get your user to tell you what he needs, but you can always find newer and better ones. Still, for no other reason than "I just felt like making one", I wrote the one described here. This user control is intended to be used with the right-mouse-click and appears as a menu under your mouse wherever it is on the screen at the time and can be expanded to any number of levels.

See the screen capture below taken near the right edge of the screen.

screen_capture.PNG

You can see in the image above that the user's options are submenus of submenus. You can capture the results when your user clicks on any one of the options or moves the mouse away from it and it disappears.

Using the Control

To use this control is quite simple. You create an instance of it, set up the menus in the form of a string and then give it an event-handler for when it is disposed.

void formRightClickMenu_Example_MouseClick(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {
                string[] strButtons = { "file(save+load+exit)",
                                        "Level(one+two+three+four)",
                                        "options(color(background+foreground)+
					shadow+stoptime(on+off))"};
        formRightClick frmRightClick = new formRightClick(strButtons);
        frmRightClick.bolPasteToClipBoard = true; // for debugging only
        frmRightClick.Owner = this;
        frmRightClick.frmMain = this;
        frmRightClick.TopMost = true;
        frmRightClick.Show();
        frmRightClick.Disposed += new EventHandler(frmRightClick_Disposed);
    }
}

Then to find out what the user has selected, you go to the frmRightClick_Disposed that you just added above and read the string there.

void frmRightClick_Disposed(object sender, EventArgs e)
{
    formRightClick frmRightClick = (formRightClick)sender;
    string strClicked = frmRightClick.strClick.ToUpper();
    switch (strClicked)
    {
        case "FILE+SAVE":
            MessageBox.Show("save the file");
            break;

        default:
            if (strClicked.Length>0)
            MessageBox.Show("result :" + strClicked);
            break;
    }
}

Optionally, for debugging purposes, if you're not sure about your typing, you can place a break at the switch in the event-handler and then paste the results of what you selected directly from the clipboard. to enable this set:

frmRightClick.bolPasteToClipBoard = true; // for debugging only 

which you'll probably want to set to false(default setting) before you compile.

Setting Up the Menus

You may have noticed above that the menus have to be set up as an array of strings. Each string is intended to be a menu option in the right-click form being created. and each of these menu options can have as manu submenu options as you want. To define each submenu, you encapsulate the submenus between two round-brackets and then insert a '+' character between each submenu. for sub-sub-menus(and so on) you just do the same. You can see this in the example above where the options menu has three submenus (color, shadow & stop-time) and both the color and stop-time submenus have submenus of their own which are also encapsulated between two rounded brackets.

string[] strButtons = { "do a cartwheel(on the(roof+table(beside the(vestibule+
	treasure chest+wall)+against the(wall+door+cabinet)+in the
	(recepticle+bedroom+roadhouse))+floor)+in the(office+hallway+stairwell))",
	"smile(like (a(retard+moron+movie star)+an(idol+idiot))+
	so that(you(look(good+bad))+they(cry+cheer)))",
	"quit"};

The image shown at the top was taken using the button definition shown here.

To keep from getting confused when created a complicated set of sub-sub and then sub menus, I suggest you add the open and close brackets at every level before adding each of the menu-options that appear between them.

Or else you'll be counting brackets, and that's a bad way to be.

How It Works

When you call the form with the first set of menus, each menu-option is stripped of its submenus and these are tagged to it as a string. The labels themselves are of type LabelItem which have only one string variable that holds its submenu string and a pointer to its parent form. These inherit another type of control that I wrote and included here called the LabelButton. The LabelButtons are handy because they have both dull & highlight appearances, a boolean flag along with set() and reset() functions which are used by this control to highlight the menu options that lead up to the one currently being selected. Each of these LabelButtons gets highlighted when the mouse enters and then are set() only if the mouse leaves by entering into its submenus. As the mouse enters one of these labelbuttons, the submenus are read off them and then converted into an array of strings which are sent to the sub-menu form.

The submenu form is just another instance of the same control with the menu options string array pulled LabelItem's string var strSubMenu and a running strClick is kept to remember all the steps we've taken to get where we are. Since each instance of the formRightClick is stored into a static array, we can test if the mouse is under any one of these at the MouseLeave event handler. We do this by cycling through each instance in this static array from the most recent to the earliest so that if the cascade of forms "bounced" off the edge of the screen and made its way backwards(and appears over top of an earlier one) we're always looking at the deepest instance the mouse is over rather than an earlier one which allows us to go as deep into menu-sub-menu and then sub-more menus (forgive my bad spelling, I have a cold) ad infinitum.

Have a look below :

void lbtn_MouseEnter(object sender, EventArgs e)
{
	LabelItem lbtn = (LabelItem)sender;
	if (lbtn.frmSubMenu != null)
		return;
	if (frmMyOffspring != null && frmMyOffspring != lbtn.frmSubMenu)
	{
		frmMyOffspring.Dispose();
	}
	lbtn.set();
	// sub-menued item
	string[] strArray = new string[0];
	if (lbtn.strSubMenu.Length == 0)
		return;

	string strLbtnSubMenu = lbtn.strSubMenu;

	while (strLbtnSubMenu.Length > 0)
	{
		string strNextItem = getNextSubMenu(strLbtnSubMenu);
		strLbtnSubMenu = strLbtnSubMenu.Length > strNextItem.Length ? 
			strLbtnSubMenu.Substring(strNextItem.Length + 1) : "";
		Array.Resize<string />(ref strArray, strArray.Length + 1);
		strArray[strArray.Length - 1] = strNextItem;
	}

	frmMyOffspring
		= lbtn.frmSubMenu
		= new formRightClick(strArray);
	frmMyOffspring.bolPasteToClipBoard = bolPasteToClipBoard;
	lbtn.frmSubMenu.bolPlaceUnderMouse = false;

	lbtn.frmSubMenu.Owner = this;
	lbtn.frmSubMenu.frmMain = frmMain;
	lbtn.frmSubMenu.strParent = strParent + lbtn.Text.Replace
				(strSubMenuArrow, "").Trim() + "+";
	lbtn.frmSubMenu.Show();

	strClick = strParent + frmMyOffspring.strClick;
	if (frmMyOffspring.strClick.Length > 0)
		Dispose();
}

We peel off the submenus in the function getNextSubMenu() shown below by tracing through the string looking at the '(' ')' and '+' characters.

string getNextSubMenu(string strSubMenu)
{
        int intCountOpenBrackets = 0;

        for (int intCharCounter = 0; intCharCounter < strSubMenu.Length; intCharCounter++)
        {
          char chrThis = strSubMenu[intCharCounter];
          if (chrThis == ')')
            intCountOpenBrackets --;
          if (chrThis == '(')
            intCountOpenBrackets++;
          if (chrThis == '+' && intCountOpenBrackets == 0)
            return strSubMenu.Substring(0, intCharCounter);
        }
        return strSubMenu;
}

This is just a matter of keeping track of the brackets and only calling it quits when we find a '+' char and the count of open brackets is zero.

History

  • 29th November, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Christ Kennedy

CEO
unemployable
Canada Canada

Member

Christ Kennedy, published his fourth novel "Cleats of the Counter Revolution" in the summer of 2010. He grew up in the suburbs of Montreal and is a bilingual Quebecois with a bachelor’s degree in computer engineering from McGill University and is currently walking across ontario plotting a new novel, far away from any computer.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 Pinmembermjbohn20:46 18 Apr '11  
GeneralRe: My vote of 5 PinmvpChrist Kennedy7:48 23 Apr '11  
GeneralMy vote of 1 Pinmembericetea941:45 6 Dec '10  
GeneralMy vote of 1 PinmemberJohnDoe123456781:29 6 Dec '10  
GeneralRe: My vote of 1 PinmemberChrist Kennedy12:17 6 Dec '10  
GeneralMy vote of 5 PinmemberPogoboyKramer0:50 30 Nov '10  
GeneralRe: My vote of 5 PinmemberChrist Kennedy1:19 30 Nov '10  
GeneralMy vote of 5 PinmemberSlacker0076:46 29 Nov '10  
GeneralRe: My vote of 5 PinmemberChrist Kennedy6:50 29 Nov '10  
GeneralMy vote of 1 PinmemberEspiritu6:20 29 Nov '10  
GeneralRe: My vote of 1 PinmemberChrist Kennedy6:35 29 Nov '10  
GeneralRe: My vote of 1 PinmemberScott Barbour12:01 29 Nov '10  
GeneralRe: My vote of 1 PinmemberChrist Kennedy1:58 30 Nov '10  
GeneralRe: My vote of 1 Pinmembertlhintoq9:45 6 Dec '10  
GeneralRe: My vote of 1 PinmemberChrist Kennedy10:15 6 Dec '10  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120517.1 | Last Updated 29 Nov 2010
Article Copyright 2010 by Christ Kennedy
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid