Back in the Zone with ZoneTabs


I’m back after a long break from posting to announce the availability of a new web part that you may find helpful.


Zone Tabs 2.0 is a new version of a tab web part I previously released on GotDotNet that helps reduce clutter on a web part page by allowing you to attach the other web parts in a zone to tabs. (A web part zone is one of those rectangular areas where you can drop your web parts on a page.) For example, a web part zone might contain 10 web parts, but instead of scrolling to see them all, the user clicks different tabs to show a subset of related web parts at any one time.


Screen shot


As you can see in the screen shot, it’s possible to use more than one set of Zone Tabs on a page so long as they’re each in their own zone. In addition, Zone Tabs can be set up to pivot the web part zone between horizontal and vertical views, a function that is generally performed by developing a new page in Visual Studio or customizing a page in SharePoint Designer 2007. The new version of Zone Tabs requires Microsoft Office SharePoint Server 2007 or Windows SharePoint Server 3.0 to work.


To download the web part as a WSS Solution Package as well as full source code, please visit the new MSDN Code Gallery at http://code.msdn.com/ZoneTabs. The Read Me file explains how to install and set up the web part. This posting will go into a little more information about how I wrote it, and some cool things I learned along the way.


Creating Tabs with CSS


Horizontal TabsThe first thing I wanted to do was to make the tabs look better than they did previously. ZoneTabs 1.0 rendered itself as a table, and used some of the built-in WSS styles; for some reason they didn’t look as good in the new version of SharePoint products, even though the overall SharePoint UI was greatly improved. So I set out to make the tabs look better.


I found a number of blog articles on various ways to create tabs in HTML, and wanted something that was easy and attractive, while also easily adapting themselves to varying lengths of text. I decided on a technique called “sliding doors” which was described on several sites. Basically, the idea is to render the tabs as a bunch of anchor elements within unnumbered list items, such as:


<ul>


  <li><a href=”#”>Tab1</a></li>


  <li><a href=”#”>Tab2</a></li>


</ul>



When I first saw this I was pretty surprised that list items were being used, but it turns out that it’s possible to override the usual bulleted list using style sheets. The CSS sets the <li> tags to show most of the tab, including the top and one of the sides, and the <a> tag to show the other side. The <li>’s image is as big as a tab could ever be. This is the clever part: since the <a> tag is on top of the <li> tag, the tab edge in the <a> tab overlaps the big image in the <li> tab and thus trims it perfectly to size. For a detailed drill-down, check out David Bowman’s article at http://www.alistapart.com/articles/slidingdoors/; this has one of the clearest explanations.


For ZoneTabs, I re-wrote the CSS to my own needs and generated the tab images myself from scratch. I used a vector graphics program to make these GIANT tabs, including some blended highlights for a 3-D effect, which I converted to bitmaps (.gif files) that were much smaller and anti-aliased. Then I sliced off the edge to make two images, each of which I extended until they were big enough to handle an 800×800 pixel tab. Eventually I had an HTML page with nice extensible tabs on it, in four color sets (light and dark in blue, black, gray and gold). This was pretty tedious, I have to admit.


Making the Tabs into a WebControl


The next step was to take the HTML tabs and make them into a control suitable for use in a web part. I realized that tabs were a list of items, so I decided to jump right in and subclass the ListControl base class. For the hyperlinks, I used LinkButton controls:


int i = 0;


foreach (ListItem li in this.Items)


{


      LinkButton lb = new LinkButton();


      // Elsewhere in this class, the code expects the ID to be a


      // string representation of the tab’s item index


      lb.ID = i++.ToString();


      lb.Text = li.Text;


      lb.Font.Size = FontUnit.Point(_fontSize);


      lb.Font.Bold = _bold;


      lb.Click += new EventHandler(tab_Click);


      this.Controls.Add(lb);


}


 


The best part is that the ListControl base dealt with the ListItems for me … whether they are data bound or added using the Add() method, by the time my code ran (in CreateChildControls() or the OnDataBound event), the ListItems were ready and waiting so I could create a LinkButton for each one.


The next bit of magic is in the Render() method, and it’s as simple as emitting everything except the anchor tags around the LinkButton controls as they render.


foreach (Control c in this.Controls)


{


      if (c is LinkButton)


      {


            // We know the control’s ID is the item index,


            // so check to see if we’re rendering the


            // selected index and decorate the control accordingly


            int controlIndex = Convert.ToInt32(c.ID);


            if (controlIndex == this.SelectedIndex)


            {


                  writer.Write(“<li class=\”selected\”>”);


            }


            else


            {


                  writer.Write(“<li>”);


            }


            c.RenderControl(writer);


            writer.Write(“</li>”);


        }


}


 


The last piece is the event handler that handles clicking on the LinkButton controls, and adjusts the selected list item accordingly. It also fires the OnSelectedIndexChanged() event if the selected index has changed.


private void tab_Click(object sender, EventArgs e)


{


if (sender is LinkButton)


      {


            LinkButton lb = sender as LinkButton;


            int oldIndex = this.SelectedIndex;


            int newIndex = Convert.ToInt32(lb.ID);


 


            // If the user clicked a new index, then


      // update the selected item


            // and fire our SelectedIndexChanged event


            if (oldIndex != newIndex)


            {


                  if (oldIndex >= 0)


                  {


                        this.Items[oldIndex].Selected = false;


                  }


                  this.Items[newIndex].Selected = true;


 


                  OnSelectedIndexChanged(e);


            }


}


}


 


Making it into a Web Part


Remember that ZoneTabs was an update from an old-style WSS web part. The first step was to change the base class to System.Web.UI.WebControls.WebParts.WebPart, and to change the ToolPart (the fly-out used to configure the web part) to an EditorPart. I didn’t change the EditorPart much at all, though I do have some ideas on how to make it better … as they say “shipping is a feature” so I decided to make some minor cosmetic changes such as allowing users to change the tab colors, and to leave the main logic alone.


The EditorPart is just a brute force composite control, with lots of text boxes, check boxes and drop-downs to capture all the information. The tricky part is the ability to select which of the other web parts in a zone should be shown when each tab is selected. As in the original version, this is stored in a web part property in the form of an XML string.


<tabs>


      <tab name=”First Tab”>


            <webPart title=”Part1” visible=”false />


           


      </tab>


</tabs>


 


I can think of more elegant ways to do this, and to let the .NET framework do the serialization for me, but I decided to preserve the code on both the EditorPart and WebPart ends and to create and query the XML using the XML DOM and XPath queries. The trick to storing the tabs is that I store only the web parts that I want to hide and ignore the ones I want to show under each tab. When ZoneTabs runs, it loops through the web parts in its zone and checks each one to see if it should be hidden. This works best for a couple of reasons – first of all, if someone adds a web part after the tabs are in place, it will be ignored (and always shown). Secondly, if a web part is hidden due to audiences or personalization, the built-in logic won’t be affected. Thus ZoneTabs play nice with audiences and personalization.


Actually hiding a web part is very simple … I set its Hidden property to true. If I can’t find a reason to hide it, I clear the Hidden property by setting it to false.


Flipping on its side


Then I had an idea … why not try pivoting the web part zone on its side using the API? Sure enough, it worked just fine by simply changing this.Zone.LayoutOrientation. Most of the work was in adapting the CSS and images so the tabs would show up sideways as well, and this threw me back into tedious image manipulation for a little while. It’s ironic that when the zone is vertical the tabs are horizontal and vice-versa, but it makes sense if you think of it, so the tab orientation also needs to be flipped based on the web part settings.


Check it Out


Please go download the web part and leave a comment either here or on the MSDN Code Gallery and let me know what you think! For now it’s still released as a “Beta”; a few loyal Tab Part users from the old version have had success using them, but I’d like a little more feedback before I mark it as released.


Thanks!


This posting is provided “AS IS” with no warranties, and confers no rights. Thank you for reading it!


Comments (21)

  1. T Henry says:

    hi, great webparts. is there an easy way to customize the themes.

  2. Bob German says:

    The difficulty depends on how cleanly you want to add a "theme". For each theme, there is a set of images and CSS styles. You could create your own images (perhaps by recoloring some of mine) and then override my CSS styles in your own style sheet. That’s the simplest approach. If you wanted to name your theme and make it selectable when editing the web part, then you’d need to change the code.

    The CSS file is at _layouts/images/EasyTabParts.css (within any site). You’ll notice there is a set of styles for each "theme"; these reference the images that form the background of each tab.

    I hope this helps – thanks!

  3. Adi Jayaram says:

    Hi,

    The editor part does not display the checkboxes

    to enable zones on individual tabs, please advice.

    1. Each row of check boxes corresponds to a web part in the zone, with a checkbox for each of the tabs (by number).

    Thanks, Adi

  4. Bob German says:

    This will normally occur if there are no other web parts in the zone. Zone Tabs needs other web parts so it can show/hide them based on its tabs.

    Are you sure you’ve placed the Zone Tabs in a zone with other web parts?

  5. Aniket says:

    Very nice piece of work…

    Also it would be great if it will not postback….

  6. Jack Lapke says:

    Thanks for sharing this.  I don’t have permissions to import web parts, but I’ll ask system administrators here.  I find is amazing that a capbaility that was in SharePoint 2003 was dropped.

  7. Bob German says:

    FYI – I have re-posted the SharePoint 2003 version for those who need it.

    Nothing was dropped, I just decided to port this to the new ASP.NET 2.0 style web parts, which only work with the new version of SharePoint. Also I wanted to take advantage of the "features" and "solutions" capability for installing and managing customizations to SharePoint, which are MUCH more powerful than what was there before… in the 2003 version I avoided adding any images or style sheets because creating the setup was so difficult.

    Anyway thanks for the feedback!

  8. Suresh says:

    Is there any way to persist the current tab through a document/comment upload/insert?  I’ve got several tabs that show List View Web Parts with the summary toolbar.  A user can click the third tab, Documents, and see a list of all documents.  They can also click ‘Add a new document’ at the bottom and go through that process.  At the end of that process, they are sent back to the page with the Zone Tabs Web Part, but the selected tab is always the first tab.  It would be great to have a way to have the selected tab be the tab they were just on.

  9. Bob German says:

    Thanks Suresh –

    It’s funny you ask because a bug I had early on was that the tabs got reset with any postback on the page! You’ll see some checks in the code to ensure that doesn’t happen. The tricky part about your scenario is that the user leaves the page and returns to it via an http get, the usual postback technique of remembering the tab selection won’t work.

    Perhaps I could drop a cookie on them to remember their most recent tab selection… I’ll keep that in mind for the next version 😉

    Thanks!

  10. bache says:

    Great job, thanks!

    I want to make a slight change – to select a tab according to a parameter in the query string. However I face up some difficulties with building the new solution. I make a new key file to sign the assembly and change the PublicKeyToken in the ZoneTabs.webpart and manifest.xml. I create the new solution, add it, deploy it, activate the feature. But when I try to add the web part, I recieve the error which says that the web part cannot be imported.

    Is there anything else which I am missing?

    Thanks!

  11. Bob German says:

    Thanks Bache — seems to me it should work if you checked the public key w/the sn command and re-deployed the solution. If you were previously running my version of the web part you might need to manually remove the ZoneTabs.webpart file from the Web Part gallery; I’ve had issues where the .webpart files don’t get updated, which would leave the old public key in place.

    Best of luck – thanks!

  12. Peter. says:

    Hi Bob,

    The Page Tab control is not included in new version?, i have a blank site and i need Page Tab Navigation…

    Regards

  13. petka says:

    Just want to second Aniket on postback issue. Webpart works great, but causes postback when tabs are clicked. Any ideas?

  14. Bob German says:

    Hi Petka – Yes I know, I know! 🙂

    I need to find some "free time" (what’s that?) to add an AJAX type feature. I think it’s a great idea; I’ll post on this blog when I get it, but right now I’m over my head with other work and alas this takes a back seat…

    Thanks!

  15. Joy Kevin Machado says:

    How can i use this control to use ajax.

  16. jikner says:

    Is there any way to add additional tabs?

  17. Mehul Bhuva says:

    Hi Bob,

    This webpart of yours is a wonderfull contribution to the Sharepoint community. If we can add a flavour of AJAX into it, it will be the most famous webpart within the moss community…

  18. Mehul Bhuva says:

    Hi,

    I have AJAX enabled the Zone Tab webpart, i have written a step-by-step how-to article on my blogsite, check it out at: http://mehulbhuva.blogspot.com/2009/02/ajaxify-zone-tabs-web-part-in.html

  19. Tina says:

    Hi, I was trying to fine the download for SharePoint 2003 and didn’t see this. Is it still available? I really need it. Thanks

  20. pretty_kiddy says:

    thank your Excellent job

    a css bug for ie 6 ?

    http://www.wood-son.com/temp/ie.jpg

Skip to main content