Привязка состояния представления [VisualStateManager] к viewmodel MVVM?

Как привязать состояние VisualStateManager элемента управления к свойству в viewmodel? Можно ли это сделать?

4 ответов


на самом деле вы можете. Фокус в том, чтобы сделать добавленные собственность и добавить свойство изменен обратный вызов, который фактически вызывает 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 );
    }
  }

затем вы можете установить это свойство в xaml и добавить привязку к viewmodel, как и любой другой:

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

Name и State являются регулярными свойствами в viewmodel. Когда Name устанавливается в viewmodel либо привязкой, либо чем-то еще, он может изменить State ведьма будет обновление визуальное состояние. State также может быть установлен любым другим фактором, и все же он обновит состояние представления в текстовом поле.

поскольку мы используем обычную привязку для привязки к статусу, мы можем применить конвертеры или что-нибудь еще, что мы обычно могли бы сделать, поэтому viewmodel не должен знать, что его фактически установка имени визуального состояния, состояние может быть bool или перечислением или что-то еще.

вы также можете использовать этот подход, используя wpftoolkit на .net 3.5, но вы бросить target до Control вместо FrameworkElement.

еще одно краткое замечание о визуальных состояниях, убедитесь, что вы не называете свои визуальные состояния так, чтобы они конфликтовали со встроенными, если вы не знаете, что делаете. Это особенно верно для проверки, так как механизм проверки будет пытаться установить его состояния каждый раз привязка обновляется!--13--> (и в некоторых других случаях). иди сюда для ссылки на имена визуальных состояний для diffrent контроли.


Я новичок в WPF, но после скручивания состояний через слои MVVM странными способами в течение некоторого времени я наконец нашел решение, которым доволен. Измените состояние как часть логики ViewModel и прослушайте его в представлении XAML. Нет необходимости в преобразователях или коде за" мостовыми " методами или подобными.

Просмотр кода за конструктор

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

пространства имен XAML

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

в 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

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

теперь установка свойства состояния ExampleViewModel вызовет соответствующее изменение состояния в представлении. Убедитесь, что визуальные состояния имеют имена, соответствующие значениям свойств состояния, или усложняют их перечислениями, преобразователями и т. д.


прочтите эту статью:Silverlight 4: Использование VisualStateManager для анимации состояния с MVVM

альтернативно, если вы только после переключения между двумя состояниями вы можете использовать DataStateBehaviour. Я использовал это для переключения фона при отображении страницы входа в систему.

пространства имен

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>

это стало еще проще с помощью рамки, такие как Калибурн.Микро!--4-->.


вот класс, который я использую для поддержки MVVM VisualStateManager состояния в 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);
    }
}

в XAML:

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