如何将控件的VisualStateManager状态绑定到viewmodel中的属性? 可以吗?
答案 0 :(得分:29)
其实你可以。
诀窍是创建一个附加属性并添加一个实际调用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中设置此属性,并像其他任何方式一样为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
也可以由任何其他因素设置,但仍会更新文本框中的视图状态。
由于我们使用普通绑定绑定到Status,我们可以应用转换器或我们通常能够做的任何其他事情,因此viewmodel不必知道它实际上设置了一个视觉状态姓名,州可以是布尔或者枚举或其他。
您也可以在.net 3.5上使用wpftoolkit使用此方法,但您必须将target
投射到Control
而不是FrameworkElement
。
关于视觉状态的另一个快速说明,确保你没有命名你的视觉状态,以便它们与内置的状态冲突,除非你知道你正在做什么。对于验证尤其如此,因为验证引擎将尝试在每次更新绑定时设置其状态(以及其他一些时间)。 Go here提供有关不同控件的可视状态名称的参考。
答案 1 :(得分:27)
我是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");
}
}
现在设置ExampleViewModel的State属性将在视图中触发相应的状态更改。确保可视状态具有与State属性值对应的名称,或者使用枚举,转换器等使其复杂化。
答案 2 :(得分:10)
阅读本文:Silverlight 4: using the VisualStateManager for state animations with 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等框架可以使这更简单。
答案 3 :(得分:0)
这是我在WPF中用于MVVM支持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}">
...
答案 4 :(得分:0)
这是与.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}"