Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Update VisualState From ViewModel

0.00/5 (No votes)
24 Jan 2011 1  
A nice and cleaner way of handling visual states on xaml

Introduction

A nice and cleaner way of handling visual states on xaml is setting the states from ViewModel rather than setting Visibility of each control using Binding and then setting binding at viewmodel. Creating visual state groups simplify concept of understanding the UI and gives more flexibility for designers to work on it independently. Below I have designed simple example to demonstrate the concept

Using the code

Step 1: Create State Manager Class
To start off the work, firstly you would need a state manager class The StateManager class would bind the dependency object on view to the view model and using this binding, we can interact with the visual states from view model. Below is a sample class which registers 'VisualState' as a dependency property. The StateManager class is derived from DependencyObject, which would allow us to register a dependency property and get and set values on it using GetValue and SetValue methods.

public class StateManager : DependencyObject
{
  public static string GetVisualStateProperty(DependencyObject obj)
  {
     return (string)obj.GetValue(VisualStatePropertyProperty);
  } 
  public static void SetVisualStateProperty(DependencyObject obj, string value)
  {
    obj.SetValue(VisualStatePropertyProperty, value);
  } 
  public static readonly DependencyProperty VisualStatePropertyProperty = 
                                    DependencyProperty.RegisterAttached("VisualStateProperty",typeof(string),typeof(StateManager),
                                    new PropertyMetadata((s, e) =>
                                              {
                                                  var propertyName = (string)e.NewValue;
                                                  var ctrl = s as Control;
                                                  if (ctrl == null)
                                                      throw new InvalidOperationException("This attached property only supports types derived from Control.");
                                                  System.Windows.VisualStateManager.GoToState(ctrl, (string)e.NewValue, true); 
                                               }
                                    )); 
} 


Step 2: Register this dependency object to your control and link ViewModel
Add this state manager to the control and attach the ViewModel with this using Binding.
In your View.xmal add below line:

xmlns:Shared="clr-namespace:GUI.Modules.Shared" 
    Shared:StateManager.VisualStateProperty="{Binding VisualStateName}"

Where : StateManager is the class you created in step 1
& VisualStateName is property(string) in your viewmodel class. Using this binding, we can interact with view from viewmodel class

Step 3: Add your Visual States
Adding visual states is easy piece. Decide what all states your object must be set and add states under a VisualStateGroup.
Below I have added a VisualStateGroup 'TriggerState' which can be either in 'TriggerHigh' state or 'TriggerLow' state. In the page below, I have a button and two textboxes(hiTrigger & lowTrigger). I have used command binding on button to interact with ViewModel using MVVMLight(more details on this can be found at http://mvvmlight.codeplex.com/ . On click of button, ToggleTriggers event gets fired. This is handled at viewmodel class where I am simply toggling the state of controls using visualstates.

<Grid x:Name="LayoutRoot">
  <VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="TriggerState">
     <VisualState x:Name="TriggerHigh">
      <Storyboard>
           <!-- set the trigger control enable/disable
                set high trigger enabled
                set low trigger disabled
           --> 
           <ObjectAnimationUsingKeyFrames Storyboard.TargetName="hiTrigger"
                       Storyboard.TargetProperty="IsEnabled" Duration="0"> 
                <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
           </ObjectAnimationUsingKeyFrames>
           <ObjectAnimationUsingKeyFrames Storyboard.TargetName="lowTrigger"
                       Storyboard.TargetProperty="IsEnabled" Duration="0">
                <DiscreteObjectKeyFrame KeyTime="0" Value="False"/> 
           </ObjectAnimationUsingKeyFrames>
       </Storyboard>
   </VisualState>
   <VisualState x:Name="TriggerLow">
      <Storyboard>
           <!-- set the trigger control enable/disable
                set high trigger disabled
                set low trigger enabled
           --> 
           <ObjectAnimationUsingKeyFrames Storyboard.TargetName="hiTrigger"
                       Storyboard.TargetProperty="IsEnabled" Duration="0"> 
                <DiscreteObjectKeyFrame KeyTime="0" Value="False"/>
           </ObjectAnimationUsingKeyFrames>
           <ObjectAnimationUsingKeyFrames Storyboard.TargetName="lowTrigger"
                       Storyboard.TargetProperty="IsEnabled" Duration="0">
                <DiscreteObjectKeyFrame KeyTime="0" Value="True"/> 
           </ObjectAnimationUsingKeyFrames>
       </Storyboard>
   </VisualState>

 </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="40" />
            
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBox x:Name="hiTrigger" Text="hiTrigger" Margin="8" />
            <TextBox x:Name="lowTrigger" Text="lowTrigger" Margin="8" />
        </StackPanel>
        <Button Grid.Row="1" Content="ToggleTriggers" Width="100" 
                Height="20" HorizontalAlignment="Left" Margin="8"
                Command="{Binding ToggleTriggers}" />
</Grid>

 


Step 4: Create VisualStateName property in ViewModel
Create a public property in ViewModel 'VisualStateName' (same name as you binded in control xaml)

public string _visualStateName;

public string VisualStateName
{ 
   get { return _visualStateName; }
   set { _visualStateName = value;
   NotifyPropertyChanged("VisualStateName");} 
} 


Step 5: Setting VisualState from ViewModel
set the state required by application.

VisualStateName = "TriggerHigh";


Thats it !!!
Happy Coding.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here