33

如何将控件的 VisualStateManager 状态绑定到视图模型中的属性?可以做到吗?

4

5 回答 5

31

其实你可以。诀窍是制作一个附加属性并添加一个实际调用的属性更改回调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 不必知道它实际上设置了一个可视状态名称 State可以是 bool 或 enum 或其他。

您也可以使用 .net 3.5 上的 wpftoolkit 使用这种方法,但您必须转换target为 aControl而不是 a FrameworkElement

关于视觉状态的另一个快速说明,确保你没有命名你的视觉状态,以免它们与内置的冲突,除非你知道你在做什么。对于验证来说尤其如此,因为验证引擎会在每次绑定更新时尝试设置其状态(以及在其他一些时间)。转到此处以获取有关不同控件的视觉状态名称的参考。

于 2011-05-14T13:26:42.863 回答
28

我是 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>

视图模型代码

// Update property as usual
private string _state;
public string State
{
  get { return _state; }
  set
  {
    _state = value;
    NotifyPropertyChanged("State");
  }
}

现在设置 ExampleViewModel 的 State 属性将触发视图中相应的状态更改。确保视觉状态具有与 State 属性值相对应的名称,或者使用枚举、转换器等使其复杂化。

于 2012-08-24T13:31:20.720 回答
12

阅读这篇文章: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之类的框架,这变得更加简单。

于 2011-05-16T11:11:04.033 回答
1

这是我用于 MVVM 支持VisualStateManagerWPF 中状态的类:

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}">
    ...
于 2016-06-06T22:51:58.320 回答
0

这是一个适用于 .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}"

于 2020-09-17T01:58:33.713 回答