Accordion Menu In Window Forms

In this tutorial, I'll explain how to develop a custom Accordion menu class, that you can use in your Windows Forms application to group controls together.


You've probably seen Accordion menus being used on some websites to show and hide content by collapsing panels to reveal content. In this tutorial I'll explain how to create a custom control that inherits from the Panel class to create a simple Accordion menu to contain a group of Buttons.The image on the left shows what the Accordion menu will look like. Notice Sub Menu's have a parent menu.
To create the Accordion menu, you will need a class that inherits from the Panel class. You can name this class Accordion as it will be responsible for containing all the controls. To keep the Accordion flexible, you will want to supply it with the controls you want to display for each menu. This can be done by creating another class called Menu and populating it with a set of public properties. These properties will be responsible for the generating the menu headings and the controls to show for each menu. Let's have a quick look at the Menu class supplied in the demo.

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Windows.Forms;
  4. using System.Drawing;
  5.  
  6. namespace xUI
  7. {
  8.     class Menu
  9.     {
  10.         private string _title;
  11.         private List<Control> _controlCollection = new List<Control>();
  12.         private Color _backgroundColor;
  13.         private Color _foreColor;
  14.         private Font _font;
  15.         private ContentAlignment _textAlignment;
  16.  
  17.         public string Title
  18.         {
  19.             get { return this._title; }
  20.             set { this._title = value; }
  21.         }
  22.  
  23.         public List<Control> Controls
  24.         {
  25.             get { return this._controlCollection; }
  26.         }
  27.  
  28.         public Color BackgroundColor
  29.         {
  30.             get { return this._backgroundColor; }
  31.             set { this._backgroundColor = value; }
  32.         }
  33.  
  34.         public Color ForeColor
  35.         {
  36.             get { return this._foreColor; }
  37.             set { this._foreColor = value; }
  38.         }
  39.  
  40.         public Font TextFont
  41.         {
  42.             get { return this._font; }
  43.             set { this._font = value; }
  44.         }
  45.  
  46.         public ContentAlignment TextAlign
  47.         {
  48.             get { return this._textAlignment; }
  49.             set { this._textAlignment = value; }
  50.         }
  51.     }
  52. }

As you can see in the class, there are a few public properties, which will be used to describe each Menu such as the title and the appearance of the menu. Also notice there is a generic List collection which will hold all of the controls to display for each menu.

With the menu class created, we need to add it to the Accordion class and because the Accordion can have multiple menus, we need a collection to store each menu. Let's have a quick look at an excerpt of the Accordion class supplied in the demo.

  1. private List<Menu> _menuCollection = new List<Menu>();
  2.  
  3. public List<Menu> Menu
  4. {
  5.         get { return this._menuCollection; }
  6. }

Notice there is a generic List collection which holds objects of Type Menu and a public property Menu that returns the collection. Now the accordion can hold menus but it can't render them on the form. We need a method that can loop through each menu, create a header control and place any sub controls under the menu. Let's create a new method called Bind in the Accordion class which will bind all the menus and its sub controls to a hosted control. The hosted control in this case is a Panel and as the Accordion class inherits from a Panel class, we can add the menus to its controls using the Controls property. The Bind method should loop through all the Menus stored in the List<Menu> collection. For each menu heading, it should add a new Label control. After creating a Label control for the menu heading, a Panel needs to be created to hold the menu's collapsible content. It is this Panel that we will show/hide when the menu is clicked. Finally we need to add a click event to the Label control and the event should check if the collapsible Panel is visible or not. Let's have a look at the Bind method from the sample demo.

  1. public void Bind()
  2. {
  3.         int panelHeight = 0;
  4.         this._menuCollection.Reverse();
  5.         foreach (Menu menu in this._menuCollection)
  6.         {
  7.                 Label lblTitle = new Label();
  8.                 Panel menuPanel = new Panel();
  9.  
  10.                 lblTitle.Text = menu.Title;
  11.                 lblTitle.BackColor = menu.BackgroundColor;
  12.                 lblTitle.ForeColor = menu.ForeColor;
  13.                 lblTitle.Font = menu.TextFont;
  14.                 lblTitle.TextAlign = menu.TextAlign;
  15.                 lblTitle.Dock = DockStyle.Top;
  16.                 lblTitle.Click += new EventHandler(lblTitle_Click);
  17.  
  18.                 menu.Controls.Reverse();
  19.                 foreach (Control control in menu.Controls)
  20.                 {
  21.                         control.Dock = DockStyle.Top;
  22.                         menuPanel.Controls.Add(control);
  23.                 }
  24.  
  25.                 menuPanel.Dock = DockStyle.Top;
  26.                 menuPanel.AutoSize = true;
  27.                 menuPanel.Visible = false;
  28.  
  29.                 this.Controls.Add(menuPanel);
  30.                 this.Controls.Add(lblTitle);
  31.  
  32.                 panelHeight += lblTitle.Height;
  33.                
  34.         }
  35.         this.Height = panelHeight;
  36. }

Notice how the Bind method iterates over the Menu collection. For each menu its created a Label and a Panel Control, which gets added to the hosted control. When the Label control is created a few properties are set mainly for the menu appearance. All the controls are docked at the top of the hosted control. The menu's sub controls are added to the Panel, which in tern is added to the hosted control. Finally there is a click event which will check the state of each menu to determine weather to show/hide its sub controls.
  1. void lblTitle_Click(object sender, EventArgs e)
  2. {
  3.         this.AutoSize = true;
  4.         Label menu = (Label)sender;
  5.         int index = this.Controls.GetChildIndex(menu) -1;
  6.  
  7.         if (this.Controls[index].Visible)
  8.         {
  9.                 this.Controls[index].Visible = false;
  10.         }
  11.         else
  12.         {
  13.                 this.Controls[index].Visible = true;
  14.         }
  15. }


Above is the click event handler for the menu. It gets the index position of the menu, which by this point is hosted in the parent Panel. Because the menu collection was reversed when adding them to the hosted Panel, we decrement the index position of the menu, which will give use the location of it's sub control Panel. We can now use this position to access the Panel control for each menu and check the visibility state, turning it on and off as needed. You can download the sample application from the link below.


Download For C#

No comments:

Post a Comment