Introduction
Frequently, a developer needs to assign multiple functionality to a button, e.g., print different reports with a single button. A developer has to put checkbox lists or comboboxes on the form to choose which report to print. Moreover, it requires extra effort and time from the developer and it is not very convenient for a user to click on multiple controls.
One solution is to combine the button and choice controls into one. It saves space on the form and reduces work for the developer, and is convenient for the end user. User can choose the action that your button should perform by right clicking on the button. After each right click, the button changes caption, image and the action it should perform. The same functionality can be achieved if you left click the right corner of the button. A context menu appears and you can choose the action from the menu. This functionality resembles that of the standard ToolBar
�s button functionality. But a toolbar�s button works only within the toolbar, while the CycleButton
can be used anywhere on the form independently from other controls.
Using the control
First, add the compiled control to the toolbox. Fill Captions
property with the captions of the options. Optionally, you can set CaptionsImageList
ImageList
, one image for each caption. In the Click
event handler, check the Index
property and execute the appropriate action.
Using the code
First, in the CycleButton
constructor, we should define the MouseDown
and Paint
event handlers. Then we load a bitmap that represents an arrow in the right corner of the button.
System.Resources.ResourceManager myManager =
new System.Resources.ResourceManager(typeof(CycleButton));
Stream st =
Assembly.GetExecutingAssembly().GetManifestResourceStream("AO.arrow.bmp");
loadedImage = System.Drawing.Image.FromStream(st);
In the Paint
event handler, we draw an arrow and then set a caption text and image according to the current caption index. Painting of the arrow and image is similar. The only difference is that we draw an arrow as embedded resource and the caption image is taken from the ImageList
set by the CaptionsImageList
property.
Point ulCorner = new Point(this.ClientSize.Width-15,this.ClientSize.Height/2-5);
Point urCorner = new Point(this.ClientSize.Width-4,this.ClientSize.Height/2-5);
Point llCorner = new Point(this.ClientSize.Width-15,this.ClientSize.Height/2+3);
Point[] destPara = {ulCorner, urCorner, llCorner};
System.Drawing.Color lowerColor = System.Drawing.Color.FromArgb(255,255,255);
System.Drawing.Color upperColor = System.Drawing.Color.FromArgb(255,255,255);
System.Drawing.Imaging.ImageAttributes imageAttr =
new System.Drawing.Imaging.ImageAttributes();
imageAttr.SetColorKey(lowerColor, upperColor,
System.Drawing.Imaging.ColorAdjustType.Default);
System.Drawing.Graphics deviceContext = e.Graphics;
deviceContext.DrawImage(loadedImage, destPara,
new Rectangle(0, 0, 11, 8), GraphicsUnit.Pixel, imageAttr);
if (captionsarray.GetLength(0)>0)
{
this.Text=captionsarray[index];
if ((captionsImageList!=null) &&
(captionsImageList.Images.Count==captionsarray.Length))
{
int topident=ClientSize.Height - captionsImageList.ImageSize.Height > 0 ?
(ClientSize.Height-captionsImageList.ImageSize.Height)/2 : 0;
Point ulCorner1 = new Point(4,topident);
Point urCorner1 = new Point(captionsImageList.ImageSize.Width+4,topident);
Point llCorner1 = new Point(4,captionsImageList.ImageSize.Height+topident);
Point[] destPara1 = {ulCorner1, urCorner1, llCorner1};
System.Drawing.Graphics deviceContext1 = e.Graphics;
deviceContext.DrawImage(captionsImageList.Images[index],destPara1,
new Rectangle(0,0, captionsImageList.ImageSize.Width,
captionsImageList.ImageSize.Height, GraphicsUnit.Pixel);
}
In OnMouseDownin
function, we process the left and right clicks. Left click checks whether the user clicks on the arrow, and if so, dynamically builds the menu from the array of captions and finally displays it. Each menu item is connected with the OnClick
method. In the OnClick
method, the current index is assigned and then it simulates the button's click by calling the user assigned OnClick
event handler.
Right click just cyclically increments a counter and sets the appropriate caption and image.
switch (e.Button)
{
case MouseButtons.Right:
if (captionsarray.GetLength(0)>0)
{
if ((captionsarray.GetLength(0))==index+1)
{
index=0;
this.Text=captionsarray[index];
return;
}
}
this.Text=captionsarray[++index];
break;
case MouseButtons.Left:
if ((e.X>this.ClientSize.Width-15) && (captionsarray.GetLength(0)>0))
{
cm=new ContextMenu();
foreach (string str in captionsarray)
{
cm.MenuItems.Add(str,new EventHandler (OnClick));
}
cm.MenuItems[index].Checked=true;
cm.Show(this, new Point(this.ClientSize.Width-15,
this.ClientSize.Height/2-5));
}
break;
So user has two ways of activating the button. First, right click several times and then left click to perform an action, and second, left click on the arrow and choose the appropriate item from the menu.
Points of Interest
Seven years ago, when I was developing a Delphi app, I faced a problem: how to print several reports with one button. So I developed such a control, and more than two years ago, I transferred the control's logic to .NET. Now I decided to publish it at CodeProject.