Vantage Point: Bob German's Weblog

Notes from BlueMetal Architects, where Bob is SharePoint Principal Architect. Here you will find postings on all things SharePoint, especially developer related topics.

Back in the Zone with ZoneTabs

Back in the Zone with ZoneTabs

  • Comments 21

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 800x800 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!

Blog - Comment List MSDN TechNet
  • Loading...
Leave a Comment
  • Please add 6 and 2 and type the answer here:
  • Post