Update: Ability to remember selected tab added! See C# code below!
Update: jQuery used - now allows jQuery animations to be set (from C# too) for the tab transitions.
Update: Use of HTML5 and CSS3 to bring decent borders and rounded corners - it looks better now basically :).
Introduction
I was recently developing a website that pulled lots of information from many sources and collated it into one account on the site. However, all that information had to be editable by the user whenever (s)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 easy to setup and use and as useful as I have.
Background
It would probably be useful if you, 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 - Basic Tab
Personal Tab of the Tab Control
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:
TabControl TheTabCtrl = new TabControl("InfoTabCtrl");
TabPage BasicPage = new TabPage("BasicInfoTabPage", "Basic");
BasicPage.Controls.Add(ABox);
TheTabCtrl.Tabs.Add(BasicPage);
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);
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";
foreach (TabPage ATab in Tabs)
{
WebControl TabCtrl = new WebControl(HtmlTextWriterTag.Div);
TabCtrl.CssClass = "TabPageSelectionTitle";
TabCtrl.Attributes.Add("onclick", CreateChangeTabCall(ATab.Id));
TabCtrl.Attributes.Add("style", "background-color:;");
TabCtrl.ID = ATab.Id + "TitleCtrl";
Literal NameLit = new Literal();
NameLit.Text = ATab.Name;
TabCtrl.Controls.Add(NameLit);
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;
Code to re-select the user's last tab:
if (IsPostBack)
{
string value = Request.Form[TheTabCtrl.Id + "_SelectedTab"];
if(!string.IsNullOrEmpty(value))
{
try
{
TheTabCtrl.SelectedTab = TheTabCtrl.Tabs.IndexOf(TheTabCtrl.Tabs.Where(x => x.Id == value).First());
}
catch
{
}
}
}
Using new bits of the JavaScript you can now get the last selected tab for your tab controls from the form post back. See above code for a how-to :)
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 = "#C7E9FF";
var NotSelectedBgColour = "#99C8E8";
function ChangeTab(TabControlName, Id, EffectIn, EffectOut, Speed)
{
if (CurrentTabs[TabControlName] != Id)
{
if (!EffectIn)
EffectIn = "fade";
if (!EffectOut)
EffectOut = "fade";
if (!Speed)
Speed = 300;
if (CurrentTabs[TabControlName] != null)
{
$("#" + CurrentTabs[TabControlName])
.hide(EffectOut, { direction: "vertical" }, Speed, null);
$("#" + CurrentTabs[TabControlName] + "TitleCtrl")
.animate({ backgroundColor: NotSelectedBgColour }, Speed);
}
$("#" + Id)
.parent()
.css("height", $("#" + Id)
.show(EffectIn, {}, Speed, null)
.outerHeight(true));
$("#" + Id + "TitleCtrl").animate({ backgroundColor: SelectedBgColour }, Speed);
CurrentTabs[TabControlName] = Id;
$("#" + TabControlName + "_SelectedTab").val(Id);
}
}
$(document).ready(function ()
{
$(".TabPageSelectionTitle").css("background-color", NotSelectedBgColour);
});
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.
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:
.TabControlTabsWrapper
{
position:relative;
width:100%;
height:100%;
overflow:hidden;
background-color:#FAFDFF;
border:solid #000000;
border-width:medium;
border-bottom-left-radius:10px;
border-bottom-right-radius:10px;
-moz-border-bottom-left-radius:10px;
-moz-border-bottom-right-radius:10px;
}
.TabPageSelectionTitle
{
float:left;
padding:2px;
padding-left:4px;
padding-right:4px;
border-right:2px solid #000000;
border:solid #000000;
border-width:medium;
border-bottom:0px;
border-top-left-radius:10px;
border-top-right-radius:10px;
-moz-border-top-left-radius:10px;
-moz-border-top-right-radius:10px;
}
Not all the CSS but the bits that are useful - these set up the borders and corners of the borders. I deliberately left a square corner on the top-right of the tabs, I think it looks nice both in contrast and it mirrors the other side too.
History
- 7/5/2011 - Initial post
- 22/5/2012 - jQuery and HTML5/CSS3 update