Gepost door: antonidol | woensdag 12 mei 2010

Overlapping TabItems with the Silverlight Toolkit TabControl

OverlappingTabs

Standard TabItems are not overlapping, but designers like them to. You may encounter a design with overlapping tabs that you have to create and implement. This is not a simple task, certainly when the tabs have a tapered side that overlaps the tab to the right of it. The Z-Index of these tabs are opposite to the standard layout.

Control Vendors have custom versions of a TabControl, that use properties for overlap. The Toolkit TabControl currently doesn’t support overlapping tabs, but by updating templates and a bit of C# code you can get the same effect. Here’s how it’s done:

Creating an overlapping tab in Expression Design:

OverlappingTabsCreate01

  1. Start Design, start a new file with a size of 640 x 480 pixels.
  2. Add a rectangle, 50 pixels wide, 25 pixels high and make its CornerRadius 3 pixels. Make sure the Rectangle is selected.
  3. Choose Object. Convert Object to Path. This way you can change the vertices of the shape.
  4. Select the Direct Selection tool (second from the top in the Toolbox) and select the bottom right two vertices by dragging a selection area around them.
  5. Drag the two vertices to the right, about half the width of the rectangle. Hold the Shift key to constrain the translation horizontally.
  6. Zoom in to the right hand side of the shape. With the Pen Tool selected, at the right bottom corner delete the second vertice from the bottom by clicking on it.
  7. Hold the Alt Key and click the other vertice to make it a rounded point (fig. 1).
  8. Select the Direct Select Tool and move the second vertice from the top to the right. Select the Pen Tool, hold the Alt Key and drag from this anchor point in the direction of the line of the tab, so the sharp corner becomes smooth.
  9. Using the Direct Selection Tool, move the left vertice at the left bottom corner down to the bottom of the tab, delete the other by clicking on it with the Pen Tool selected (fig. 3).

OverlappingTabsCreate02

figure 1: Create a round point at the bottom right corner.

OverlappingTabsCreate03

figure 2: Create a smooth corner at the top right corner.

OverlappingTabsCreate04

figure 3: Remove a anchor point to create a straight corner at the lower left corner.

Double click the Magnifying Glass Icon at the bottom of the toolbox to show the entire drawing. It helps to place the top right corner of the tab at the 0,0 coordinates using the Action Bar at the bottom of the screen. Check if the Foreground color is white and the border is black. Make sure the tab is selected, showing its bounding box and transformation handles, and select Edit, Copy from the menu.

Implementing overlapping tabs in Expression Blend:

  1. Start Blend, Begin a new Silverlight project, Open MainPage.xaml.
  2. Open the Asset Panel, enter Tab in the search box, select TabControl and drag a rectangle on the Artboard. You’ll get a TabControl with two tabs. A reference to System.Windows.Controls.dll is added to the project references and a namespace xmlns:controls is added to your MainPage.xaml file.
  3. The TabControl is a container for the TabItems. It has a four Grids to place tabs on all sides of the tab area. Tabitems are placed in these grids. The TabItems consist of a Header and a Grid as you can see in the Object and Timeline Panel when a tab is selected. Right click a Tab and select Edit Template, Edit a Copy… from the context menu, choose a name and a location for the ControlTemplate and click OK.

OverlappingTabsImplement01

figure 4. Creating a TabItem ControlTemplate

  1. You’ll enter Template Editing Mode for the TabItem. The Objects and Timeline panel shows eight grids and a FocusVisualElement Border. For each of the four sides (Top, Bottom, Left, Right) you’ll see a Selected and a Unselected Part. Control Parts are recognizable by the green icon next to their name. You can also find them in the Parts Panel.
  2. Locate the TemplateTopSelected Grid in the Object and Timeline Panel and open all the borders and grids that are part of it by clicking the small triangles in from of it. Drag the ContentControl named HeaderTopSelected up to the TemplateTopSelected Grid so it moves to the same hierarchical level. Now you can delete the rest of the content of the TemplateTopSelected Grid. Also delete all the animations that reference those shapes if they are not deleted by Blend.
  3. With this Grid selected, choose Paste from the edit menu. This will place the tab you created in Expression Design into the grid. Remove its Margins to make the tab fit inside the Grid.
  4. Drag the layer of the new Path up so it is placed right beneath the TemplateTopSelected Grid and above the Focus and Disabled visuals in the Objects and Timeline Panel. Move the HeaderTopSelected Content Control beneath the Path.
  5. Repeat Step 3 and 4 for the TemplateTopUnselected Grid. Make its Foreground a slightly darker color than the selected state of the tab.
  6. Set for both the TemplateTopSelected as the TemplateTopUnselected Grid the left margin to –20 pixels. This will ruin the bounding box placing in Blend, but move the tabs to the left at runtime.
  7. Make sure the Grid for the selected state of the tab has a Path at the bottom, that makes the connection with the tab area beneath it. It is important that a tab connects to the area that shows when a tab is selected.
  8. You may want to delete the FocusVisualTop border because it is rectangular and your tabs are not. Also remove any animations targeting this control.
  9. Scope up to the Control Level using the breadcrumbs at the top of the screen or the Return Scope to [UserControl] button at the top of the Objects and Timeline Panel.
  10. Right Click the TabControl and Select Edit Additional Templates, Edit Layout of Items [ItemPanel], Create New. Make sure it is a StackPanel with its Orientation set to Horizontal. Scope up to the Control Level.
  11. Right Click the TabControl (not a tab) and Select Edit Template, Edit a Copy… Give the System_Windows_Controls_Primitives:TabPanel a left margin of 20, but leave the other margins as they are: margin=”20,2,2,-1″ compensating for the negative left margin on the first TabItem.
  12. Use the Visual State Manager to create a state for a MouseOver that is a light gray between the selected and unselected Foreground colors. A transition time of half a second gives the right effect.

        <controls:TabControl x:Name=”TabControl” Width=”500″ Height=”100″
             Style=”{StaticResource TabControlStyle1}”
             ItemsPanel=”{StaticResource ItemsPanelTemplate1}”
             SelectionChanged=”TabControl_SelectionChanged”>
            <controls:TabItem x:Name=”Tab_1″ Header=”Tab 1″
                 Style=”{StaticResource TabItemStyle1}”>
                <Grid Background=”#FFE5E5E5″>
                    <TextBlock Text=”Area 1″ Margin=”20″/>
                </Grid>
            </controls:TabItem>
            <controls:TabItem x:Name=”Tab_2″ Header=”Tab 2″ 
                Style=”{StaticResource TabItemStyle1}”> 
                <Grid Background=”#FFE5E5E5″>
                    <TextBlock Text=”Area 2″ Margin=”20″/>
                </Grid>
            </controls:TabItem>
            <controls:TabItem x:Name=”Tab_3″ Header=”Tab 3″
                 Style=”{StaticResource TabItemStyle1}”>
                 <Grid Background=”#FFE5E5E5″>
                      <TextBlock Text=”Area 3″ Margin=”20″/>
                 </Grid>
            </controls:TabItem>
            <controls:TabItem x:Name=”Tab_4″ Header=”Tab 4″
                 Style=”{StaticResource TabItemStyle1}”>
                 <Grid Background=”#FFE5E5E5″>
                     <TextBlock Text=”Area 4″ Margin=”20″/>
                 </Grid>
            </controls:TabItem>
        </controls:TabControl>

Getting the ZIndex right

All this will create the right shape and states of the the TabControl, but the order of the tabs is still wrong. By default the first tab is selected and a right tab will overlap a left tab. With overlapping tabs this should be the other way around. Setting the Canvas.ZIndex property of the TabItems in XAML won’t help. The control is initialized with the wrong settings at runtime.

A little C# helps. Thanks to Rachel, who solved this problem in WPF, we can set the overlapping tabs in the right order when the SelectionChanged event of the TabControl is fired:

private void TabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)

    TabControl tabControl = sender as TabControl; 
    tabControl.Dispatcher.BeginInvoke( new Action(() =>
            UpdateZIndex(sender as TabControl)));
}

private void UpdateZIndex(TabControl tc)

    if (tc != null)
    {
        foreach (TabItem tabItem in tc.Items)
        {
            tabItem.SetValue(
                        Canvas.ZIndexProperty,
                        (tabItem == tc.SelectedItem ?
                        tc.Items.Count :
                        (tc.Items.Count-1) – tc.Items.IndexOf(tabItem)));
        }
    }
}

The UpdateZIndex method is invoked in a way that updates all the TabItems. This way a left tab will show above a right tab the way you want in overlapping TabItems.

OverlappingTabs

fig 5. Overlapping tabs have a reversed ZIndex.

Working code is at my SkyDrive

Joe Gershgorin has refactored the code to use a Behavior. This also supp0rts longer text in the headers. You can find his version at: http://www.bitspy.com/OverlappingTabs_Silverlight4.zip

Njoy!

About these ads

Responses

  1. Wow, nice article. Thanks for sharing. I once had to do something like this, it was a total hack. Yours is pretty neat. Really impressive.

  2. I look for this for a long time and it’s pretty impressive what you did with few lines of XAML. Not easy to get you on google ..

  3. GOOD WORK!

  4. How do you manage if the text in the header is very long, so that the path has to stretch. In my example it doesn’t look very well….

  5. Thanks, I re-factored the code for Silverlight 4 to use be based off intrinsic styles. And moved the TabItem focus code into a behavior that applies in the control template Style. This allows applying Overlapping tabs app wide with only Styles and no code behind needed! Here’s the code if you’re interested (Behavior requires Blend 4 SDK to be installed).

    http://www.bitspy.com/OverlappingTabs_Silverlight4.zip

  6. Roman, I had the same problem too. I reworked the path/style to accommodate any width header text, you can find it here:

    http://www.bitspy.com/OverlappingTabs_Silverlight4_v2.zip

  7. Hi Joe,
    your first solution works fine. The Second one doesn’t. The Tab 3 is in front of Tab2, that’s right. But the Tab4 is in front of the long Tab3 when the selected tab is Tab1. In my project I’m using a custom tab control from a third party. I’m going to check if your behavior will work with this control

  8. Thanks Antoni, glad you like, thanks for the original code!

    Roman, here’s an updated version with some fixes/improvements that may solve your issue:

    http://www.bitspy.com/OverlappingTabs_Silverlight4_v3.zip

    I’m also working on a version that’s based on the Telerik RadTabControl.

  9. @Joe
    Thanks for your help. I will try tomorrow. A Version for the RadTabControl would be great ;)

  10. Roman, here’s the Telerik version:

    http://www.telerik.com/community/forums/silverlight/tabcontrol/enclosed-example-overlapping-tab-style.aspx

  11. New location for Telerik version:

    http://www.telerik.com/community/code-library/silverlight/tabcontrol/enclosed-example-overlapping-tab-style.aspx

  12. Is the solution with the behavior specific for SL4? I am unable to get it to work with SL3?

    • Joe Gershgorin specifically specifies the code is re-factored for Silverlight 4 to use be based off intrinsic styles and moved the TabItem focus code into a behavior. It seems SL4-only, but Joe should be the one to answer this.

      Your problems could be with the behavior although SL3 knows those too, doesn’t it? Then you could have trouble with the intrisic styles and you may have to name the used styles using a Key and refer to them by that Key.

      Succes!

  13. Hi,

    Thanks for the effort everyone has put into this control for the community. I was able to download your source code and run the application through expression blend perfectly but when I tried to copy your control into an existing project along with the “Behaviors” and “Styles” folder (I also made sure all references to your “OverlappingTabs” project were updated to my existing projects name) I could see the control styled corrected within the blend designer surface but once I ran the project the style was dropped for some reason revealing the tab control in its default black form? My App.xaml references the correct ResourceDictionary style location but have I missed some other reference required for the styling?

    Thanks.

    • Only thing I can think of is to make sure the Build Actions voor the specific parts are correct and links to ResourceDictionaries are working. If it works in a Blend project it should work in your own projects. Remember the Styles are implicit now, so should work on a TargetType and don’t need a x:Key. Maybe others have suggestions for you? You could mail Joe Gershgorin directly…

  14. [...] http://antonidol.wordpress.com/2010/05/12/overlapping-tabitems-with-the-silverlight-toolkit-tabcontr… [...]


Geef een reactie

Vul je gegevens in of klik op een icoon om in te loggen.

WordPress.com logo

Je reageert onder je WordPress.com account. Log uit / Bijwerken )

Twitter-afbeelding

Je reageert onder je Twitter account. Log uit / Bijwerken )

Facebook foto

Je reageert onder je Facebook account. Log uit / Bijwerken )

Google+ photo

Je reageert onder je Google+ account. Log uit / Bijwerken )

Verbinden met %s

Categorieën

Volg

Ontvang elk nieuw bericht direct in je inbox.

%d bloggers like this: