6
votes

When using the VisualStateManager in WPF there can be a requirement to transition to a VisualState on control initialization. As far as I can tell there is no way to declare an initial state in Xaml, leaving you with the limited option of transitioning to the required state in your code behind after initialization.

Using code behind is not always desirable, and if you are using a Binding to control your VisualStates then not always possible.

So the question is: how do you set an initial VisualState in WPF without setting it in the code behind?

2
Not sure if I follow this correctly but are you trying to transition to a State on Startup without using code-behind? If so you could use the GoToStateAction msdn.microsoft.com/en-us/library/ff723953(v=expression.40).aspx and set the event trigger to be for Loaded and choose the corresponding state. Is that what your after?Viv
This is what I am trying to do, however to add a little more complexity the initial state is dependent on a bound property. The solution you have given uses Blend which I am not using and have no familiarity with. I don't really want to use it if I don't have to. Thanks for the suggestion though.Richard E

2 Answers

3
votes

Too long for a comment

Binding "should" make no difference. If it works fine from code-behind it's bound to work from xaml unless there is something really weird in the Bindings.

All of blend's actions can be considered as a xaml helper tool. End result is you get some xaml that blend creates for you. If you do not want to use blend. Just add the xaml yourself in VS.

For this very thing the GoToStateAction can be coded such as

<Window ...
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        ...>
...
<Button x:Name="button"
        Style="{DynamicResource ButtonStyle1}">
  <i:Interaction.Triggers>
    <i:EventTrigger>
      <ei:GoToStateAction StateName="YourState"
                          TargetObject="{Binding ElementName=button}" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

You'll need the corresponding references in your project as well.

On a side-note do try blend. It has it's advantages in specific places. You prolly would not replace typing xaml directly, but it serves as a good helper tool. Ignoring it completely unless forced to is pointless IMO.

0
votes

You can directly bind any control with visual state at the time of initialisaton itself in xaml. You need to create one dependency property to change the state. hope below code can help you .

<Grid  model:StateManager.VisualStateProperty="{Binding VisibilityState}" >
        <Grid.RowDefinitions>
            <RowDefinition Height="48" />
            <RowDefinition Height="97" />
            <RowDefinition Height="65" />
            <RowDefinition Height="297" />
        </Grid.RowDefinitions>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="VisibleStateGroup">
                <VisualState x:Name="VisibleState">
                    <Storyboard Duration="0:0:0">
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="myGrid" Storyboard.TargetProperty="(UIElement.Visibility)">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0">
                                <DiscreteObjectKeyFrame.Value>
                                    <Visibility>Visible</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>

                    </Storyboard>
                </VisualState>
                <VisualState x:Name="CollapsedState">
                    <Storyboard Duration="0:0:0">
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="myGrid" Storyboard.TargetProperty="(UIElement.Visibility)">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0">
                                <DiscreteObjectKeyFrame.Value>
                                    <Visibility>Collapsed</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>

            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Grid Name="myGrid" Grid.Row="0" Grid.ColumnSpan="2" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="383*" />
                <ColumnDefinition Width="383*" />
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0,0,15,0" HorizontalAlignment="Right" VerticalAlignment="Center">


                <Label Content="MyName"></Label>
            </StackPanel>
        </Grid>

Dependency property code for visual state change

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 Grid;  
              if (ctrl == null)
                  throw new InvalidOperationException("This attached property only supports types derived from FrameworkElement.");
              var transitionWorked = System.Windows.VisualStateManager.GoToElementState(ctrl, (string)e.NewValue, true);
              //MessageBox.Show(transitionWorked.ToString());
          }));
    }