Home > C#, MVVM, Windows Phone, WinRT > A Behavior to handle VisualState in Universal apps with MVVM

A Behavior to handle VisualState in Universal apps with MVVM

29/07/2014

Some times ago we talked about a way to handle VisualState in Universal apps using Behavoir SDK and MVVM. In that occasion, we shown how to use DataTriggerBehavior and GotoStateAction to map VisualState to a value of an Enum in the ViewModel.

To make things simpler, we can create a custom Behavior that specifically targets this scenario:

public class EnumStateBehavior : DependencyObject, IBehavior
{
    public DependencyObject AssociatedObject { get; private set; }

    public object Value
    {
        get { return (object)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(EnumStateBehavior), 
        new PropertyMetadata(null, ValuePropertyChanged));

    private static void ValuePropertyChanged(object sender, 
        DependencyPropertyChangedEventArgs e)
    {
        var behavior = sender as EnumStateBehavior;
        if (behavior.AssociatedObject == null || e.NewValue == null) return;

        VisualStateManager.GoToState(behavior.AssociatedObject as Control, 
            e.NewValue.ToString(), true);
    }

    public void Attach(DependencyObject associatedObject)
    {
        var control = associatedObject as Control;
        if (control == null)
            throw new ArgumentException(
                "EnumStateBehavior can be attached only to Control");

        AssociatedObject = associatedObject;
    }

    public void Detach()
    {
        AssociatedObject = null;
    }
}

The Behavior defines a Dependency Property called Value (lines 11-13) that must be bound to an Enum. When its value changes, in the ValuePropertyChanged method (lines 15-23) we make a transition using the VisualStateManager to a state with a name that is equals to the enum value itself.

Then, we define a ViewModel as follows:

public class MainViewModel : ViewModelBase
{
    public enum ViewModelState
    {
        Default,
        Details
    }

    private ViewModelState currentState;
    public ViewModelState CurrentState
    {
        get { return currentState; }
        set
        {
            this.Set(ref currentState, value);
        }
    }

    public RelayCommand GotoDetailsStateCommand { get; set; }
    public RelayCommand GotoDefaultStateCommand { get; set; }

    public MainViewModel()
    {
        GotoDetailsStateCommand = new RelayCommand(() =>
        {
            CurrentState = ViewModelState.Details;
        });

        GotoDefaultStateCommand = new RelayCommand(() =>
        {
            CurrentState = ViewModelState.Default;
        });
    }
}

The CurrentState property at lines 10-17 represents the state of the ViewModel, that we can change using the two RelayCommands defined in the constructor.

Finally, in the MainPage.xaml we must bind this property to the behavior using the Behavior SDK:

<Page
    x:Class="MvvmDemo.MainPage"
    DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}"
    ...
    xmlns:i="using:Microsoft.Xaml.Interactivity"
    xmlns:behaviors="using:MvvmDemo.Behaviors"
    ...
    >

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid x:Name="DefaultPanel" HorizontalAlignment="Center" 
              VerticalAlignment="Center">
            <StackPanel>
                <TextBlock Text="Default State" 
                           Style="{StaticResource SubheaderTextBlockStyle}" />
                <Button Margin="0,20,0,0" Content="Go to Details state" 
                        Command="{Binding GotoDetailsStateCommand}" />
            </StackPanel>
        </Grid>

        <Grid x:Name="DetailsPanel" HorizontalAlignment="Center" 
              VerticalAlignment="Center" Visibility="Collapsed">
            <StackPanel>
                <TextBlock Text="Details State" 
                           Style="{StaticResource SubheaderTextBlockStyle}"
                           Foreground="Yellow" />
                <TextBlock Text="Details message here..." 
                           Style="{StaticResource TitleTextBlockStyle}" />
                <Button Margin="0,20,0,0" Content="Go to Default state" 
                        Command="{Binding GotoDefaultStateCommand}" />
            </StackPanel>
        </Grid>

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="ApplicationViewStates">
                <VisualState x:Name="Default"/>

                <VisualState x:Name="Details">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DefaultPanel" 
                                                    Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DetailsPanel" 
                                                    Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Grid>
    
    <i:Interaction.Behaviors>
        <behaviors:EnumStateBehavior Value="{Binding CurrentState}" />
    </i:Interaction.Behaviors>
</Page>

At lines 34-51 we have created two Visual States with names that correspond to the Enum values specified in the ViewModel. Then, at lines 54-56, we have declared the EnumStateBehavior and bound its Value property to the CurrentState property of the ViewModel. In this way, everytime the latter changes, an automatic transition to the new VisualState will occur.

You can download the working example using the link below.

A Behavior to handle VisualState in Universal apps with MVVM

Categories: C#, MVVM, Windows Phone, WinRT
  1. 29/07/2014 at 09:21

    great article as usual ! thanks ! you wrote behavoirs instead of behaviors for your namespace😉

    • 29/07/2014 at 09:24

      Thank you! I have corrected the typo🙂

  1. 29/07/2014 at 13:15
  2. 30/07/2014 at 03:59
  3. 30/07/2014 at 13:37
Comments are closed.
%d bloggers like this: