如何将控件的VisualStateManager状态绑定到ViewModel中的属性?这个操作是否可行?
如何将控件的VisualStateManager状态绑定到ViewModel中的属性?这个操作是否可行?
实际上你是可以的。窍门在于创建一个附加属性并添加一个属性变更回调函数,该函数实际上调用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 );
}
}
<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可以是布尔值、枚举或其他任何内容。
您还可以使用wpftoolkit在.net 3.5上使用这种方法,但您必须将target
强制转换为Control
而不是FrameworkElement
。
关于视觉状态的另一个快速注释,请确保不要命名您的视觉状态,以使其与内置状态发生冲突,除非您知道自己在做什么。对于验证尤其如此,因为验证引擎将尝试在每次更新绑定时(以及其他某些时间)设置其状态。有关各种控件的视觉状态名称的参考,请转到此处。
我是WPF的新手,但是经过一段时间的在MVVM层中奇怪的方式扭曲状态后,我终于找到了一个让我满意的解决方案。在ViewModel逻辑中更改状态并在XAML View中监听它。不需要转换器或代码后台"桥接"方法等。
View Code behind constructor
// 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");
}
}
现在设置 ExampleViewModel 的 State 属性将会在视图中触发相应的状态更改。 确保可视状态具有与 State 属性值相对应的名称,否则可能会使用枚举、转换器等使其复杂化。
阅读此文章: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这样的框架,这将变得更加简单。
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);
}
}
<TargetElement utils:MvvmVisualState.CurrentState="{Binding VisualStateName}">
...
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}"