Creating a Tabbed View
Categories
Part 1 - Creating a Tabbed View
Part 2 - Creating Dynamic Tabs
Part 3 - Dynamic Tabbed Web Part Pages
Download the code for this article.
During my podcast interview with Craig I discuss how readers of my book learn how to take the basic components in the portal framework - zones, web parts, etc - and to customize them to suit everyday requirements. In the book I really strove to show examples that are quite common, and that my readers would undoubtedly be called upon to implement in their own portals. A couple of these examples were:
- A catalog zone which displays in a pop-up dialog.
- An editor zone with collapsible/expandible editor parts.
- The ability for users to remove web parts that cause a page to crash.
Anyways, today while browsing the ASP.NET forums I saw a request for a common portal feature - tabbed views. Before I explain how to implement such a feature, take a look at the following 2 images which show tabbed views implemented in the Google and Live portals:

Throughout the remainder of this article I will discuss what is required to allow users to have a tabbed view and to add code that allows a user to drag web parts from one tab and onto another tab. When our page is complete it will look like so:
As you can see, it's from this simple page that a user could drag the Untitled web part from WebPartZone1 (which resides on Tab 1) and onto another zone named WebPartZone2 (which resides on Tab 2). OK, without further ado, let's get cracking. First things first, let's create a basic page with some tabs on it...
Create a page with Tabs
For our example, we'll create a very simple page with 2 tabs with a zone in each tab. Each zone will begin with a single web part. To do this I need to add the following HTML to my web form:
<asp:WebPartManager id="WebPartManager1" runat="server" />
<div id="page">
<div id="tabs>
<span class="tabheader" id="tabheader1" onmouseover="DisplayTab(1);">Tab 1</span>
<span class="tabheader" id="tabheader2" onmouseover="DisplayTab(2);">Tab 2</span>
</div>
<div id="tab1" class="tabpage_visible">
<div class="tabbody">
<asp:WebPartZone ID="WebPartZone1"runat="server">
<ZoneTemplate>
<asp:TextBox ID="TextBox1"runat="server" />
</ZoneTemplate>
</asp:WebPartZoneID>
</div>
</div>
<div id="tab2" class="tabpage_hidden">
<div class="tabbody">
<asp:WebPartZone ID="WebPartZone2"runat="server">
<ZoneTemplate>
<asp:Calendar ID="Calendar1"runat="server" />
</ZoneTemplate>
</asp:WebPartZoneID>
</div>
</div>
</div>
From the HTML we can see that the first control which has been added is the WebPartManager; this control is required on all web part pages as it really is the brains of all of the portal operations. Next we can see that I've added a DIV element with an ID of "tabs" which will contain the SPAN's for each tab that I want to display. You could use an HTML UL for the tabs if you want to get a little fancier with the CSS styles but I've used SPAN's for simplicity. After the tab elements we have DIV's for each page that is associated with a tab. For example, the DIV with the ID of "tab1" is associated with the SPAN with the ID of "tabheader1". The idea being that when the user clicks on a tabheader, a piece of javascript will get executed to ensure that the correct tab page is visible. Before taking a look at the javascript which will handle the clicks on the tab headers, I'll quickly present the CSS that is used to layout the page:
.tabpage_visible {
height: 300px;
width: 400px;
background-color: yellow ;
display: "";
position: absolute ;
}
.tabpage_hidden {
height: 300px;
width: 400px;
background-color: yellow ;
display: none ;
position: absolute ;
}
.tabheader {
height: 40px;
width: 150px;
background-color: silver ;
color: black ;
position: relative ;
}
.tabbody {
height: 100%;
background-color: white ;
}
Again, we have some very simple stuff happening here. The tabpages have a style for their visible and hidden state; theres a style for the tabs in the header; and finally a style which sets some styles for the body of a tab page. Very simple stuff. The magic that will allow a user to switch between tab pages is a small piece of javascript which is invoked when the user clicks on tab - it looks like so:
var tabs = ["tab1", "tab2"] ;
var _currentTab = null ;
window.onload = function() {
DisplayTab(1) ;
}
function DisplayTab( tabNumber ) {
var newTab = document.getElementById("tab" + tabNumber) ;
if( newTab == null || newTab == _currentTab )
return ;
for( var i=0; i<tabs.length; i++ ) { var tab = document.getElementById(tabs[i]) ;
tab.className = (tab == newTab) ? "tabpage_visible" : "tabpage_hidden" ;
}
_currentTab = newTab ;
}
Two global variables - tabs and _currentTab - are used to store references to the tabs and to keep track of which tab is the one that is currently being displayed to the user. When window loads, we preset the currently loaded tab to be tab1; this is achieved by calling our DisplayTab helper function and passing in the number of the tab that we want to display. This is the same helper function which gets called when the user clicks on a tab link.
So that's the end of our simple tabbed page and at this point you can run it and click between the tab pages.
Enabling dragging between tabs
Now that we've seen how to create a very simple tabbed interface we need to work out how to allow the user to drag web parts between separate tabs. The way that we'll be doing that in this article is by listening to drag events and then writing some custom logic to determine whether the drag is occurring over one of our pages. To do this we'll attach an event handler to the ondragover event of the document using the attachEvent method - also we must remember to clean up after ourselves by calling detachEvent when the page unloads:
window.onload = function() {
DisplayTab(1) ;
document.body.attachEvent("ondragover", CheckTabs);
}
document.onunload = function() {
document.body.detachEvent("ondragover", CheckTabs);
}
Here we see that the attach/detach event methods have been added to the code which will handle the page loading and unloading. Now whenever the user is dragging web parts around on their page, the method that we've wired up to the ondragover event of the document will be run - the CheckTabs method which looks like so:
function CheckTabs() {
var eventLocation = __wpGetPageEventLocation(window.event, true);
for( var i=0; i<tabs.length; i++ ) {
var tab = document.getElementById(tabs[i]) ;
var headerId =
"tabheader" + (i+1) ;
var tabHeader = document.getElementById(headerId) ;
if (IntersectsWith(eventLocation, tabHeader)) {
DisplayTab( i+1 ) ;
break ;
}
}
}
function IntersectsWith(location, el) {
var withinX = (location.x < (el.offsetLeft + el.offsetWidth)) && (location.x > el.offsetLeft)
var withinY = (location.y < (el.offsetTop + el.offsetHeight)) && (location.y > el.offsetTop)
return withinY && withinX ;
}
The first thing that you'll notice about the CheckTabs function is that it calls a helper function named __wpGetPageEventLocation to determine the location of the dragover event which has just taken place. The wpGetPageEventLocation function is a private function that is contained within the resources of the ASP.NET .dll and probably isn't really designed to be called from your own code
It doesn't really do anything overly special and so you could just include your own logic for getting the location of the event - but I'm going to use their here for brevity.
As we progress down through the CheckTabs method you will see that what we are doing is to cycle through each of the tabs and using another custom function named IntersectsWith to check whether the event location (the drag) is over a tab - if so, we call our handy DisplayTab function to display that tab.
That's really all there is to being able to drag web parts between pages and at this point you should run your page to note that when you drag a web part from one zone, and drag it up over a tab, that the tab page becomes the active page and you will be able to view the other zone.
Gotcha!
For those of you who have made it this far and run the example, you will notice that while you can drag a web part and have it switch pages, you are not actually able to drop that web part into the zone on the other page. When I first created this sample today and I saw this behavior I was stumped - in fact it took me a couple of hours to debug the issue. It turns out that positioning logic on the previously hidden zone is a little screwy and that, whenever we switch between tab pages we must call another private ASP.NET javascript function to make things right
. So, provided you are happy enough calling a couple of undocumented ASP.NET javascript functions I will now show you which function to call
:
function DisplayTab( tabNumber ) {
var newTab = document.getElementById("tab" + tabNumber) ;
if( newTab == null || newTab == _currentTab )
return ;
for( var i=0; i<tabs.length; i++ ) {
var tab = document.getElementById(tabs[i]) ;
tab.className = (tab == newTab) ?
"tabpage_visible" :
"tabpage_hidden" ;
}
_currentTab = newTab ;
__wpm.UpdatePositions() ;}
The function in question is highlighted in red and has been inserted at the end of the DisplayTab helper function. This function cycles through the zones contained within the web part manager and ensures that the left, right, top, and bottom measurements are correctly reflected in the in-memory instances of all zone and web part variables.
That's it, re-run your page now and you should have a perfectly working prototype that allows you to drag web parts from one tab page to another.
Now that you've read this article, you should read part 2 where we will look at how to make the tabs dynamic.
Read Part 2 >>
Comments:
#
AJAX support?
posted by
Steve
on
12/6/2006 2:31:44 PM
:
Great article, was able to get this up and running quickly, thanks very much. Quick question however, doesnt seem to support AJAX update panels? javascript errors. I am not able to do any drag/drop between either zones nor tabs? Would you expect support for the update panel with this??
#
Re: AJAX support?
posted by
The Yeti
on
12/13/2006 12:09:06 AM
:
Hey Steve,
could it be that you have replaced the original WebPartManager and WebPartZone with the versions from Microsoft.Web.Preview.UI.Controls.WebParts (web.config tagMapping)?
That was my problem that caused the WebPartZones to break.
Removing the tag mapping made the example work for me.
Regards,
The Yeti
#
Read your ASP.NET Web Parts book
posted by
Robbe Morris
on
12/13/2006 7:48:51 AM
:
Well done sir. I need to write a review on that at eggheadcafe soon. Needless to say, I thought the code samples and detailed explainations were quite good. People will find your book useful.
#
Project Source - Problems with Webparts
posted by
Anksunamon
on
12/13/2006 8:43:37 PM
:
Hi,
Good article, but could you put a zip file containing all the code to test it?
Have you managed to use webparts with asp.net AJAX for using drag & drop without posting back to the server? Impossible no ?
I have played with webparts for a long time but this often crash my IE6... On many different computers... Have you ever experienced this?
Thanks in advance...
#
Hi Anksunamon...
posted by
Darren Neimke
on
12/13/2006 8:53:24 PM
:
>> Good article, but could you put a zip file
>> containing all the code to test it?
You should be able to extract the behavior from the code that I've posted here. I've posted nearly all the code that is needed for this to work.
>> Have you managed to use webparts with asp.net AJAX
>> for using drag & drop without posting back to the
>> server? Impossible no ?
Unfortunately it's not possible without an incredible amount of effort. On this front I would definitely say that this is very much a V1 implementation by the ASP.NET team.
I haven't experienced the crashing behaviour that you have mentioned.
#
Project Source - Problems with Webparts
posted by
Anksunamon
on
12/13/2006 8:54:59 PM
:
Hi,
Good article, but could you put a zip file containing all the code to test it?
Have you managed to use webparts with asp.net AJAX for using drag & drop without posting back to the server? Impossible no ?
I have played with webparts for a long time but this often crash my IE6... On many different computers... Have you ever experienced this?
Thanks in advance...
#
Ok
posted by
Anksunamon
on
12/13/2006 9:03:58 PM
:
Thanks for the answer... I'll try to copy/paste your code to try it...
Sorry for the double pot, but I've just refreshed the page after having posted my comment... Strange... And Dangerous?
I'll try to send you a simple example of the webpart project that makes me crash my explorer... To wich adress?
#
email addy
posted by
Darren Neimke
on
12/13/2006 9:14:39 PM
:
showusyourcode
AT
hotmail
DOT
com
#
Help!
posted by
Ira
on
12/14/2006 1:15:17 AM
:
The already has been a request for all the code to be made available but though I realise that you have posted all the necessary code, most novices will struggle when it comes to knowing how/where to post the javascript code. Does this go in the .cs page for the code? Does it go in the XHTML page? Just how is this done, novices need to be "spoonfed" a lot of the time!
Cheers
#
Help!
posted by
Ira
on
12/14/2006 1:16:14 AM
:
The already has been a request for all the code to be made available but though I realise that you have posted all the necessary code, most novices will struggle when it comes to knowing how/where to post the javascript code. Does this go in the .cs page for the code? Does it go in the XHTML page? Just how is this done, novices need to be "spoonfed" a lot of the time!
Cheers
#
Spoonfeeding novices...
posted by
Darren Neimke
on
12/14/2006 7:20:33 AM
:
Ira, a couple of things.
1) I don't get paid to this stuff.
2) I don't think that you really help novices by spoonfeeding them.
#
Unable to get it working
posted by
sajithn
on
1/12/2007 6:11:35 AM
:
Thanks a lot for this code .but for some reason i am not able to get the drag and drop functionality ,i could see the web parts but am not able to drag and drop from one tab to another.All i can see in the generated aspx page apart from the code above is the hidden form variable for state and style for the web part zone declared .I have a doubt if it has go anything to do with window.onload. Please help !!
PS : you have written a fantastic book
#
create a page with tabs
posted by
Hua Yang
on
1/12/2007 7:02:39 AM
:
Great articles!
Hi,
I am still using July CTP. I have a page, in the code, at many places, I use CollapsiblePanelExtender. The page has been running many months.
Now I need to add more content to that page; so I added a tabStrip and make the page multiview. There are two views - one view contains my old contents and one view will have new content. Now I introduced a trouble. Whenever the view containing my old code is inactive(the view is invisible), my system crashes by the error below.
Can you help?
Assertion Failed: could not find an HTML element with ID"wpm_XXXX_XXXXx" for control type "Sys.UI.Control"
Many thanks
Hua
#
uncaught exception: permission denied to get property window.DoCatalogPostBack
posted by
Jeff
on
1/19/2007 10:11:00 AM
:
everything work great on my local machine but not on my webserver. have seen this before? there is no descripton in the error tag.
when i click on the webpart to add i got this error.
thank you
#
Does not seem to correctly attach dragover event when using AJAX zone/manager
posted by
Daniel Moore
on
1/31/2007 4:05:06 AM
:
For me, compatibility in Firefox is essential, and dragging and dropping in Firefox requires the AJAX extensions (Microsoft.Web.Preview.UI.... whatever).
Anyway, this code does not seem to work with that. I have a feeling it has to do with the ondragover event not firing correctly. Has anyone gotten this to work with the Microsoft.Web.Preview Zones/Manager?
#
Catalog Zone
posted by
reuben m
on
4/1/2007 7:12:50 AM
:
Hi, I tried the above example. And it works great. I Was wondering if anyone knew how I can restrict the zone that show up in the list of available zone in the Catalog to only the Zones that are visible? For some reason, I have 6 zones total, 3 in div1 and three in div2, when im working with the visible zone, div2, and try to add web parts to a zone, I'm given the option to add web parts to zones in div1 too even though div1 is invisible!
#
source code download
posted by
Dan Rubic
on
4/16/2009 8:05:49 AM
:
Hi I am not able to download souce code from 'Download the code for this article.' link on the top of this page. It is showing runtime error.
#
Why?
posted by
Bill
on
5/7/2009 6:57:12 AM
:
Why is the <asp:WebPartZone's end tag a </asp:WebPartZoneID is it a typo?
#
Nope...
posted by
Darren Neimke
on
5/7/2009 7:18:49 AM
:
There's 2 WebPartZone controls there
#
wow gold
posted by
wow gold
on
6/2/2009 1:27:50 PM
:
very nice site.....
#
safari
posted by
dan
on
6/22/2009 6:55:12 PM
:
can't get this to work at all in safari... could be missing something completely, am relatively new to this.. any ideas what i could be doing wrong? works fine across all IE but tabs aren't even clickable in safari...
#
rolex
posted by
George Matthews
on
9/20/2009 3:26:32 PM
:
Una replica autentica di Rolex avr� una parte posteriore della vite gi� che pu� essere aperta soltanto con un caso di <a href="
http://www.axtonmfg.com">replica Rolex</a> con un attrezzo speciale di Rolex.
Comments are currently disabled for this post.