Change VisualState based on property value

2.3k views Asked by At

How to change a VisualState based on a property value on WP7?

Im trying to use MVVM pattern and when my model is loading I want my view to go to especific VisualState.

In Silverlight we have triggers for property changes but in WP7 not!

PS: I dont want to use frameworks I want to understand how it is done in WP7.

2

There are 2 answers

1
Derek Lakin On BEST ANSWER

I use the following attached behavior:

using System;
using System.Windows;
using System.Windows.Controls;

namespace PixelLab.WP7.Common.Behaviors
{
    /// 
    /// Provides an attached behavior for binding visual states.
    /// 
    public static class VisualStates
    {
        /// 
        /// Identifies the CurrentState attached property.
        /// 
        public static readonly DependencyProperty CurrentStateProperty = DependencyProperty
            .RegisterAttached(
                "CurrentState",
                typeof(string),
                typeof(VisualStates),
                new PropertyMetadata(TransitionToState));

        /// 
        /// Gets the current visual state of the specified object. This is an attached property. 
        /// 
        /// The source object.
        /// The current visual state of the specified object.
        public static string GetCurrentState(DependencyObject obj)
        {
            return (string)obj.GetValue(CurrentStateProperty);
        }

        /// 
        /// Sets the current visual state of the specified object. This is an attached property.
        /// 
        /// The target object.
        /// The new visual state.
        public static void SetCurrentState(DependencyObject obj, string value)
        {
            obj.SetValue(CurrentStateProperty, value);
        }

        static void startOnGuiThread( Action act )
        {
            var disp = Deployment.Current.Dispatcher;
            if( disp.CheckAccess() )
                act();
            else
                disp.BeginInvoke( act );
        }

        private static void TransitionToState( object sender, DependencyPropertyChangedEventArgs args )
        {
            FrameworkElement elt = sender as FrameworkElement;
            if( null == elt )
                throw new ArgumentException( "CurrentState is only supported on the FrameworkElement" );

            string newState = args.NewValue.ToString();
            startOnGuiThread( () => ExtendedVisualStateManager.GoToElementState( elt, newState, true ) );
        }
    }
}

In your view model, expose a property for the current visual state, and then on the visual element that you want to handle the visual state for you use the following to bind the visual state, for example

<phone:PhoneApplicationPage ...
    xmlns:common="clr-namespace:PixelLab.Common;assembly=PixelLab.Common"
    common:VisualStates.CurrentState="{Binding CurrentState}">
1
Damian On

Initially the DataStateBehavior behavior looks to be a perfect match, and this article even specifically talks about using it with WP7.

However the Codeplex project the article references no longer has the behavior, and the behavior is not in Expression Blend (at least for WP7 projects).

I'd be inclined to expose a property on the ViewModel and programatically listen for changes in the View and change the Visual State accordingly:

In your View's constructor:

ViewModelLocator.MainViewModelStatic.PropertyChanged += ViewModelPropertyChanged;

Then create an event handler that changes the state accordingly:

private void ViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if(e.PropertyName == MainViewModel.SomeProp)
    {
        // Change the state using the VisualStateManager
    }
}

Damian