dex3703
dex3703

Reputation: 2107

getting the current VisualState in WinRT

I'm trying to get an object's current VisualState. I found an answer on SO that this isn't possible, and another blog elsewhere that I should be able to get these like this:

var currentState = VisualStateManager.GetVisualStateGroups(ContainerGrid);

However curentState doesn't seem to get anything. What am I doing wrong?

Upvotes: 2

Views: 3096

Answers (3)

nudaca
nudaca

Reputation: 51

In UWP XAML just name de VisualStateGroup:

<VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="PopupStates">
                <VisualState x:Name="Mobile">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0"/>
                    </VisualState.StateTriggers>

and you get the name using:

PopupStates.CurrentState.Name

Upvotes: 1

dex3703
dex3703

Reputation: 2107

I found this answer worked better for me, as I didn't need anything external: Silverlight: VisualStateManager.GetVisualStateGroups doesn't, How can I get them?

Upvotes: 2

Filip Skakun
Filip Skakun

Reputation: 31724

The WinRT XAML Toolkit has this extension method - AwaitableUI.ControlExtensions.GoToVisualStateAsync() that finds the storyboard for visual state transition using the method you mentioned (VisualStateManager.GetVisualStateGroups()) and awaits its completion after calling the regular VisualStateManager.GoToState() method. It has worked fine for me so far. The only thing of note is that you need to call GetVisualStateGroups() on the element that specifies the visual state groups, so in most cases you might need to dig into the control template's visual tree since that is where they are usually defined - somewhere deeper in the template than the actual logical control.

Here is my full sample class:

using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;

namespace WinRTXamlToolkit.AwaitableUI
{
    /// <summary>
    /// Extension methods for Control class.
    /// </summary>
    public static class ControlExtensions
    {
        #region GoToVisualStateAsync()
        /// <summary>
        /// Goes to specified visual state, waiting for the transition to complete.
        /// </summary>
        /// <param name="control">
        /// Control to transition.
        /// </param>
        /// <param name="visualStatesHost">
        /// FrameworkElement that defines the visual states
        /// (usually the root of the control's template).
        /// </param>
        /// <param name="stateGroupName">
        /// Name of the state group
        /// (speeds up the search for the state transition storyboard).
        /// </param>
        /// <param name="stateName">
        /// State to transition to.
        /// </param>
        /// <returns>
        /// Awaitable task that completes when the transition storyboard completes.
        /// </returns>
        /// <remarks>
        /// If a state transition storyboard is not found - the task
        /// completes immediately.
        /// </remarks>
        public static async Task GoToVisualStateAsync(
            this Control control,
            FrameworkElement visualStatesHost,
            string stateGroupName,
            string stateName)
        {
            var tcs = new TaskCompletionSource<Storyboard>();

            Storyboard storyboard =
                GetStoryboardForVisualState(visualStatesHost, stateGroupName, stateName);

            if (storyboard != null)
            {
                EventHandler<object> eh = null;

                eh = (s, e) =>
                {
                    storyboard.Completed -= eh;
                    tcs.SetResult(storyboard);
                };

                storyboard.Completed += eh;
            }

            VisualStateManager.GoToState(control, stateName, true);

            if (storyboard == null)
            {
                return;
            }

            await tcs.Task;
        } 
        #endregion

        #region GetStoryboardForVisualState()
        /// <summary>
        /// Gets the state transition storyboard for the specified state.
        /// </summary>
        /// <param name="visualStatesHost">
        /// FrameworkElement that defines the visual states
        /// (usually the root of the control's template).
        /// </param>
        /// <param name="stateGroupName">
        /// Name of the state group
        /// (speeds up the search for the state transition storyboard).
        /// </param>
        /// <param name="stateName">
        /// State to transition to.
        /// </param>
        /// <returns>The state transition storyboard.</returns>
        private static Storyboard GetStoryboardForVisualState(
            FrameworkElement visualStatesHost,
            string stateGroupName,
            string stateName)
        {
            Storyboard storyboard = null;

            var stateGroups = VisualStateManager.GetVisualStateGroups(visualStatesHost);
            VisualStateGroup stateGroup = null;

            if (!string.IsNullOrEmpty(stateGroupName))
            {
                stateGroup = stateGroups.FirstOrDefault(g => g.Name == stateGroupName);
            }

            VisualState state = null;

            if (stateGroup != null)
            {
                state = stateGroup.States.FirstOrDefault(s => s.Name == stateName);
            }

            if (state == null)
            {
                foreach (var group in stateGroups)
                {
                    state = group.States.FirstOrDefault(s => s.Name == stateName);

                    if (state != null)
                    {
                        break;
                    }
                }
            }

            if (state != null)
            {
                storyboard = state.Storyboard;
            }

            return storyboard;
        } 
        #endregion
    }
}

This is how I call it:

await this.GoToVisualStateAsync(_layoutRoot, PopupStatesGroupName, OpenPopupStateName);

Where this is my InputDialog control and _layoutRoot is a part of its template defined like this:

<Setter
    Property="Template">
    <Setter.Value>
        <ControlTemplate
            TargetType="controls:InputDialog">
            <Grid
                x:Name="LayoutRoot"
                Background="{TemplateBinding BackgroundScreenBrush}">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup
                        x:Name="PopupStates">
                        <VisualState
                            x:Name="OpenPopupState">
                            <Storyboard>
...

Once you extract that visual state group you can get the last set state by getting stateGroup.CurrentState.

Upvotes: 1

Related Questions