Как вы привязываете состояние элемента управления VisualStateManager к свойству viewmodel? Это можно сделать?
Привязка [VisualStateManager] отображает состояние в режиме просмотра MVVM?
Ответ 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}">
...