将视图状态绑定到MVVM视图模型?
如何将控件的VisualStateManager状态绑定到viewmodel中的属性? 可以这样做吗?
其实你可以。 诀窍是创build一个Attached属性并添加一个实际调用GoToState
的属性改变的callback:
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
,无论是通过绑定还是其他的方式,都可以改变State
来更新视觉状态。 State
也可以由任何其他因素设置,并且仍然会更新文本框上的视图状态。
由于我们使用普通绑定来绑定状态,所以我们可以应用转换器或其他我们通常可以做的事情,所以视图模型不必知道它实际上设置了一个可视状态名称状态可以是一个布尔或枚举或任何。
您也可以使用这种方法使用.net 3.5上的wpftoolkit,但是您必须将target
转换为Control
而不是FrameworkElement
。
关于视觉状态的另一个快速注释,确保你不要命名你的视觉状态,以便它们与内置的状态相冲突,除非你知道你在做什么。 validation尤其如此,因为validation引擎会在每次更新绑定 (以及其他时间)时尝试设置其状态。 去这里查看不同控件的可视化状态名称。
我是WPF的新手,但在通过MVVM层以奇怪的方式扭曲状态一段时间后,我终于find了一个我很满意的解决scheme。 将状态更改为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的State属性将在视图中触发相应的状态改变。 确保视觉状态具有与状态属性值相对应的名称,或使用枚举,转换器等将其复杂化。
阅读本文: Silverlight 4:使用VisualStateManager与MVVM进行状态animation
或者,如果您刚刚在两种状态之间切换,则可以使用DataStateBehaviour 。 我已经使用这个来显示login页面时切换背景。
命名空间
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这样的框架,这变得更加简单。
这里是我使用MVVM支持WPF中的VisualStateManager
状态的类:
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}"> ...