web-dev-qa-db-fra.com

Lier l’état de la vue [VisualStateManager] à un modèle de vue MVVM?

Comment liez-vous l'état VisualStateManager d'un contrôle à une propriété dans votre modèle de vue? Cela peut-il être fait?

30
aL3891

En fait, vous pouvez. L'astuce consiste à créer un Propriété attachée et à ajouter une propriété modifiée rappel qui appelle réellement GoToState:

public class StateHelper {
    public static readonly DependencyProperty StateProperty = DependencyProperty.RegisterAttached( 
        "State", 
        typeof( String ), 
        typeof( StateHelper ),
        new UIPropertyMetadata( null, StateChanged ) );

      internal static void StateChanged( DependencyObject target, DependencyPropertyChangedEventArgs args ) {
      if( args.NewValue != null )
        VisualStateManager.GoToState( ( FrameworkElement )target, args.NewValue, true );
    }
  }

Vous pouvez ensuite définir cette propriété dans vous xaml et ajouter une liaison à votre viewmodel comme n'importe quel autre:

<Window .. xmlns:local="clr-namespace:mynamespace" ..>
    <TextBox Text="{Binding Path=Name, Mode=TwoWay}"
             local:StateHelper.State="{Binding Path=State, Mode=TwoWay}" />
</Window>

Name et State sont des propriétés standard dans le modèle de vue. Lorsque Name est défini dans le modèle de vue, que ce soit par la liaison ou autre chose, cela peut changer la State qui mettra à jour l'état visuel. State pourrait également être défini par tout autre facteur, mais il mettrait néanmoins à jour l'état d'affichage dans la zone de texte.

Puisque nous utilisons une liaison normale pour lier à Status, nous pouvons appliquer des convertisseurs ou tout autre chose que nous pourrions normalement faire. Ainsi, le modèle de vue n'a pas besoin de savoir qu'il définit en fait un nom d'état visuel, State. pourrait être un bool ou une enum ou autre.

Vous pouvez également utiliser cette approche à l'aide de wpftoolkit sur .net 3.5, mais vous devez transtyper target en Control au lieu de FrameworkElement.

Une autre remarque rapide sur les états visuels, assurez-vous de ne pas nommer vos états visuels de manière à ce qu’ils entrent en conflit avec les états intégrés, à moins que vous ne sachiez ce que vous faites. Ceci est particulièrement vrai pour la validation car le moteur de validation essaiera de définir ses états chaque fois que la liaison est mise à jour (et à d'autres moments également). Allez ici pour une référence sur les noms d'état visuels pour différents contrôles.

29
aL3891

Je suis nouveau dans WPF, mais après avoir modifié les états des couches de MVVM de manière étrange pendant un certain temps, j'ai finalement trouvé une solution qui me satisfait. Modifiez l'état dans le cadre de la logique ViewModel et écoutez-le dans la vue XAML. Pas besoin de convertisseurs ou de code derrière les méthodes de "pontage" ou autres.

Voir le code derrière le constructeur

// Set ViewModel as the views DataContext
public ExampleView(ExampleViewModel vm)
{
  InitializeComponent();
  DataContext = vm;
}

Espaces de noms XAML

// Reference expression namespaces
xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.Microsoft.com/expression/2010/interactions"

Liaisons XAML

// Bind GoToStateAction directly to a ViewModel property
<i:Interaction.Triggers>
  <ei:DataTrigger Binding="{Binding State}" Value="{Binding State}">
    <ei:GoToStateAction StateName="{Binding State}" />
  </ei:DataTrigger>
</i:Interaction.Triggers>

ViewModel Code

// Update property as usual
private string _state;
public string State
{
  get { return _state; }
  set
  {
    _state = value;
    NotifyPropertyChanged("State");
  }
}

Définir maintenant la propriété State de ExampleViewModel déclenchera un changement d'état correspondant dans la vue. Assurez-vous que les états visuels ont des noms correspondant aux valeurs de la propriété State ou compliquez-les avec des énumérations, des convertisseurs, etc.

27
Olav

Lisez cet article: Silverlight 4: utilisation de VisualStateManager pour les animations d’état avec MVVM

Si vous venez juste de basculer entre deux états, vous pouvez également utiliser DataStateBehaviour . J'ai utilisé cela pour changer le fond lorsque la page de connexion est affichée.

Espaces de noms

xmlns:ei="http://schemas.Microsoft.com/expression/2010/interactions" 
xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity" 

XAML

<i:Interaction.Behaviors>
   <ei:DataStateBehavior TrueState="LoginPage" FalseState="DefaultPage" 
                         Binding="{Binding IsLoginPage}" Value="true" />
</i:Interaction.Behaviors>

Ceci est rendu encore plus simple en utilisant un framework tel que Caliburn.Micro .

10
Town

Voici une classe que j'utilise pour la prise en charge par MVVM des états VisualStateManager dans WPF:

public static class MvvmVisualState
{
    public static readonly DependencyProperty CurrentStateProperty
        = DependencyProperty.RegisterAttached(
            "CurrentState",
            typeof(string),
            typeof(MvvmVisualState),
            new PropertyMetadata(OnCurrentStateChanged));

    public static string GetCurrentState(DependencyObject obj)
    {
        return (string)obj.GetValue(CurrentStateProperty);
    }

    public static void SetCurrentState(DependencyObject obj, string value)
    {
        obj.SetValue(CurrentStateProperty, value);
    }

    private static void OnCurrentStateChanged(object sender, DependencyPropertyChangedEventArgs args)
    {
        var e = sender as FrameworkElement;

        if (e == null)
            throw new Exception($"CurrentState is only supported on {nameof(FrameworkElement)}.");

        VisualStateManager.GoToElementState(e, (string)args.NewValue, useTransitions: true);
    }
}

Dans votre XAML:

<TargetElement utils:MvvmVisualState.CurrentState="{Binding VisualStateName}">
    ...
0
Drew Noakes