SketchFlow / Using (Selected) TabItem in TabControl to Trigger State

2.2k views Asked by At

Context: A new Sketchflow / Silverlight project. Expression Blend 4 (Ultimate)

I have the below code. I have a TabControl with 2 TabItem's. I also have 2 "callout" (big bubble things) as quick visual on the state change.

I created a VisualStateGroup and added to States under it. When I manually invoke these from the NAVIGATE window (after I run the project), the states work as expected. callout1 and callout2 flip flop their opacities (between 100% and 10%). So I have a basic understanding of State's and how they work.

However, when I add a trigger event to the TabItem, the trigger looks good, but does not work. Below is a stripped example, down to the bare bones.

I tried EventName="MouseLeftButtonDown" and EventName="Click" with no luck.

I also commented out the ObjectAnimationUsingKeyFrames tags, no luck there as well.

Anybody see what I'm missing?

Basically, I cannot get (selecting) a TabItem to trigger a State change.

Thanks.

-----------START XAML CODE

<UserControl 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:data="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
xmlns:System="clr-namespace:System;assembly=mscorlib" 
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:pi="http://schemas.microsoft.com/prototyping/2010/interactivity"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
    x:Class="MyProject.MyScreen"
    Width="640" Height="480" mc:Ignorable="d">

    <Grid x:Name="LayoutRoot" Background="White">

                <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="VisibleTabs">
                <VisualState x:Name="Tab1VisualState">
                    <Storyboard>
                        <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout1" d:IsOptimized="True"/>
                        <DoubleAnimation Duration="0" To="0.1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout2" d:IsOptimized="True"/>

                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(TabControl.SelectedIndex)" Storyboard.TargetName="tabControl">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <System:Int32>0</System:Int32>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>


                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Tab2VisualState">
                    <Storyboard>

                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(TabControl.SelectedIndex)" Storyboard.TargetName="tabControl">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <System:Int32>1</System:Int32>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>

                        <DoubleAnimation Duration="0" To="0.1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout1" d:IsOptimized="True"/>
                        <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout2" d:IsOptimized="True"/>


                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>


        <data:TabControl x:Name="tabControl" Height="150" Margin="41,0,215,50" VerticalAlignment="Bottom" SelectedIndex="0">
            <data:TabItem Header="Tab Number One" Height="24" VerticalAlignment="Bottom">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseLeftButtonDown">
                        <pi:ActivateStateAction TargetState="Tab1VisualState"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>

            </data:TabItem>
            <data:TabItem Header="Tab Number Two">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseLeftButtonDown">
                        <pi:ActivateStateAction TargetState="Tab2VisualState"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>

            </data:TabItem>
        </data:TabControl>

        <ed:Callout x:Name="callout1" AnchorPoint="0,1.25" CalloutStyle="Oval" Content="Tab1 Rocks" Foreground="{StaticResource BaseForeground-Sketch}" Fill="{StaticResource BaseBackground-Sketch}" FontSize="{StaticResource SizeDouble-Sketch}" FontFamily="{StaticResource FontFamily-Sketch}" ed:GeometryEffect.GeometryEffect="Sketch" HorizontalAlignment="Left" Height="100" Margin="0,84,0,0" Stroke="{StaticResource BaseBorder-Sketch}" StrokeThickness="2" VerticalAlignment="Top" Width="200" Opacity="1.0"/>
        <ed:Callout x:Name="callout2" AnchorPoint="0,1.25" CalloutStyle="Oval" Content="Tab2 Rocks" Foreground="{StaticResource BaseForeground-Sketch}" Fill="{StaticResource BaseBackground-Sketch}" FontSize="{StaticResource SizeDouble-Sketch}" FontFamily="{StaticResource FontFamily-Sketch}" ed:GeometryEffect.GeometryEffect="Sketch" HorizontalAlignment="Left" Height="100" Margin="200,84,0,0" Stroke="{StaticResource BaseBorder-Sketch}" StrokeThickness="2" VerticalAlignment="Top" Width="200" Opacity="0.1"/>       


    </Grid>
</UserControl>
2

There are 2 answers

1
Andrew On

A few ideas but nothing definite:

  1. It may be that the stateActions need to be told about the location of the visualstates (note there should be the usual < /> around this code but the code will not show up at all if I include these)

    pi:ActivateStateAction TargetState="Tab2VisualState" TargetObject="{Binding ElementName=LayoutRoot}"
    
  2. you could try a GoToStateAction instead (I know this is the Blend way and not sketchflow but they are very similar)

  3. try a ControlStoryBoardAction as the trigger, for this you will need to name your storyboards.

If none of the above work this may help to narrow down the issue but fundamentally your code looks sound.

2
Chuck Hays On

Here is a simple trigger you can use to trigger actions based on tab selection. Add this to your project, compile, and set the trigger of the behvior to an instance of this trigger type. The behavior has to be attached to the TabControl itself. Then set the TabIndex of the trigger to the index you want to trigger the action. The trigger listens to the selection changed event of the tabcontrol and matches that against the TabIndex value you supply.

public class TabSelectedTrigger : TriggerBase<TabControl>
{
    public static readonly DependencyProperty TabIndexProperty = DependencyProperty.Register("TabIndex", typeof (int),
                                                                                             typeof (TabSelectedTrigger),
                                                                                             new PropertyMetadata(-1));
    public int TabIndex
    {
        get { return (int)this.GetValue(TabIndexProperty); }
        set { this.SetValue(TabIndexProperty, value); }
    }


    protected override void OnAttached()
    {
        base.OnAttached();

        this.AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
    }

    void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if(this.TabIndex == this.AssociatedObject.SelectedIndex)
        {
            this.InvokeActions(null);
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
    }
}

Example usage:

<sdk:TabControl Margin="59,49,67,81">
        <i:Interaction.Triggers>
            <local:TabSelectedTrigger TabIndex="1">
                <ei:GoToStateAction StateName="VisualState1"/>
            </local:TabSelectedTrigger>
            <local:TabSelectedTrigger TabIndex="0">
                <ei:GoToStateAction StateName="VisualState"/>
            </local:TabSelectedTrigger>
        </i:Interaction.Triggers>
        <sdk:TabItem Header="TabItem">
            <Grid Background="#FFE5E5E5"/>
        </sdk:TabItem>
        <sdk:TabItem Header="TabItem">
            <Grid Background="#FFE5E5E5"/>
        </sdk:TabItem>
    </sdk:TabControl>