XAML - How to know when a VisualState Transition is completed in a CustomControl

2 minutes read

I’ve been developing for WinRT for quiet some time now in XAML and C#. I’ve faced an issue where I needed to wait that an animation was completed before starting a piece of work in a view model. The problem was that this animation was a VisualState transition inside one of my custom controls.

My custom control represents a reversi token which has two sides, black and white. When its IsBlack property changes it triggers a visual state transition. As my application uses the MVVM pattern I needed to find a way to propagate the information that an animation has started and stopped from the custom control to the view model.

I first created added a property to my Token control :

public bool IsColourChanging
{
    get { return (bool) GetValue(IsColourChangingProperty); }
    set { SetValue(IsColourChangingProperty, value); }
}

public static readonly DependencyProperty IsColourChangingProperty =
    DependencyProperty.Register("IsColourChanging", typeof (bool), typeof(Token), new PropertyMetadata(false));

Then in the OnApplyTemplate method of the control I call the following method which is used to subscribed to the CurrentStateChanging and CurrentStateChanged events of the VisualStateGroup containing the states representing each colour :

private void HandleColourChanging()
{
    var child = VisualTreeHelper.GetChild(this, 0) as FrameworkElement;

    Debug.Assert(child != null, "Token's template must be defined !");

    var groups = VisualStateManager.GetVisualStateGroups(child);

    Debug.Assert(groups != null, "Token's VisualStateGroups should be defined!");

    // The Cast operator is not necessary in Windows 8 Metro Style Apps
    var colourGroup = groups.Cast<VisualStateGroup>().FirstOrDefault(g => g.Name == ColourStatesGroupName);

    Debug.Assert(colourGroup != null, "Token's VisualState Transition should be defined!");

    colourGroup.CurrentStateChanging += ColourGroupCurrentStateChanging;
    colourGroup.CurrentStateChanged += ColourGroupCurrentStateChanged;
}

In the ColourGroupCurrentStateChang[ing/ed] methods I now just have to set the IsColourChanging property accordingly :

private void ColourGroupCurrentStateChanging(object sender, VisualStateChangedEventArgs e)
{
    IsColourChanging = true;
}

private void ColourGroupCurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
    IsColourChanging = false;
}

At this step, every time a transition between two colours is run, the IsColourChanging is set to true until the transition complete.

So now how to propagate this information to my view models ? Using bindings of course and here’s how it can be done assuming the ItemsControl ItemsSource property is set to a collection of TokenViewModels :

<ItemsControl>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:Token IsBlack="{Binding IsBlack}"
                         IsColourChanging="{Binding IsAnimating, Mode=TwoWay}"
                         Width="100"
                         Height="100" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

You can see here that the IsColourChanging property of the control is bound to the IsAnimating control of its view model in a two way manner. Now it’s up to to viewmodel to react to the change of the IsAnimating property as wanted.

I consider this trick to be important as WinRT application are made to be fast and fluid and so using a lot a animations. As fast and fluid does not mean unmaintainable it’s a way among others to separate your logic between the view and the view model.

You can see a sample code in WPF in my skydrive as usual.

Hope it’ll help.

Updated:

Leave a Comment