wpfdata-bindingmvvmmvvm-lightvisualstatemanager

Setting a Visual State from a data bound enum in WPF


Hey all, I've got a scenario where I want to switch the visiblity of 4 different content controls. The visual states I have set opacity, and collapsed based on each given state (See code.) What I'd like to do is have the visual state bound to a property of my View Model of type Enum. I tried using DataStateBehavior, but it requires true/false, which doesn't work for me. So I tried DataStateSwitchBehavior, which seems to be totally broken for WPF4 from what I could tell. Is there a better way to be doing this? I'm really open to different approaches if need be, but I'd really like to keep this enum in the equation.

Edit:

The code shouldn't be too important, I just need to know if there's a well known solution to this problem.

<UserControl
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:Custom="http://schemas.microsoft.com/expression/2010/interactivity" 
         xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
         xmlns:ee="http://schemas.microsoft.com/expression/2010/effects" 
         xmlns:customBehaviors="clr-namespace:SEL.MfgTestDev.ESS.Behaviors"
         x:Class="SEL.MfgTestDev.ESS.View.PresenterControl" 
         mc:Ignorable="d" 
         d:DesignHeight="624" 
         d:DesignWidth="1104" 
         d:DataContext="{Binding ApplicationViewModel, Mode=OneWay, Source={StaticResource Locator}}">
<Grid>
    <Grid.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Layout/TerminalViewTemplate.xaml"/>
                <ResourceDictionary Source="Layout/DebugViewTemplate.xaml"/>
                <ResourceDictionary Source="Layout/ProgressViewTemplate.xaml"/>
                <ResourceDictionary Source="Layout/LoadoutViewTemplate.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Grid.Resources>
    <Custom:Interaction.Behaviors>
        <customBehaviors:DataStateSwitchBehavior Binding="{Binding ApplicationViewState}">
            <customBehaviors:DataStateSwitchCase State="LoadoutState" Value="Loadout"/>
        </customBehaviors:DataStateSwitchBehavior>
    </Custom:Interaction.Behaviors>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="ApplicationStates" ei:ExtendedVisualStateManager.UseFluidLayout="True">
            <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:1">
                    <VisualTransition.GeneratedEasingFunction>
                        <SineEase EasingMode="EaseInOut"/>
                    </VisualTransition.GeneratedEasingFunction>
                    <ei:ExtendedVisualStateManager.TransitionEffect>
                        <ee:SmoothSwirlGridTransitionEffect/>
                    </ei:ExtendedVisualStateManager.TransitionEffect>
                </VisualTransition>
            </VisualStateGroup.Transitions>
            <VisualState x:Name="LoadoutState">
                <Storyboard>
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="LoadoutPage">
                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="LoadoutPage">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="ProgressState">
                <Storyboard>
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ProgressPage">
                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="ProgressPage">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="DebugState">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="DebugPage">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="DebugPage">
                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="TerminalState">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="TerminalPage">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="TerminalPage">
                        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <ContentControl x:Name="LoadoutPage" ContentTemplate="{StaticResource LoadoutViewTemplate}" Opacity="0" Content="{Binding}" Visibility="Collapsed"/>
    <ContentControl x:Name="ProgressPage" ContentTemplate="{StaticResource ProgressViewTemplate}" Opacity="0" Content="{Binding}" Visibility="Collapsed"/>
    <ContentControl x:Name="DebugPage" ContentTemplate="{StaticResource DebugViewTemplate}" Opacity="0" Content="{Binding}" Visibility="Collapsed"/>
    <ContentControl x:Name="TerminalPage" ContentTemplate="{StaticResource TerminalViewTemplate}" Opacity="0" Content="{Binding}" Visibility="Collapsed"/>
    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding ApplicationViewState}">
        <TextBlock.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="Black" Offset="0"/>
                <GradientStop Color="White" Offset="1"/>
            </LinearGradientBrush>
        </TextBlock.Background>
    </TextBlock>
</Grid>


Solution

  • You can do this easily by following the steps below:

    1. Create an event that will be raised when the enum property is changed
    2. Add a GotToStateAction (Behavior) for every value of the enum (define the value in the Conditions part) to change the Visual state. When the property is set the first time and every time the value changes the state will be set to the correct state.

    More info on the GoToStateAction can be found here: http://blogs.msdn.com/b/expression/archive/2010/02/22/switching-visual-states-easily-using-gotostateaction.aspx