views:

467

answers:

2

Hi,

I am trying to use the Silverlight DataStateBehavior, it works fine in most cases where I click a button which sets say a 'Selected' Property in the view model to either false or true. The DataStateBehavior then tells the VisualStateManager to go to the relevant state.

Like this:

   <Button...>
   <i:Interaction.Behaviors>
             <id:DataStateBehavior Binding="{Binding Selected}" Value="True" TrueState="SelectedVisualState" FalseState="DeselectedVisualState"/>
          </i:Interaction.Behaviors>
   </Button>

The above works fine. What I am trying to do though is to get it to set the correct state when the application loads, if I were to set the 'Selected' property on the view model to true by default I wouldn't see any changes in the UI until I clicked the button to change the viewmodel property.

I know there are several classes involved with the DataState stuff including:

  • BindingListener.cs
  • ConverterHelper.cs
  • DataStateBehavior.cs
  • DataStateSwitchBehavior.cs
  • DataTrigger.cs

Any clues would be good, Thanks

A: 

One way I have solved this problem is to make a behavior you can add to your control to put it into an initial visual state upon loading. Here is a simple example:

public class InitialVisualStateBehavior : Behavior<Control>
{
    public static readonly DependencyProperty InitialStateProperty = DependencyProperty.Register(
        "InitialState",
        typeof(string),
        typeof(InitialVisualStateBehavior),
        null);

    public string InitialState
    {
        get { return (string)GetValue(InitialStateProperty); }
        set { SetValue(InitialStateProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
        }
    }

    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        VisualStateManager.GoToState(this.AssociatedObject, this.InitialState, false);
    }
}

You would then just add that behavior to the UserControl-level in XAML:

<i:Interaction.Behaviors>
    <myi:InitialVisualStateBehavior InitialState="SelectedVisualState" />
</i:Interaction.Behaviors>

You could also easily modify this to accept a comma separated list of initial states which you could then split and loop through if you needed to put the control in a bunch of different mutually exclusive states after loading.

This might also be refactored into a TriggerAction that you could just trigger off of the Loaded event of the control, I'm not sure which way would be cleaner.

Dan Auclair
A: 

I'm actually going to add a second answer which I just tried, and seems to be cleaner since it can be done all in XAML and without a custom behavior. I'll leave the other answer just as a reference for an alternative solution since they both work.

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <ic:GoToStateAction StateName="SelectedVisualState"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

You will just need at add a reference to the Microsoft.Expression.Interactions assembly that is part of the Blend SDK.

xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
Dan Auclair
Thanks for the reply, I forgot to mention this would be inside a datatemplate and so the state chosen is dependent on an ID value of the data item for example. If the ID is 0 use state "Open", if the ID is 1 use state "Closed" etc. The above is still really useful, thanks again
JimmySavile