VSM Questions/Problems

Mar 3, 2009 at 7:38 AM
Hello!
I am facing some problems with the VisualStateManager and I have some questions.
I defined some VisualStates for a TabControl (We are not using Triggers because we want to transfer it to Silverlight in the future)
I change the states in the code behind. This works fine when a user selects a TabItem. The state changes correctly. But when the application is loaded the first TabItem is selected by default but the state is not correct. Although it goes into the function and changes the state correctly to Selected.

Here is my ControlTemplate:
<ControlTemplate x:Key="TabItemControlTemplate" TargetType="{x:Type TabItem}">
    <Grid Width="Auto" MinHeight="25" x:Name="TabItemRoot" Margin
="0">
        <Grid.ColumnDefinitions
>
            <ColumnDefinition Width
="0.9*"/>
            <ColumnDefinition Width
="0.1*"/>
        </Grid.ColumnDefinitions
>

        <VisualStateManager.VisualStateGroups
>
            <VisualStateGroup x:Name
="CommonStates">
                <VisualStateGroup.Transitions
>
                    <VisualTransition GeneratedDuration
="00:00:00"/>
                </VisualStateGroup.Transitions
>
                <VisualState x:Name
="Disabled"/>
                <VisualState x:Name
="Normal"/>
                <VisualState x:Name
="MouseOver" />
            </VisualStateGroup
>
            <VisualStateGroup x:Name
="FocusStates">
                <VisualState x:Name
="Focused" />
                <VisualState x:Name
="Unfocused" />
            </VisualStateGroup
>
            <VisualStateGroup x:Name
="SelectionStates">
                <VisualStateGroup.Transitions
>
                    <VisualTransition GeneratedDuration
="00:00:00"/>
                </VisualStateGroup.Transitions
>
                <VisualState x:Name
="Selected" >
                   <Storyboard
>
                        <ColorAnimation To="#C5C5C5" Duration="0" 
                                    Storyboard.TargetName
="backRectangle"             
                                     Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[0].
                                            (GradientStop.Color)"/>
                        <ColorAnimation To="#B7B6B6" Duration="0" Storyboard.TargetName="backRectangle"         
                                        Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[1].            
                                            (GradientStop.Color)"/>
                        <ColorAnimation To="#A8A8A9" Duration="0" Storyboard.TargetName="backRectangle" 
                                        Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[2].    
                                            (GradientStop.Color)"/>
                    </Storyboard
>
                </VisualState
>
                <VisualState x:Name
="Unselected" >
                    <Storyboard
>
                        <ColorAnimation To="#FCFBFB" Duration="0" Storyboard.TargetName="backRectangle" 
                                    Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[0].        
                                        (GradientStop.Color)"/>
                           <ColorAnimation To="#D3D3D3" Duration="0" Storyboard.TargetName="backRectangle" 
                                    Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[1].
                                        (GradientStop.Color)"/>
                            <ColorAnimation To="#FDFDFD" Duration="0" Storyboard.TargetName="backRectangle" 
                                        Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[2].
                                            (GradientStop.Color)"/>
                    </Storyboard
>   
                </VisualState
>
            </VisualStateGroup
>
        </VisualStateManager.VisualStateGroups
>    

    <Rectangle x:Name="backRectangle" Height="Auto" Grid.Column="0" Grid.ColumnSpan="2" Stroke="Black" 
                    StrokeThickness
="0">
        <Rectangle.Fill
>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint
="0.5,0">
                <GradientStop Color
="#FFFCFBFB"/>
                <GradientStop Color="#FFD3D3D3" Offset
="1"/>
                <GradientStop Color="#FFFDFDFD" Offset
="0.5"/>
            </LinearGradientBrush
>
        </Rectangle.Fill
>
    </Rectangle
>

    <ContentPresenter Margin="13,5,13,5" x:Name="Content" ContentSource="Header" 
                    RecognizesAccessKey
="True" Grid.Column
="0" />
    
    <Border x:Name="border" Opacity="1" Background="Transparent" BorderBrush="#808180" 
                BorderThickness
="1,1,1,0" Grid.Column="0" Grid.ColumnSpan
="2"/>
</Grid
>
</ControlTemplate
>

In the code behind I am doing this:

private

 

void MainTabs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    TabControl control = (TabControl)sender;
    int index = control.SelectedIndex;
    if (index != -1)
    {
        int i = 0;
        foreach (TabItem item in control.Items)
        {
            if (i == index)
                VisualStateManager.GoToState(item, "Selected", false);
            else
                VisualStateManager.GoToState(item, "Unselected", true);
            i++;
        }
    }
}

How can I set the first TabItem to Selected when it is not selected by a user?
Thanks in advance!

 

Coordinator
Mar 3, 2009 at 1:55 PM

Your selectionchanged method is being called on startup but GoToState is returning false b/c the template of TabItem is not yet built.  Here is an alternative implementation that does work for startup.

public class CustomTabItem : TabItem
{
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        // this will handle the case for applying VSM on startup
        UpdateState(false);
    }

    protected
override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
        if (e.Property == TabItem.IsSelectedProperty)
        {
            // here is the normal case when selection changes
             UpdateState(true);
        }
    }

 

 

    private void UpdateState(bool useTransitions)
    {
        if (IsSelected)
        {
            VisualStateManager.GoToState(this, "Selected", useTransitions);
        }
        else 
        {
            VisualStateManager.GoToState(this, "Unselected", useTransitions);
        }
    }
}

 

 

 
Then replace your ControlTemplate.TargetType to this CustomTabItem as well as the TabItems in the TabControl.  Hope this helps.

Mar 3, 2009 at 2:11 PM
This works fine! Thank you.
Now I understand how this all works together with these custom classes. On your blog I found a similar implementation for the DataGrid. That's what I will need next. Thanks.