Master Toolbars and Menus with the New ToolStrip Control

he upcoming .NET framework version 2.0 has new features galore, one of which is the new ToolStrip control. The ToolStrip aims to unify and replace existing toolbars, menus, and status bars. The new version offers specialized control implementations for use in dropdown menus, context menu popups, and status bar controls in Window Forms applications. To create this multipurpose control Microsoft had to completely redesign the class organization for menus and toolbars?a worthwhile effort?because the new design provides better design time support, custom rendering, built-in rafting, run-time re-ordering, and many more features. First, here’s a brief overview of the new object model.

ToolStrip Architecture Overview
In the previous versions of the .NET framework, Menu was the base class for Main Menu and ContextMenu controls while the ToolBar and StatusBar were specific control type implementations. In contrast, Whidbey groups these controls logically and provides an integrated base class called ToolStrip that’s both rich and extensible. ToolStrip is now the base class for MenuStrip, ContextMenuStrip, and StatusStrip controls. All these controls function as item containers, inheriting common behavior and a common event model?extended appropriately so that each implementation accommodates specific behaviors. The base ToolStrip class handles painting, user input, and drag/drop events.

?
Figure 1. ToolStrip and ToolStripItem: The .NET framework 2.0 contains several implementations of the base ToolStrip type, each of which can contain a set of ToolStripItem controls, such as ToolStripButton, ToolStripDropDownItem, etc.

A ToolStrip is a container for ToolStripItem elements. Each individual element on the ToolStrip is a ToolStripItem that manages the layout and event model for the type it contains. For example, elements visible on the toolbar, such as buttons, text boxes, labels, or combo boxes, or visible on the menu bar, such as “File->New,” each correspond to a ToolStripItem.

Table 1 shows a list of ToolStripItem controls you can place within a ToolStrip.

Table 1. Common ToolStripItem Controls: The ToolStrip provides custom implementations of the controls most commonly hosted in toolbars and status bars.

Control Implementation Description
ToolStripLabel Used to display normal text, hyperlinks, and images.
ToolStripButton Provides a typical pushbutton that you can configure to support both text and images.
ToolStripComboBox A ComboBox with methods/properties to configure various styles.
ToolStripSeparator A separator that you can use to visually separate groups of ToolStripItem elements.
ToolStripDropDownButton This control provides a button which, when clicked, displays a ToolStripDropDown control. You’ll implement a sample of this to get a better idea of how to use it.
ToolStripTextBox A normal textbox which can be used to enter text.
ToolStripMenuItem A special menu control built specifically for use with the MenuStrip and ContextMenuStrip controls.
ToolStripProgressBar A specialized progress bar implementation for use within a StatusStrip control.
ToolStripSplitButton A combination of normal button and a DropDownButton.
ToolStripControlHost A control that acts as a host to your customized implementations or any other Windows Form controls.

Though you can use most of the controls listed in Table 1 in all ToolStrip controls, Microsoft recommends that you use the various ToolStripItem types only within the matching ToolStrip type containers. For example, a ToolStripMenuItem is designed for use within a MenuStrip or ContextMenuStrip. Similarly, a ToolStripProgressBar is most appropriate with the StatusStrip. So while you might be able to put a ToolStripMenuItem inside a StatusStrip, you probably shouldn’t.

You can use a ToolStripManager to gain finer-grained control over ToolStrip rendering, and over behavior such as rafting (sharing horizontal and vertical real estate within a tool area when docked). You might also use a ToolStripManager to merge ToolStrip controls with each other or for other non-standard tasks.

Build a Basic Menu
You may want to download the sample project for this article at this point so you can test things out as you read through the article, but here are the first steps involved..

  1. Create a new Windows application project and rename it to MDIForm.cs.
  2. Set the form’s IsMDIContainer to true.
  3. Drag and drop a MenuStrip onto the form and rename it to mnuStrip.
  4. In the form’s constructor, the following code snippet adds two MenuItems and sets their Text property to “File” and “New”. Note the use of DropDownItems.Add to add a ToolStripItem type. The ToolStripMenuItem inherits from ToolStripDropDownItem, and its DropDownItems property returns the associated ToolStripItemCollection.
       ToolStripMenuItem  fileitem = new           ToolStripMenuItem();       fileitem.Text = "&File";       ToolStripMenuItem  newitem = new           ToolStripMenuItem();       newitem.Text = "&New";       newitem.Click += delegate {           FirstForm f = new FirstForm();              f.MdiParent = this;              f.Show(); };        fileitem.DropDownItems.Add(newitem );

Note that the preceding code associates the Click event of the new ToolStripItem with an anonymous method that opens up another form of type FirstForm and sets its MDIParent.

The next snippet adds a MonthCalendar to the ToolStripControlHost. As mentioned in Table 1, the ToolStripControlHost is a ToolStripItem type used to host Windows Forms controls. Normally, you’d do something significant when the user selects a particular date, but for simplicity, the sample code just shows a MessageBox.

?
Figure 2. Using MonthCalendar with a ToolStripControlHost: The ToolStripControlHost provides a container that lets you host standard controls within ToolStrips?even in places where you wouldn’t typically find them, such as this File menu dropdown.
      MonthCalendar calend = new MonthCalendar();      ToolStripControlHost host = new             ToolStripControlHost(calend );      calend.DateSelected += delegate(object sender,          DateRangeEventArgs e)         { MessageBox.Show("You selected " +             e.Start.ToShortDateString());         };      fileitem.DropDownItems.Add(host);      mnuStrip.Items.Add(fileitem);

The ToolStripControlHost constructor used in the preceding snippet accepts a “Control” parameter?the control you want to host in the ToolStrip. Note that this article simply demonstrates hosting a control in the standard ToolStripControlHost implementation. For customized ToolStripItems, you should inherit and create a custom ToolStripControlHost implementation. You can override methods such as OnSubscribeControlEvents to handle events raised by the hosted controls or wrap custom functionality within properties to enrich the hosted control. That’s how Microsoft implements the provided ToolStripItem controls internally, such as ToolStripComboBox, ToolStripProgressBar, etc. Figure 2 shows how the MonthCalendar control displays when you run the preceding code and click the File menu.

Extending the Menu
Next, assume that you want to implement a menu where the dropdown items get their data from a backend database. In the sample code, check out the DataStore class, which constructs a Dataset with two DataTables. The first table holds a list of Departments and the second a list of Sub-departments.

The ToolStripDropDownButton class is a button that you can associate with an element of type ToolStripDropDown. When a user clicks the button the control pops up the ToolStripDropDown items associated with it. So, to use the class to drop down the list of Departments from the table in the DataSet, you first declare a ToolStripDropDownButton. Then, create a ToolStripDropDownMenu (derived from ToolStripDropDown), and add a ToolStripButton item for each record found in the Department table as shown below.

?
Figure 3. ToolStripDropDownButton Implementation. The “Departments” ToolStripDropDownButton displays a list of department names drawn from a database. The figure shows the effect of applying the various ToolStripTextDirection variations.
   ToolStripMenuItem hrMenu = new ToolStripMenuItem();   hrMenu.Text = "&Human Resources";                   /****Get the dropdown and attach buttons*************/   ToolStripDropDownButton deptButton = new       ToolStripDropDownButton();   ToolStripDropDownMenu  dept = new       ToolStripDropDownMenu();   dept.DefaultDropDownDirection =       ToolStripDropDownDirection.AboveRight;   DataTable deptTable = DataStore.GetDepartment();                   foreach (DataRow  record in deptTable.Rows )   {      ToolStripButton item = new ToolStripButton();      item.Tag = record;      item.Text =  record["DepartmentName"].ToString();      item.TextAlign = ContentAlignment.BottomCenter;      item.TextDirection =          ToolStripTextDirection.Horizontal;      item.Width = 200;      dept.Items.Add(item);   }      dept.Width  = 200;   dept.Height = 40;   deptButton.RightToLeft = RightToLeft.Yes ;   deptButton.DropDown  = dept;   deptButton.Enabled = true;   deptButton.Height = 40;   deptButton.Width  = 200;   deptButton.Text = "&Departments";   hrMenu.DropDownItems.Add(deptButton );

You can customize each of these ToolStripItems as required. The TextDirection property is interesting; try setting it to the values Vertical90 and Vertical270 to see how it affects the text orientation. After adding all the ToolStripButton elements to the ToolStripDropDown set the ToolStripDropDown’s DropDown property to associate the DropDown elements with the ToolStripDropDownButton. Figure 3 shows how this looks at runtime.

You can embed a ComboBox in a tool strip using the ToolStripComboBox control. The following implementation is simple and similar to using a standard ComboBox. Note that ToolStripComboBox is a specialized implementation of ToolStripControlHost, exposing a hosted ComboBox. You can set the ComboBox DropDownStyle, its BackColor, and ForeColor, etc. The sample adds Sub-department names to the Items collection of the ComboBox. Figure 4 shows the code running in a Windows Form.

?
Figure 4: The ToolStripComboBox Implementation. Here’s how the ToolStripComboBox looks at runtime.
   /********Drop down  box menucontrol*************/   ToolStripComboBox cboSubDept = new       ToolStripComboBox();   cboSubDept.DropDownHeight = 30;   cboSubDept.DropDownWidth  = 200;   cboSubDept.DropDownStyle = ComboBoxStyle.DropDownList;   cboSubDept.ComboBox.BackColor = Color.DarkViolet ;   cboSubDept.ComboBox.ForeColor  = Color.White ;   cboSubDept.ComboBox.ItemHeight = 30;   DataTable subDeptTable = DataStore.GetSubDepartment();      foreach (DataRow record in subDeptTable.Rows)   {      cboSubDept.ComboBox.Items.Add(         record["SubDepartmentName"].ToString());   }   cboSubDept.ComboBox.SelectedIndex = 1;   cboSubDept.ComboBox.SelectedValueChanged += delegate      {          MessageBox.Show("You selected " +              cboSubDept.SelectedItem.ToString(),                    "Information", MessageBoxButtons.OK,              MessageBoxIcon.Information);      };   cboSubDept.ComboBox.SelectedValue  = 1;   hrMenu.DropDownItems.Add(cboSubDept);

Going Beyond Simple Menus
The next example uses a ToolStripLabel element. You use this element to display read-only normal text, an image, or a hyperlink.

   /*********Add Separator******************/   ToolStripSeparator sep = new ToolStripSeparator();   hrMenu.DropDownItems.Add(sep);   /**************************************/      /********Add hyperlinks*************/   ToolStripLabel lblMore = new ToolStripLabel();   lblMore.IsLink = true;   lblMore.LinkBehavior = LinkBehavior.AlwaysUnderline;   lblMore.LinkColor = Color.DarkViolet;   lblMore.VisitedLinkColor = Color.LightBlue;   lblMore.Text = "Click for more..";   lblMore.Click += delegate      {         MessageBox.Show("You selected link",             "Information", MessageBoxButtons.OK,                   MessageBoxIcon.Information);      };   hrMenu.DropDownItems.Add(lblMore);   mnuStrip.Items.Add(hrMenu);   /******************************************/

The preceding code snippet illustrates some important properties. The IsLink property ensures that the label type is rendered as a link. LinkBehavior lets you configure the label’s UI using self-describing values such as AlwaysUnderLine and HoverUnderline. You can also set the default LinkColor and VisitedLinkColor properties.

The final menu item in the sample code adds a ToolStripTextBox control with specific TextAlignment and CharacterCasing property settings. Although not shown in the sample code, you may also want to look at the AutoCompleteSource property, as it can help you implement smart controls.

A Raft of New Usability Features
At the point, you have a fully functional MenuStrip that illustrates the new .NET 2.0 UI ToolStrip features; but to provide a truly usable toolbar, you need to implement Rafting. Rafting is the process of sliding a toolbar horizontally or vertically to place it in a particular location relative to other toolbars that share the same screen real estate. The following example uses a ContextMenuStrip to handle the rafting of the completed ToolStrip. Drag and drop a ContextMenuStrip and rename it to stripctxtMenu. Then add four ToolStripButton controls to the ContextMenuStrip as shown in the following code snippet.

   void SetContextMenuStrip()   {      ToolStripButton btnTop=new ToolStripButton();      btnTop.Text = "&Top";      btnTop.Click +=delegate {mnuStrip.Raft =          RaftingSides.Top ;  };         ToolStripButton btnBottom = new ToolStripButton();      btnBottom.Text = "&Bottom";      btnBottom.Click += delegate {mnuStrip.Raft =          RaftingSides.Bottom; };         ToolStripButton btnLeft = new ToolStripButton();      btnLeft.Text = "&Left";      btnLeft.Click += delegate {mnuStrip.Raft =          RaftingSides.Left; };         ToolStripButton btnRight  = new ToolStripButton();      btnRight.Text = "&Right";      btnRight.Click += delegate {mnuStrip.Raft =          RaftingSides.Right; };         stripctxtMenu.Items.Add(btnTop);      stripctxtMenu.Items.Add(btnLeft);      stripctxtMenu.Items.Add(btnRight);      stripctxtMenu.Items.Add(btnBottom);         mnuStrip.ContextMenuStrip = stripctxtMenu;   }

Notice that the anonymous methods associated with each of the button’s Click event use the RaftingSides enumeration to set the ToolStrip menu’s Raft property. The last line of the snippet associates the MenuStrip’s ContextMenuStrip property with the new ContextMenuStrip. The new ContextMenuStrip controls how the MenuStrip rafts. To test it, right click on the MenuStrip and select the appropriate Raft setting, then notice the way you can move the ToolStrip.

Building Context Menus
In the very first code snippet in this article, clicking the New menu opened a new form, which you haven’t seen yet. You’ll implement the ToolStrip, ContextMenuStrip and StatusStrip here.

Create the new Form and rename it to FirstForm. Ensure that the code sets the new form’s MDIParent property before calling its Show method. I’ve repeated the first code snippet below, for clarity.

   ToolStripMenuItem  fileitem = new       ToolStripMenuItem();   fileitem.Text = "&File";   ToolStripMenuItem  newitem = new       ToolStripMenuItem();   newitem.Text = "&New";   newitem.Click += delegate       {          FirstForm f = new FirstForm();         f.MdiParent = this;         f.Show();       };   fileitem.DropDownItems.Add(newitem );

Drag and drop a ToolStrip onto the FirstForm form and rename it to tlStrip. Also drag a ContextMenuStrip and rename it to cntxtMenu.

Implement the Department and Sub-department items the same way as you did with the MenuStrip in the earlier examples, but this time, associate them with the ContextMenuStrip instead. The downloadable sample code contains the complete implementation. Note that there’s no difference between the MenuStrip and ContextMenuStrip implementation in terms of using the event model or the object model. This flexibility in the ToolStrip architecture helps you build reusable components/controls. Although this sample itself isn’t generically reusable, it would be very easy to create an application configuration file that could implement this functionality.

?
Figure 5: Child Form ToolStrip and ContextMenuStrip: The figure shows how the completed ToolStrip and ContextMenuStrip controls look in the child form.

The ToolStrip implementation is very simple and uses a set of images in an ImageList. Figure 5 shows how the completed ToolStrip looks. Here’s the code.

   ToolStripButton lblQuest = new ToolStripButton();   lblQuest.Image  = imgList.Images[1];   lblQuest.Text = "&Question";   lblQuest.Enabled = true;   tlStrip.Items.Add(lblQuest);      ToolStripButton lblExcla = new ToolStripButton();   lblExcla.Image = imgList.Images[2];   lblExcla.Text = "&Exclamation";   lblExcla.Enabled = true;   tlStrip.Items.Add(lblExcla);      ToolStripButton lblInfo = new ToolStripButton();   lblInfo.Image  = imgList.Images[3];   lblInfo.Text = "&Information";   lblInfo.Enabled = true;   tlStrip.Items.Add(lblInfo );   tlStrip.Items.Add(drp);   tlStrip.Items.Add(mnudeptCombo);   tlStrip.Raft =  RaftingSides.Right;

Building a Status Strip
The last important new ToolStrip feature is the StatusStrip. A StatusStrip can accommodate all the types used in a ToolStripItem; however, you would usually try to limit its use to showing the status of the current user interface, typically with either progress bars or text. For example, Outlook 2003 provides an icon with a dropdown button when you are offline. When you click this button you get a menu of available options. This is the type of situation in which you need to use the StatusStrip.

The following code snippet adds a ToolStripLabel with a ToolStripDropDownButton on one side. Clicking the button lets users select between two different buttons. Clicking the label causes the progress bar to update. Figure 6 shows you how this looks at runtime.

?
Figure 6. StatusStrip Example: The sample shows what happens when users click the ToolStripDropDownButton or click the label to update the ToolStripProgressBar.
   ToolStripProgressBar bar = new ToolStripProgressBar();   bar.Name = "Status";   bar.ImageTransparentColor = Color.Brown;   bar.Height = 20;   bar.Maximum = 100;   bar.Visible = false;   stsStrip.Items.Add(bar);      ToolStripLabel lblSts = new ToolStripLabel();   lblSts.Text = "Status offline.";   stsStrip.Items.Add(lblSts);   ToolStripDropDownButton ddownbutton = new       ToolStripDropDownButton();   ToolStripDropDown ddown = new ToolStripDropDown();   ToolStripButton dbtnConnect = new ToolStripButton();   dbtnConnect.Text = "Click &here to connect...";   dbtnConnect.ToolTipText =       "Starts the process of connecting based " +      "on preferences.";   ToolStripButton dbtnpref = new ToolStripButton();   dbtnpref.Text = "&Preferences";   dbtnpref.ToolTipText = "Preferences for connection.";   ddown.Items.Add(dbtnpref);   ddown.Items.Add (dbtnConnect);   ddownbutton.DropDown = ddown;   stsStrip.Items.Add(ddownbutton);   lblSts.DoubleClick  += delegate      {         lblSts.Visible = false;         ddownbutton.Visible = false;         StartProgressBar startCallback=new             StartProgressBar(ProgressBarCall);         this.BeginInvoke(startCallback);         ddownbutton.Visible = true;         lblSts.Visible = true;      };

You have now seen examples for nearly every ToolStripItem element type and all the ToolStrip controls. You can extend these to create customized themes or styles for your ToolStrips.

There are three ways to customize the rendering of ToolStrip controls as seen below:

Use a ToolStripRenderer. Use this class to create custom color schemes or styles for ToolStrip controls. You inherit the extensible ToolStripRenderer class and override certain key protected and public methods to achieve custom stylings.

Use a ToolStripProfessionalRenderer. This class renders based on Windows’ current color scheme, letting your controls adjust to users’ color preferences.

Use a ToolStripSystemRenderer. This applies a flat styling for the controls and uses the system colors for the windows.

To understand how to use these Renderers look at the following code snippet for the ContextMenuStrip.

   ToolStripButton btnToolStripManager = new       ToolStripButton();   btnToolStripManager.Text ="&For ToolStripManager";   btnToolStripManager.Click +=delegate      {         CustomToolStripRenderer customrender = new             CustomToolStripRenderer();         ToolStripManager.Renderer=customrender ;       };               bcustom.DropDownItems.Add(btnToolStripManager );   ToolStripButton btnToolStrip = new ToolStripButton();   btnToolStrip.Text ="For &Current ToolStrip";   btnToolStrip.Click +=delegate      {         CustomToolStripRenderer customrender = new             CustomToolStripRenderer();         tlStrip.Renderer=customrender ;       };      bcustom.DropDownItems.Add(btnToolStrip);   mnuModes.Items.Add(bcustom);      ToolStripButton btnProfesional=new ToolStripButton();   btnProfesional.Text = "&Professional rendering";   btnProfesional.Click += delegate      {         ToolStripProfessionalRenderer profrender = new              ToolStripProfessionalRenderer();         ToolStripManager.Renderer=profrender;       };   mnuModes.Items.Add(btnProfesional);      ToolStripButton btnSystem=new ToolStripButton();   btnSystem.Text = "&System rendering";   btnSystem.Click += delegate      {         ToolStripSystemRenderer sysrender = new             ToolStripSystemRenderer();         ToolStripManager.Renderer = sysrender;      };

The most interesting parts of the preceding snippet are the anonymous method declarations. For example, the first case instantiates a CustomToolStripRenderer? a customized implementation of ToolStripRenderer?and sets that as the ToolStripManager’s Renderer property. Remember that the ToolStripManager controls the color rendering and styling of the ToolStrip, so the style or color schema from the customized implementation will apply to the entire ToolStrip.

   public class CustomToolStripRenderer:ToolStripRenderer       {      public CustomToolStripRenderer():  base()      {            }         protected override void Initialize(          ToolStrip toolStrip)      {         base.Initialize(toolStrip);         toolStrip.AllowDrop = true;         toolStrip.AutoScroll = true;         toolStrip.AutoSize = true;         toolStrip.BackColor = Color.Brown;         toolStrip.ForeColor = Color.Wheat;         toolStrip.CanOverflow = true;         toolStrip.Font = new             Font(FontFamily.GenericSerif, 10);         }         protected override void InitializeItem(         ToolStripItem item )      {         base.InitializeItem(item);         item.BackColor = Color.Beige;         item.ForeColor = Color.Red;      }       }

The customization class shown above overrides two methods: Initialize, which initializes the associated ToolStrip, and InitializeItem, which initializes each ToolStripItem in the ToolStrip. The color properties you set in this snippet will be rendered when you call the CustomToolStripRenderer. The custom code simply changes some of the properties of the associated ToolStrip and ToolStripItems. You can see this if you click the

?
Figure 7: Rendering Modes Used in ToolStrips. You can experiment with various rendering modes by selecting one of the Render modes available from the Rendering Options item on the context menu.

context menu and select the Custom Rendering option. The ToolStripManager’s Render property gets set to the selection and the entire toolbar and items inside are redrawn to reflect the changes (see Figure 7). Note that the sample doesn’t provide information about overriding the behaviors of all critical methods because such a discussion is out of the scope of this article.

You can configure your applications to use the Renderer property for all ToolStrips using a ToolStripManager or for just a specific ToolStrip. You can see both options in the downloadable sample code.

Using the Professional or System renderers is similar. You would explicitly create an instance of the desired renderer and set that via the Render property or just change the ToolStripManager.RenderMode option’s value to Professional/System.

The ToolStrip is a huge new topic within the .NET framework 2.0 because of the sheer richness of the control set and the corresponding UI elements. But it’s also a big step towards efficiency, flexibility, and extensibility because of the improved object model and the support for manager classes and customized Renderer objects.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: