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.

Table 13-1. TabControl properties

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.

Table 13-2. TabAlignment enumeration

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

Table 13-3. TabAppearance enumeration

Value

Description

Buttons

Tabs look like 3-D buttons.

FlatButtons

Tabs look like flat buttons.

Normal

Tabs look like standard tabs.

Table 13-4. TabSizeMode enumeration

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.

If the vertical size of the Padding is set larger than default and Multiline is false, the tabs may display with part of the text truncated if there are more tabs than will fit in a one row.

 

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.

Table 13-5. TabPageCollection properties

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.

Table 13-6. TabPageCollection methods

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.

Table 13-7. TabPage properties

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.

The TabPage TabIndex property is not listed as a member of the TabPage class in the SDK documentation that ships with the .NET Framework (although if you search, it will show up). It also does not show up in the IntelliSense list of all available members for a TabPage object in Visual Studio .NET. However, it will compile and return the value listed in Table 13-7. This functionality is entirely different from the TabIndex property of the Control class.

Its existence can be confirmed by the WinCV class viewer utility, which ships as part of the SDK. To run this utility, open a .NET command prompt (Start Programs Microsoft Visual Studio .NET 2003 Visual Studio .NET Tools Visual Studio .NET 2003 Command Prompt) and enter the command WINCV.

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.

There is actually a second event raised by the TabControl control: DrawItem. It occurs when the tabs are drawn if the DrawMode property was set to TabDrawMode.OwnerDrawFixed. This allows the parent window to draw the tab, giving you control over the appearance of the tabs.

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

If this project were implemented in Visual Studio .NET, the Parent property would not be set this way. Instead, an array of Control objects would be created by Visual Studio .NET, and the AddRange method used to add the array of TabPage objects to the TabControl object's Controls collection. It would look something like:

this.tc.Controls.AddRange(new System.Windows.Forms.Control[ ] { this. tpAddress, this. tpDates, this. tpToDo, this.tpMisc});

Me.tc.Controls.AddRange( _ New System.Windows.Forms.Control( ) _ {Me.tpAddress, Me.tpDates, Me.tpToDo, _ _Me.tpMisc })

which can be seen by expanding the Windows Form Designer generated code.

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.

Referring to the previous note, the Parent property of each control is implicitly set in Visual Studio .NET by using the AddRange method.

 

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 () that can be either double-clicked or dragged onto a form. However, the TabPage control in the Toolbox has no icon.

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

Категории