A user on the Expression forums asked how he could activate a tab within a tab, from a control nested inside those tabs. Normally I would create an action that targeted a TabItem directly, and this could then be used to activate that TabItem using similar code to that below. The problem is that because the TabItem you might be trying to activate may be a sibling to the TabItem your control is in, the system won’t let you target such an item. As an alternative, this behavior takes in the name of a TabItem and activates it and its parents recursively. The code uses reflection to find a reference to the proper TabItem object. This allows you to nest TabItems deeply and activate other tabs from within a tab. The requirement then is that the TabItem have an x:Name applied (rename the item in the Blend Objects and Timeline panel). This name is then specified in the properties of the behavior. The source for the behavior and an example are provided below.
namespace SilverlightApplication10
{
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
public class ActivateTabAction : TargetedTriggerAction<FrameworkElement>
{
public static readonly DependencyProperty TabNameProperty = DependencyProperty.Register("TabName", typeof(string), typeof(ActivateTabAction), new PropertyMetadata(string.Empty));
public string TabName
{
get { return (string)GetValue(TabNameProperty); }
set { SetValue(TabNameProperty, value); }
}
protected override void Invoke(object o)
{
// Find userControl parent.
var control = this.Target;
while(control != null && !(control is UserControl))
{
control = control.Parent as FrameworkElement;
}
if(control == null || !(control is UserControl))
{
return;
}
var uc = control as UserControl;
// Find the TabItem field by its name.
var type = uc.GetType();
var target = type.GetField(this.TabName, BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance);
if(target == null || target.FieldType != typeof(TabItem))
{
return;
}
// Get the tabitem.
var tabItem = target.GetValue(uc) as TabItem;
if(tabItem == null)
{
return;
}
ActivateTab(tabItem);
// Recursively search for tab controls up the visual tree to activate.
var parent = tabItem.Parent;
while (parent != null)
{
if (parent is TabItem)
{
ActivateTab(parent as TabItem);
}
if (parent is FrameworkElement)
{
parent = (parent as FrameworkElement).Parent;
}
else
{
parent = null;
}
}
}
// Find the TabItem's parent TabControl and activate the tab.
private void ActivateTab(TabItem tabItem)
{
var tabControl = tabItem.Parent as TabControl;
if (tabControl != null)
{
tabControl.SelectedItem = tabItem;
}
}
}
}
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:local="clr-namespace:SilverlightApplication10"
x:Class="SilverlightApplication10.MainPage"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" Background="White">
<sdk:TabControl x:Name="tabControl" Margin="64,64,87,103" SelectedIndex="0">
<sdk:TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<sdk:TabControl Margin="0">
<sdk:TabItem x:Name="tabItem" Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</sdk:TabItem>
<sdk:TabItem x:Name="tabItem1" Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</sdk:TabItem>
</sdk:TabControl>
</Grid>
</sdk:TabItem>
<sdk:TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<Button x:Name="button" Content="Tab 1 in Tab 1 (action on button)" Height="53" Margin="46,63,189,0" VerticalAlignment="Top">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<local:ActivateTabAction TargetObject="{Binding ElementName=button}" TabName="tabItem"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button x:Name="button1" Content="Tab 2 in Tab 1 (action on button)" Margin="46,120,189,103">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<local:ActivateTabAction TargetObject="{Binding ElementName=button1}" TabName="tabItem1"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</sdk:TabItem>
</sdk:TabControl>
</Grid>
</UserControl>
June 21st, 2011 on 4:33 am
Hello Chuck,
Thank you very much for this article. Is being very useful for me.
I’m new in Silverlight and MVVM pattern. I try to invoke the trigger from a HierarchicalDataTemplate and when it look for the parent uc of the treeviewitem that has been selected, it find a null object at the datatemplate level. What about this case?
This is the datatemplate:
The target is a “TextBlock”, the parent is a “StackPanel”, and the parent of this is null!