Подтвердить что ты не робот

Привязка [VisualStateManager] отображает состояние в режиме просмотра MVVM?

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

4b9b3361

Ответ 1

Собственно, вы можете. Хитрость заключается в создании свойства Attached и добавлении измененного обратного вызова свойства, которое на самом деле вызывает 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 и добавить привязку к своей модели просмотра, как и любое другое:

<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 задается в режиме просмотра, либо привязкой, либо чем-то другим, он может изменить State, ведьма обновит визуальное состояние. State также может быть установлен любым другим фактором, и все же он обновит состояние представления в текстовом поле.

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

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

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

Ответ 2

Я новичок в 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");
  }
}

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

Ответ 3

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

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

Namespaces

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>

Это делается еще проще, используя фреймворк, такой как Caliburn.Micro.

Ответ 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}">
    ...