Tabbed Pages
A loose-leaf notebook with tabbed separator pages is an efficient way to organize and present a large amount of information in a compact space. The labeled tab on each separator page clearly separates and identifies the contents of that section. Although you can be in only one section at a time, you can easily see the labels of all available sections and instantly move between them.
This tabbed notebook metaphor, a common user interface design pattern in the Windows world, is implemented in .NET with the TabControl, which contains a collection of TabPage objects. All TabPage objects are layered one on top of another, with only one tab page visible at a time. Select which page to view by clicking on the tab. Each tab page can contain any number of controls.
Referring back to the class hierarchy displayed in Figure 13-1, you can see that the TabControl class is directly derived from the Control class, while the TabPage class is derived from Control via ScrollableControl and Panel. The TabControl control cannot contain other controls, except for TabPage objects. The TabPage objects, on the other hand, can contain other controls and have scrollbars. Unlike the Panel control from which it is derived, the TabPage displays its Text property as the text string on the tab.
The TabControl control has many properties in addition to those inherited from Control. Many are listed in Table 13-1.
Property |
Value type |
Description |
---|---|---|
Alignment |
TabAlignment |
Read/write. The edge of the control where the tabs are located. Valid values are members of the TabAlignment enumeration, listed in Table 13-2. The default is TabAlignment.Top. If set to Left or Right, the Multiline property is automatically set to true. |
Appearance |
TabAppearance |
Read/write. The visual appearance of the tabs. Valid values are members of the TabAppearance enumeration, listed in Table 13-3. The default is TabAppearance.Normal. |
DisplayRectangle |
Rectangle |
Read-only. Returns a Rectangle representing the display area of the tab pages. |
DrawMode |
TabDrawMode |
Read/write. Allows users to customize how the control is drawn. If it is TabDrawMode.Normal, the default, the operating systems draws the tabs. If it is TabDrawMode.OwnerDrawFixed, the tabs are drawn by the parent window. |
HotTrack |
Boolean |
Read/write. If false (the default), tabs do not change the appearance when the mouse passes over. If true, the color of the tab text will change. |
ImageList |
ImageList |
Read/write. Specifies the ImageList containing images to be displayed on the tabs. The TabPage.ImageIndex property specifies which image to actually use. (The ImageList is described in Chapter 7.) |
ItemSize |
Size |
Read/write. The size of the tabs. The default size automatically fits images and Text properties. The SizeMode property must be set to Fixed to change the ItemSize.Width property. |
Multiline |
Boolean |
Read/write. If false (the default), only one line of tabs will be displayed. If there are more tabs than will fit, arrows will be displayed to allow navigation to undisplayed tabs. If false, the Alignment property is automatically set to TabAlignment.Top. |
Padding |
Point |
Read/write. The amount of space around the text on the tab. Default is (6,3). |
RowCount |
Integer |
Read-only. Returns the number of rows of tabs currently displayed. |
SelectedIndex |
Integer |
Read/write. Zero-based index of the currently selected tab page. |
ShowToolTips |
Boolean |
Read/write. If false (the default), ToolTips are not shown for tabs that have them. To create a ToolTip for a tab, set the TabPage.ToolTipText property. |
SizeMode |
TabSizeMode |
Read/write. Specifies how the tabs are sized. Valid values are members of the TabSizeMode enumeration, listed in Table 13-4. The default is TabSizeMode.Normal. |
TabCount |
Integer |
Read-only. Number of tabs in the TabControl. |
TabPages |
TabPageCollection |
Read-only. The collection of tab pages contained in the control. The TabPageCollection class has properties, listed in Table 13-5, and methods, listed in Table 13-6. |
Text |
String |
Read/write. Overrides Control.Text. Similar to the Panel controldoes not display anywhere. |
Value |
Description |
---|---|
Bottom |
Tabs located along the bottom of the control. |
Left |
Tabs located along the left edge of the control. |
Right |
Tabs located along the right edge of the control. |
Top |
Tabs located along the top of the control. |
Three of the four possible values of TabAlignment are shown in Figure 13-2. The missing Right value is symmetrical to the Left.
Figure 13-3. TabAlignment
Value |
Description |
---|---|
Buttons |
Tabs look like 3-D buttons. |
FlatButtons |
Tabs look like flat buttons. |
Normal |
Tabs look like standard tabs. |
Value |
Description |
---|---|
FillToRight |
Tabs are sized so that each row of tabs completely fills the width of the TabControl. Only applicable if Multiline is set to true and there is more than one row of tabs. |
Fixed |
All the tabs are the same width. |
Normal |
Each tab is sized for the contents of the tab and not adjusted to fill the width of the TabControl. |
The Alignment and Appearance properties of the TabControl depend on each other for correct operation. If the Alignment property is set to TabAlignment.Top, then everything works fine regardless of the Appearance property. However, if the Alignment property is set to any value other than Top, the tab pages themselves will appear to be missing unless the Appearance property is set to the default of TabAppearance.Normal. If the Appearance is set to TabAppearance.Buttons or TabAppearance.FlatButtons, the tabs will display as buttons only and events will still fire, but the tab pages themselves will again appear to be missing. Furthermore, the tabs can appear as FlatButtons only if the Alignment is set for Top; if the Alignment is not set for Top, then FlatButtons will appear as Buttons.
The Padding property adds space around the text displayed in the tab. The left image in Figure 13-3 shows tabs using the default value for Padding (6,3) while the right image shows the Padding property set to (20,10). Notice that the Multiline property is set to true for both images: only the right image needed more than one row of tabs. If Multiline had not been set to true, the tabs would display in a single row with a clickable arrow for navigating to the other tabs.
|
Figure 13-4. TabControl Padding property
All the tab pages contained by the TabControl are members of the TabPageCollection class. The commonly used properties and methods of the TabPageCollection class are listed in Table 13-5 and Table 13-6, respectively.
Property |
Value type |
Description |
---|---|---|
Count |
Integer |
Read-only. Returns the number of tab pages in the collection (the number of tab pages in the TabControl). |
IsReadOnly |
Boolean |
Read-only. Always returns false. |
Item |
TabPage |
Read/write. The tab page at the zero-based index specified by this property. |
Method |
Description |
---|---|
Add |
Adds a TabPage object to the collection. |
AddRange |
Adds multiple TabPage object to the collection. |
Clear |
Removes all TabPage objects from the collection. |
Contains |
Returns true if the specified TabPage is in the collection. |
GetEnumerator |
Returns an enumerator containing all the TabPage objects. Changing the enumerator changes the collection of TabPages. |
IndexOf |
Returns the zero-based index of the specified TabPage object or -1 if the object not found. |
Remove |
Removes the specified TabPage object. |
RemoveAt |
Removes the TabPage object at the specified zero-based index. |
Each tab page in a TabControl can also have properties of its own. The commonly used properties are listed in Table 13-7.
Property |
Value type |
Description |
---|---|---|
ImageIndex |
TabControl.ImageList |
Read/write. The index of the image to display on the tab. The zero-based index is in the ImageList specified in the TabControl.ImageList property. |
TabIndex |
Integer |
Read/write. Overridden. The zero-based index of the tab page within the TabPage collection. |
Text |
String |
Read/write. The text displayed on the tab. |
ToolTipText |
String |
Read/write. The text displayed as a ToolTip when the user's mouse passes over the tab, if the TabControl.ShowToolTips property is set to true. |
|
Other than events inherited from Control, the one commonly used event raised by the TabControl control is SelectedIndexChanged. It is raised whenever a different tab page is selected. Its usage will be demonstrated shortly.
|
The TabPage class has one method not inherited from the Control or other base class: GetTabPageFromComponent. This static (Shared in VB.NET) method takes a single object as an argument. This method will return the TabPage object containing the component if the component is found; otherwise it will return null (Nothing in VB.NET). For example, if you wanted to find and make current the tab page in a tab control that contains a RichTextBox control called rtxt, you could use a line such as the following:
tc.SelectedIndex = (TabPage.GetTabPageOfComponent(rtxt)).TabIndex;
tc.SelectedIndex = _ TabPage.GetTabPageOfComponent(rtxt).TabIndex
The programs listed in Example 13-3 (in C#) and Example 13-4 (in VB.NET) demonstrate many of the features of the TabControl and TabPage classes. In these programs, a tab control is created on a form. This tab control contains four tab pages, three of which actually contain other controls and the fourth of which has a spiffy wrench icon as part of its tab. The look of the tabs is controlled using the Alignment, Appearance (which is set to its default value), Multiline, Padding, and SizeMode properties. When either program is compiled and run, the first three pages will look like Figure 13-4. The fourth tab page, labeled "A Really Long Tab," contains no controls. It exists mainly to highlight the effects of these properties.
Figure 13-5. TabControl and TabPages
An analysis of Example 13-3 and Example 13-4 follows the code listings.
Example 13-3. TabControl and TabPages in C# (TabControls.cs)
using System; using System.Drawing; using System.Windows.Forms; namespace ProgrammingWinApps { public class TabControls : Form { public TabControls( ) { Text = "TabControl & TabPages"; Size = new Size(325,375); TabControl tc = new TabControl( ); tc.Parent = this; tc.Size = new Size(275,300); tc.Location = new Point(25,25); tc.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; tc.Alignment = TabAlignment.Top; tc.Appearance = TabAppearance.Normal; tc.HotTrack = true; tc.Multiline = true; tc.Padding = new Point(20,3); tc.SizeMode = TabSizeMode.FillToRight; tc.SelectedIndexChanged += new EventHandler(tc_Changed); tc.Text = "Some text"; // Get an image for one of the tabs Image img = Image.FromFile( "C:\Program Files\Microsoft Visual Studio .NET 2003" + "Common7\Graphics\icons\industry\wrench.ico"); ImageList imgList = new ImageList( ); imgList.Images.Add(img); tc.ImageList = imgList; // Address book tab TabPage tpAddress = new TabPage( ); tpAddress.Parent = tc; tpAddress.Text = "Address Book"; // Datebook tab TabPage tpDates = new TabPage( ); tpDates.Parent = tc; tpDates.Text = "Date Book"; // ToDo tab TabPage tpToDo = new TabPage( ); tpToDo.Parent = tc; tpToDo.Text = "ToDo List"; // Miscellaneous tab TabPage tpMisc = new TabPage( ); tpMisc.Parent = tc; tpMisc.Text = "A Really Long Tab"; tpMisc.ImageIndex = 0; // Address stuff Label lbl = new Label( ); lbl.Parent = tpAddress; lbl.Text = "Put the Address book controls on this page."; lbl.AutoSize = true; lbl.Location = new Point(10,25); // Datebook stuff Panel pnl = new Panel( ); pnl.Parent = tpDates; pnl.BorderStyle = BorderStyle.Fixed3D; pnl.Location = new Point(10,10); pnl.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; pnl.Size = new Size(tpDates.Width - 20, tpDates.Height - 20); MonthCalendar cal = new MonthCalendar( ); cal.Parent = pnl; cal.Location = new Point(25,25); // ToDo stuff RichTextBox rtxt = new RichTextBox( ); rtxt.Parent = tpToDo; rtxt.Dock = DockStyle.Fill; rtxt.BorderStyle = BorderStyle.FixedSingle; } // close for constructor static void Main( ) { Application.Run(new TabControls( )); } private void tc_Changed(object sender, EventArgs e) { TabControl tc = (TabControl)sender; MessageBox.Show("Button " + tc.SelectedIndex.ToString( )); } } // close for form class } // close form namespace
Example 13-4. TabControl and TabPages in VB.NET (TabControls.vb)
Option Strict On imports System imports System.Drawing imports System.Windows.Forms namespace ProgrammingWinApps public class TabControls : inherits Form public sub New( ) Text = "TabControl & TabPages" Size = new Size(325,375) dim tc as new TabControl( ) tc.Parent = me tc.Size = new Size(275,300) tc.Location = new Point(25,25) tc.Anchor = AnchorStyles.Top or AnchorStyles.Bottom or _ AnchorStyles.Left or AnchorStyles.Right tc.Alignment = TabAlignment.Top tc.Appearance = TabAppearance.Normal tc.HotTrack = true tc.Multiline = true tc.Padding = new Point(20,3) tc.SizeMode = TabSizeMode.FillToRight AddHandler tc.SelectedIndexChanged, AddressOf tc_Changed tc.Text = "Some text" ' Get an image for one of the tabs dim img as Image = Image.FromFile( _ "C:\Program Files\Microsoft Visual Studio .NET 2003\" + _ "Common7\Graphics\icons\industry\wrench.ico") dim imgList as new ImageList( ) imgList.Images.Add(img) tc.ImageList = imgList ' Address book tab dim tpAddress as new TabPage( ) tpAddress.Parent = tc tpAddress.Text = "Address Book" ' Datebook tab dim tpDates as new TabPage( ) tpDates.Parent = tc tpDates.Text = "Date Book" ' ToDo tab dim tpToDo as new TabPage( ) tpToDo.Parent = tc tpToDo.Text = "ToDo List" ' Miscellaneous tab dim tpMisc as new TabPage( ) tpMisc.Parent = tc tpMisc.Text = "A Really Long Tab" tpMisc.ImageIndex = 0 ' Address stuff dim lbl as new Label( ) lbl.Parent = tpAddress lbl.Text = "Put the Address book controls on this page." lbl.AutoSize = true lbl.Location = new Point(10,25) ' Datebook stuff dim pnl as new Panel( ) pnl.Parent = tpDates pnl.BorderStyle = BorderStyle.Fixed3D pnl.Location = new Point(10,10) pnl.Anchor = AnchorStyles.Top or AnchorStyles.Bottom or _ AnchorStyles.Left or AnchorStyles.Right pnl.Size = new Size(tpDates.Width - 20, tpDates.Height - 20) dim cal as new MonthCalendar( ) cal.Parent = pnl cal.Location = new Point(25,25) ' ToDo stuff dim rtxt as new RichTextBox( ) rtxt.Parent = tpToDo rtxt.Dock = DockStyle.Fill rtxt.BorderStyle = BorderStyle.FixedSingle end sub ' close for constructor public shared sub Main( ) Application.Run(new TabControls( )) end sub private sub tc_Changed(ByVal sender as object, _ ByVal e as EventArgs) dim tc as TabControl = CType(sender,TabControl) MessageBox.Show("Button " + tc.SelectedIndex.ToString( )) end sub end class end namespace
These sample programs don't do much, so almost all of the action occurs inside the constructor. A TabControl object is declared, instantiated, and given a size and location. Its Anchor property is set so the tab control will automatically resize itself if the user resizes the form, retaining the same margins on all four sides with respect to the form.
Several properties of the tab control are set, including Alignment, Appearance (which is set to its default value), Multiline, Padding, and SizeMode. Setting the HotTrack property to true causes the tab text to change color when the mouse moves over the tab.
The SizeMode property, set in these examples to TabSizeMode.FillToRight, profoundly affects the appearance of the tabs if any of the tabs are of significantly different length and/or if there are more tabs than will fit on one row. The valid values of this property are listed in Table 13-4. The effects of each possible value of the property are seen in Figure 13-5.
Figure 13-6. TabControl SizeMode property: FillToRight, Fixed, and Normal
An event handler method is added to the delegate for the TabControl SelectedIndexChanged event:
tc.SelectedIndexChanged += new EventHandler(tc_Changed);
AddHandler tc.SelectedIndexChanged, AddressOf tc_Changed
Every time the user selects a new tab, this event will be fired, invoking the event handler method tc_Changed. That event handler puts up a message box in this example, but it could do other useful things, such as logging page hits or updating the database.
The event argument for this event is of type EventArgs, so there is no additional information passed about the event. However, the object named sender can be cast back to a TabControl object (which is safe in this case and will not raise an exception), and then all of the TabControl properties are available to the method, including the SelectedIndex property, as shown in the event handler (reproduced here):
private void tc_Changed(object sender, EventArgs e) { TabControl tc = (TabControl)sender; MessageBox.Show("Button " + tc.SelectedIndex.ToString( )); }
private sub tc_Changed(ByVal sender as object, _ ByVal e as EventArgs) dim tc as TabControl = CType(sender,TabControl) MessageBox.Show("Button " + tc.SelectedIndex.ToString( )) end sub
The next several lines of code create an ImageList and populate it with a single image, wrench.ico, which is a standard icon file included with Visual Studio .NET. The ImageList is assigned to the tab control's ImageList property. You need to create the ImageList even though only a single image is used because the TabPage class has an ImageIndex property (listed in Table 13-7) but no Image property, as does the Button class, for example. This image list will be used shortly to put the wrench icon on the fourth tab page.
The four tab pages are instantiated and specified. Each follows the same pattern. The tab page object is instantiated and assigned to a variable. The Parent property of the object is set to the TabControl object, and its Text property is set. The fourth tab page also has its ImageIndex property set to 0 so the wrench icon will display:
TabPage tpAddress = new TabPage( ); tpAddress.Parent = tc; tpAddress.Text = "Address Book"; tpMisc.ImageIndex = 0;
dim tpAddress as new TabPage( ) tpAddress.Parent = tc tpAddress.Text = "Address Book" tpMisc.ImageIndex = 0
|
Each of the first three tabs then has one or more controls added to them. Adding controls to a tab page is the same as adding controls to a form, except that the Parent property of each control is set to the appropriated TabPage object rather than the form.
|
13.2.1 Tabbed Pages Using Visual Studio .NET
In Visual Studio .NET, most controls are represented by an icon in the Toolbox, and can be either dragged onto the form or double-clicked to be placed on the form. The TabControl control follows that model: there is an icon for the TabControl in the Toolbox (
There are two different ways to add TabPage objects to a TabControl control, aside from manually coding them as was done earlier in this chapter. The first way is to select and right-click the TabControl control in Design view, and then click on the Add Tab menu item from the pop-up menu. This will add a tab page directly to the tab control, with a default name of tabPage1 (tabPage2, and so on). Once the tab page is added, you can set its properties by selecting it in Design view and editing the properties in the Properties window.
The second way to add tab pages is to select the tab control in Design view, locate the TabPages property in the Properties window, and click on the Build button (...). This will bring up the TabPage Collection Editor dialog box, shown in Figure 13-6. This dialog box has buttons for adding and removing tab pages, as well as a Properties window for setting the properties of the currently selected tab page. It also has buttons for easily changing the order of the tab pages.
No matter which technique you use, you can use either the Properties windowthe one in the main Visual Studio .NET window or the one in the TabPage Collection Editorto edit the properties of the currently selected tab page.
Figure 13-7. TabPage Collection Editor in Visual Studio .NET