将 [VisualStateManager] 视图状态绑定到 MVVM 视图模型?
Posted
技术标签:
【中文标题】将 [VisualStateManager] 视图状态绑定到 MVVM 视图模型?【英文标题】:Binding [VisualStateManager] view state to a MVVM viewmodel? 【发布时间】:2011-08-25 12:20:42 【问题描述】:如何将控件的 VisualStateManager 状态绑定到视图模型中的属性? 能做到吗?
【问题讨论】:
您可以使用 GoToStateAction 来控制状态。然后你只需要将行为附加到按钮或其他东西上。 有意思,没有blend也能用吗? 但是 GoToStateAction 不在 .net 框架中,对吧?它是否可以作为免版税的 dll/源在某个地方使用? 【参考方案1】:其实你可以。
诀窍是制作一个附加属性并添加一个实际调用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,我们可以应用转换器或我们通常能够执行的任何其他操作,因此 viewmodel 不必知道它实际上设置了视觉状态name, State 可以是 bool 或 enum 或其他。
您也可以使用 .net 3.5 上的 wpftoolkit 使用此方法,但您必须将 target
转换为 Control
而不是 FrameworkElement
。
另一个关于视觉状态的快速说明,请确保不要命名视觉状态,以免它们与内置状态冲突,除非您知道自己在做什么。这对于验证尤其如此,因为验证引擎将尝试设置其状态每次绑定更新(以及在其他一些时间)。 Go here 参考不同控件的视觉状态名称。
【讨论】:
有人知道如何在 WinRT 上编译吗?我收到 XamlCompiler 错误 WMC0010: Unknown attachable member 'StateHelper.State' on element 'TextBox' 我发现了。 1:StateHelper 必须继承 DependencyObject。 2:UIPropertyMetadata 在 WinRT 中被命名为 PropertyMetadata。 3:将属性附加到 Page 或 UserControl 的第一个我是 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 属性值对应的名称,或者使用枚举、转换器等使其复杂化。
【讨论】:
请注意,虽然这需要混合 redist 程序集,但不是什么大问题,但需要注意 :) 目前最好的解决方案。【参考方案3】:阅读这篇文章: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 之类的框架,这变得更加简单。
【讨论】:
我现在有,但我希望我几天前有;)非常有趣,我没想过使用命令来听这样的事件 @al3891:我已经用另一种可能的解决方案更新了答案,具体取决于您想要做什么。那篇文章看起来非常好 - 我必须承认我还没有在其中实施解决方案,但它已经在我的列表中一段时间了! nifty :) 我没有混合,这些组件在某处可用吗? @al3891:好问题...我不确定,但this question 的公认答案似乎暗示了这一点。 Microsoft.Expression.Interaction.dll 和 Microsoft.Windows.Interactivity.dll 程序集随 Visual Studio 2015(可能更新)一起提供。【参考方案4】:这是我用于 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">
...
【讨论】:
【参考方案5】:这是一个适用于 .NET 4.7.2 的辅助类。
显然,微软在某些时候打破了对静态类中自定义附加属性的支持。其他答案导致 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"
【讨论】:
以上是关于将 [VisualStateManager] 视图状态绑定到 MVVM 视图模型?的主要内容,如果未能解决你的问题,请参考以下文章
Silverlight & Blend动画设计系列九:动画(Animation)与视图状态管理(Visual State Manager)
Silverlight & Blend动画设计系列九:动画(Animation)与视图状态管理(Visual State Manager)
控件 UI: VisualState, VisualStateManager, 控件的默认 UI