XAML - Comment connaître la fin d’une transition entre deux VisualStates depuis un view model

2 minutes read

Je développe pour WinRT depuis un petit moment en XAML et C#. J’ai récemment fait face à un problème où je devais attendre qu’une animation soit terminée avant de commencer un traitement dans un view model. Le problème était que cette animation est contenue dans une transition entre VisualState dans un de mes Custom Controls.

Mon Custom Control représente un pion de reversi ayant donc deux faces, noire et blanche. Quand sa propriété IsBlack change celà lance une transition entre deux VisualStates. Comme mon application utilise le pattern MVVM j’ai besoin de trouver un moyen de propager l’information qu’une animation a commencée et s’est arretée depuis le custom control au view model.

J’ai tout d’abord ajouté une propriété une propriété a mon contrôle Token :

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));

Ensuite dans la méthode OnApplyTemplate j’appelle la méthode qui est utilisée pour s’abonner aux évènement CurrentStateChanging et CurrentStateChanged du VisualStateGroup contenant les états visuels représentant chaque couleur :

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;
}

Dans les méthodes ColourGroupCurrentStateChang[ing/ed] je n’ai plus qu’à définir la valeur de la propriété IsColourChanging :

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

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

A cette étape, à chaque fois qu’une transition entre deux couleurs est effectuée la propriété IsColourChanging est mise à True.

Donc maintenant comment propager cette information à mon view model ? En utilisant le binding biensûr et voici comment celà peut-être effectué si on part du principe que l’ItemSource de l’ItemsControl est définie à une collection de TokenViewModels :

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

Vous pouvez voir ici que la propriété IsColourChanging du contrôle est bindée à la propriété IsAnimating du view model en mode bi-directionnel. Maintenant c’est au view model de réagir aux changements de la propriété IsAnimating.

Je considère ce trick comme étant important car les applications WinRT se doivent d’être fluides et rapide et donc utilisent beaucoup d’animations. Mais comme fluide et rapide n’est pas synonyme d’inmaintenable c’est un moyen comme un autre de séparer la logique entre la vue et le viewmodel.

Vous pouvez voir un exemple de code en WPF sur mon skydrive.

En espérant que ça aide.

Updated:

Leave a Comment