Introduction

I have recently been developing a website that pulls lots of information from many sources and collates it into one account on the site. However, all that information has to be editable by the user whenever she/he likes, through the site. Furthermore, a key aim of the site was ease of use and clarity of information. So, I had the dilemma of how to allow users to easily and clearly edit all their information without having one long page with lots of editable fields. The obvious solution was to use a tab control, but predictably nothing useable existed on the web already. So I created my own custom web control that I present in this article which I hope others will find as easy to setup, use and as useful as I have.

Background

It would probably be useful if you, the reader, know basic CSS, HTML, JavaScript and at least have an idea of how to create dynamic content in C#. I would suggest the following links to be good starting points:

Using the Code

Screen Shots

Initial View of the Tab Control

Initial View of the Tab Control - Basic Tab

Personal Tab of the Tab Control

Personal Tab of the Tab Control

After cliking the test button, the text box on the Basic tab is set to 'Test OK. :)'

After clicking the test button, the text box on the Basic tab is set to 'Test OK. :)'

To show how the code works, we will step through the server side code that builds the Default.aspx page. The code looks as follows:

//Create a tab control
TabControl TheTabCtrl = new TabControl("InfoTabCtrl");

//Block: Create and add a new tab page - BasicPage
TabPage BasicPage = new TabPage("BasicInfoTabPage", "Basic");

BasicPage.Controls.Add(ABox);

TheTabCtrl.Tabs.Add(BasicPage);
//End Block

//Block: Create and add a new tab page - PersonalPage
TabPage PersonalPage = new TabPage("PersonalInfoTabPage", "Personal");

Button AButton = new Button();
AButton.Text = "Test";
AButton.Click += new EventHandler(AButton_Click);
PersonalPage.Controls.Add(AButton);

TheTabCtrl.Tabs.Add(PersonalPage);
//End Block

//Add the tab control to the page output.
form1.Controls.Add(TheTabCtrl.GetControl);

The first line of code creates a new tab control called TheTabCtrl. The constructor takes one argument, Id. This should be a unique Id of the tab control as it is used in the JavaScript (later in this article) to keep track of what tab is selected. TheTabCtrl can then be used to add tab pages and generate the tab control output.

Next, the code creates a new TabPage. The constructor for a tab page takes two arguments, the first is a unique Id of the tab page, this is used in the JavaScript (later in this article) to change the currently displayed tab. The second argument is the Name of the tab. This is what the tab title will be when it is shown the user. In this case, I have picked 'Basic' as the title as that was one of the categories of information my website needed.

To add controls to the tab is very simple, as shown in the next line. All that is required is to call ATabPage.Controls.Add. You can add any object that derives from the System.Web.UI.Control class; this includes all the WebControls and HtmlControls. The Controls collection is simply a list of Control objects that get added to the tab later.
The next block of code just adds another tab page, called Personal, with a button on it to allow me to demonstrate how the tab control works with multiple tabs.

The last line of code adds the tab control to the page. I decided not to derive the TabControl class from the System.Web.UI.Control class as it would have meant overriding many more methods than I needed to. Instead, I have created a property, GetControl that returns a WebControl that can be added to any part of a page. (Note, If using controls with events in any of your tab pages, the TabControl must be added in the Page_Load method so that the control's events link up.) The GetCotnrol property code looks like the following:

public WebControl GetControl
{
	get
	{
		WebControl TabCtrlWrapper = new WebControl(HtmlTextWriterTag.Div);
		TabCtrlWrapper.CssClass = ClassName;
		TabCtrlWrapper.ID = Id;

		TabCtrlWrapper.Controls.Add(TabSelectionControl);

		foreach (TabPage APage in Tabs)
		{
			TabCtrlWrapper.Controls.Add(APage.GetControl);
		}

		Literal BreakLit = new Literal();
		BreakLit.Text = "<div style=\"width:100%; float:none; 
		line-height:0px;\"> <script type=\"text/javascript\">" + 
		CreateChangeTabCall(Tabs[SelectedTab].Id) + "</script></div>";
		TabCtrlWrapper.Controls.Add(BreakLit);

		return TabCtrlWrapper;
	}
}

The code first creates a WebControl, the final control that will be returned, and sets its Id and CssClass. Id was given in the constructor, the CssClass is by default set to ClassName which is, by default, set to 'TabControl'. This links to the CSS file provided allowing easy styling of the tab control. The code then adds the TabSelectionControl (see later for more detail). This control is what allows the user to select different tabs. Then, all the tab pages are enumerated and their controls (see later) added to the final control. Initially, these pages have display:none; to hide them all from the user. The block of code after the loop adds a div, with a space to force the height of the TabControl to be full height including tab pages and TabSelectionControl. A quirk of CSS and floats is that a parent element without text in it, but with floating child elements, will only be the height of those elements if there is some text in a non-floating child element - complicated and weird I know, but the solution above is quite simple. line-height is set to 0 to hide the contents of the div and make it seem as if it weren't there. The JavaScript code runs when the tab control has loaded and so sets the initially selected tab. This can be changed server side by setting the SelectedTab property, which is simply an index of the tab in the list of tabs that the control has.

The Tab Selection Control

Creates the web control that forms the tab selection section. This code enumerates all the tabs, adding a separate 'button area' for each one that displays the name of the tab. Each 'button area' has an onclick event that runs the JavaScript code that changes the tab.

WebControl TheCtrl = new WebControl(HtmlTextWriterTag.Div);
TheCtrl.CssClass = "TabPageSelectionControl";

//Enumerate all tabs, adding a 'button area' for each one.
foreach (TabPage ATab in Tabs)
{
	//Create the web control that will be the 'button area'
	WebControl TabCtrl = new WebControl(HtmlTextWriterTag.Div);
	TabCtrl.CssClass = "TabPageSelectionTitle";
	//Add the onclick attribute using the (custom) 
	//CreateChangeTabCall method with the relevant TabId.
	TabCtrl.Attributes.Add("onclick", CreateChangeTabCall(ATab.Id));
	TabCtrl.Attributes.Add("style", "background-color:;");
	TabCtrl.ID = ATab.Id + "TitleCtrl";

	//Add a literal to display the name of the tab.
	Literal NameLit = new Literal();
	NameLit.Text = ATab.Name;
	TabCtrl.Controls.Add(NameLit);

	//Add the button area to the selection control.
	TheCtrl.Controls.Add(TabCtrl);
}

return TheCtrl;

The TabPage Control

Adds the title of the tab to the tab page control, then adds all the controls specified by the user in the Controls collection.

WebControl TabPageWrapper = new WebControl(HtmlTextWriterTag.Div);
TabPageWrapper.CssClass = ClassName;
TabPageWrapper.ID = Id;
TabPageWrapper.Attributes.Add("style", "display:none;");

TabPageWrapper.Controls.Add(TitleCtrl);
foreach (Control ACtrl in Controls)
{
	TabPageWrapper.Controls.Add(ACtrl);
}

return TabPageWrapper;

Client Side Script

So I've shown how all the server side stuff works, but how does the client side work. Well, I'll start with the JavaScript, then I'll explain how to change the styling. The JavaScript code is very simple and looks like this:

var CurrentTabs = [];

var SelectedBgColour = "Navy";

function ChangeTab(TabControlName, Id, TitleCtrl) {
    if (CurrentTabs[TabControlName] != null) {
        document.getElementById(CurrentTabs[TabControlName]).style.display = "none";
        document.getElementById(CurrentTabs[TabControlName] + 
			"TitleCtrl").style.backgroundColor = "";
    }
    document.getElementById(Id).style.display = "inherit";
    document.getElementById(Id + "TitleCtrl").style.backgroundColor = SelectedBgColour;

    CurrentTabs[TabControlName] = Id;
}

CurrentTabs keeps track of what tab is selected on which tab control. An array is used so that multiple tab control can be on the same page without conflict.

SelectedBgColour is the colour code or name of the background colour the tab title (in the tab selection control) of a selected tab, should have when it is selected. For clear demo purposes, I have chosen Navy as a stark colour to show the effect.

The ChangeTab method takes the currently selected tab (if there is one), hides it, then displays the desired selected tab. It also changes the relevant styles (see above) and then stores the new selected tab. The code is self explanatory.

Styling

All the styling, except the selected tab's title background colour, is done in one short CSS file. As shown below (and explained in comments in the CSS), it is very easy to change the look of the TabControl. I have chosen stark, garish colours that contrast horribly simply to show clearly what the sections of the tab control are, you will definitely want to change these. So, the CSS:

.TabControl
{
	/*The style and colour of the border that surrounds the entire tab control.*/
	border:solid #000000;
	/*The thickness of the border around the tab control. 
	Note: changing to a thicker border here would also require reducing 
	the width of the TabPage style.*/
	border-width:medium;
	/*I thought the tab control looked better without the bottom border 
	in my final website.*/
	border-bottom:0px;

	/*The background colour of the tab control. This is invisible 
	unless the TabPage or TabPageSelectionControl styles don't have 
	background colours.*/
	background-color:Blue;
	/*Makes the tab control the full width of it's container.*/
	width:100%;
}
.TabPage
{
	background-color:Red;
	/*The width of a tab page. You will need to fiddle with this 
	if you change the TabControl border width.*/
	width:99%;

	float:left;
	padding:0.5%;
	overflow:hidden;
}
.TabPageSelectionControl
{
	background-color:Yellow;
	width:100%;
	float:left;
}
.TabPageSelectionTitle
{
	float:left;
	background-color:Aqua;
	padding:2px;
	padding-left:4px;
	padding-right:4px;
	/*Border only on the right to produce separation between tab titles.*/
	border-right:2px solid #000000;
}

History

  • 7/5/2011 - Initial post
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架