Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Cycle Choice Button

0.00/5 (No votes)
28 Mar 2005 1  
This article describes a CycleButton control.

Cycle button

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.

//Here image resource is extracted and loaded into image.


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.

//Preparation of parallelogram that locates the image on the screen

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};

//Make the background of the button invisible

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);

//Drawing itself

System.Drawing.Graphics deviceContext = e.Graphics;
deviceContext.DrawImage(loadedImage, destPara, 
      new Rectangle(0, 0, 11, 8), GraphicsUnit.Pixel, imageAttr);

//Assign text according to the current caption index.

if (captionsarray.GetLength(0)>0)
{
    this.Text=captionsarray[index];

    //Drawing custom image

    // just to check we won't get out of bounds of the array

    // we can't use standrart Image property,

    // so image should be drawn from client code

    if ((captionsImageList!=null) && 
        (captionsImageList.Images.Count==captionsarray.Length))
    {
        //ident based on size of the image

        int topident=ClientSize.Height - captionsImageList.ImageSize.Height > 0 ? 
                    (ClientSize.Height-captionsImageList.ImageSize.Height)/2 : 0;

        //preparation of parallelogram that locates the image on the screen

        //image strictly aligned at left middle, 

        //as right part of the button is used for arrow

        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};

        //drawing itself

        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:
    // Circle counter incriment and caption change


        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:
    // checks whether user clicks on the arrow or somewhere else


        if ((e.X>this.ClientSize.Width-15) && (captionsarray.GetLength(0)>0))
        {
            // creates menu from captionsarray array and displays it

            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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here