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

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


person aL3891    schedule 14.05.2011    source источник
comment
Вы можете использовать GoToStateAction для управления состоянием. Затем вам просто нужно привязать поведение к кнопке или чему-то еще.   -  person Justin XL    schedule 14.05.2011
comment
интересно, вы можете использовать это, даже если у вас нет бленда?   -  person aL3891    schedule 14.05.2011
comment
Но GoToStateAction не входит в структуру .NET, верно? доступна ли она где-нибудь в виде бесплатной библиотеки DLL / исходного кода?   -  person aL3891    schedule 14.05.2011


Ответы (5)


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

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

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

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

person aL3891    schedule 14.05.2011
comment
Кто-нибудь знает, как сделать эту компиляцию на WinRT? Я получаю ошибку XamlCompiler WMC0010: Неизвестный присоединяемый член StateHelper.State в элементе TextBox - person Nilzor; 27.08.2012
comment
Просто выстрел в темноте, но попробуйте установить класс StateHelper как «запечатанный». WinRT во многих случаях требует, чтобы классы были запечатаны. - person aL3891; 28.08.2012
comment
Я выяснил. 1: StateHelper должен наследовать DependencyObject. 2: UIPropertyMetadata называется PropertyMetadata в WinRT. 3. Присоедините свойство к первому элементу ‹Grid› страницы или UserControl, а не к самой странице или UserControl. - person Nilzor; 28.08.2012
comment
@Nilzor - Как вы решили проблему с неизвестным присоединяемым участником? Я последовал вашему совету из вашего последнего сообщения и все еще получаю эту ошибку. - person Brent Traut; 15.11.2012
comment
@ brent-traut - хороший вопрос (не помню). Сравните свой StateHelper.cs со следующим: gist.github.com/4078545. - person Nilzor; 15.11.2012
comment
@Nilzor Спасибо за суть. Я пытаюсь воссоздать все это на C ++ и думаю, что где-то теряюсь в переводе. Насколько я могу судить, компилятор XAML знает о DependancyProperty, потому что вы объявляете его как public static в определении вашего класса. Я не уверен, как сделать то же самое на C ++. Это говорит о том, что моя проблема больше связана с тем, как объявить присоединенное свойство, а не с исходным вопросом здесь. Я создам новый пост. - person Brent Traut; 16.11.2012
comment
Я столкнулся с проблемой, когда метод GoToState сначала дает сбой, потому что группы состояний еще не загружены. Кто-нибудь еще сталкивался с этим? - person Benjamin; 21.07.2015
comment
Microsoft нарушила этот ответ обновлением. Статические классы больше не могут реализовывать присоединенные свойства, XAML не компилируется. - person Soonts; 17.09.2020

Я новичок в 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>

Код модели просмотра

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

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

person Olav    schedule 24.08.2012
comment
Однако обратите внимание, что для этого требуются сборки перераспределения смеси, не так уж и много, но о чем нужно знать :) - person aL3891; 28.08.2012
comment
Прямо сейчас, лучшее решение на данный момент. - person Cabuxa.Mapache; 18.02.2015

Прочтите эту статью: 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>

Это можно сделать еще проще, если использовать такую ​​структуру, как Caliburn.Micro.

person Town    schedule 16.05.2011
comment
у меня есть сейчас, я бы хотел, чтобы у меня было несколько дней назад;) очень интересно, я не думал об использовании команд для прослушивания подобных событий - person aL3891; 16.05.2011
comment
@ al3891: Я обновил ответ другим возможным решением, в зависимости от того, что именно вы хотите сделать. Эта статья выглядит действительно хорошо - я должен признать, что еще не реализовал в ней решение, но оно уже давно в моем списке! - person Town; 16.05.2011
comment
изящно :) у меня нет бленда, эти сборки где-нибудь доступны? - person aL3891; 16.05.2011
comment
@ al3891: Хороший вопрос ... Точно не знаю, но принятый ответ на этот вопрос, безусловно, предполагает его. - person Town; 16.05.2011
comment
Сборки Microsoft.Expression.Interaction.dll и Microsoft.Windows.Interactivity.dll поставляются с Visual Studio 2015 (и, возможно, новее). - person pinki; 13.06.2017

Вот класс, который я использую для поддержки 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}">
    ...
person Drew Noakes    schedule 06.06.2016

Вот вспомогательный класс, который работает с .NET 4.7.2.

По-видимому, в какой-то момент Microsoft прекратила поддержку настраиваемых вложенных свойств в статических классах. Другие ответы приводят к ошибкам компилятора XAML о невозможности найти что-то в пространстве имен.

public sealed class VisualStateHelper: DependencyObject
{
    public static readonly DependencyProperty visualStateProperty = DependencyProperty.RegisterAttached
    (
        "visualState",
        typeof( object ),
        typeof( VisualStateHelper ),
        new UIPropertyMetadata( null, onStateChanged )
    );

    static void onStateChanged( DependencyObject target, DependencyPropertyChangedEventArgs args )
    {
        if( args.NewValue == null )
            return;
        if( target is FrameworkElement fwe )
            VisualStateManager.GoToElementState( fwe, args.NewValue.ToString(), true );
    }

    public static void SetvisualState( DependencyObject obj, string value )
    {
        obj.SetValue( visualStateProperty, value );
    }

    public static string GetvisualState( DependencyObject obj )
    {
        return (string)obj.GetValue( visualStateProperty );
    }
}

Пример использования: local:VisualStateHelper.visualState="{Binding visualState}"

person Soonts    schedule 17.09.2020