将 [VisualStateManager] 视图状态绑定到 MVVM 视图模型?

发布于 2024-11-07 17:11:43 字数 53 浏览 0 评论 0原文

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

How do you bind the VisualStateManager state of a control to a property in you viewmodel?
Can it be done?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

空城之時有危險 2024-11-14 17:11:43

其实你可以。
诀窍是创建一个附加属性并添加一个实际调用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>

NameState 是视图模型中的常规属性。当在视图模型中设置Name时,无论是通过绑定还是其他方式,它都可以更改State,从而更新视觉状态。 State 也可以由任何其他因素设置,但它仍然会更新文本框上的视图状态。

由于我们使用普通绑定来绑定到 Status,因此我们可以应用转换器或我们通常能够执行的任何其他操作,因此视图模型不必知道它实际上设置了一个视觉状态名称 State可以是布尔值或枚举或其他任何值。

您还可以在 .net 3.5 上使用 wpftoolkit 使用此方法,但必须将 target 转换为 Control 而不是 FrameworkElement

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

Actually you can.
The trick is to make an Attached property and add a property changed callback that actually calls 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 );
    }
  }

You can then set this property in you xaml and add a binding to your viewmodel like any other:

<Window .. xmlns:local="clr-namespace:mynamespace" ..>
    <TextBox Text="{Binding Path=Name, Mode=TwoWay}"
             local:StateHelper.State="{Binding Path=State, Mode=TwoWay}" />
</Window>

Name and State are regular properties in the viewmodel. When Name is set in the viewmodel, either by the binding or something else, it can change the State witch will update the visual state. State could also be set by any other factor and still it would update the view state on the textbox.

Since we're using a normal binding to bind to Status, we can apply converters or anything else that we'd normally be able to do, so the viewmodel doesn't have to be aware that its actually setting a visual state name, State could be a bool or an enum or whatever.

You can also use this approach using the wpftoolkit on .net 3.5, but you have to cast target to a Control instead of a FrameworkElement.

Another quick note on visual states, make sure you don't name your visual states so that they conflict with the built in ones unless you know what you're doing. This is especially true for validation since the validation engine will try and set its states everytime the binding is updated (and at some other times as well). Go here for a reference on visual state names for diffrent controls.

梦情居士 2024-11-14 17:11:43

我是 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 属性值相对应,或者使用枚举、转换器等使其复杂化。

I'm new to WPF, but after twisting states through MVVM layers in odd ways for some time I finally found a solution I'm happy with. Change the state as part of the ViewModel logic and listen to it in the XAML View. No need for converters or code behind "bridging" methods or the likes.

View Code behind constructor

// Set ViewModel as the views DataContext
public ExampleView(ExampleViewModel vm)
{
  InitializeComponent();
  DataContext = vm;
}

XAML Namespaces

// Reference expression namespaces
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

XAML Bindings

// 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 Code

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

Now setting the State property of ExampleViewModel will trigger a corresponding state change in the view. Make sure the visual states have names corresponding to the State property values or complicate it with enums, converters, etc.

蓝戈者 2024-11-14 17:11:43

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

Have a read of this article: Silverlight 4: using the VisualStateManager for state animations with MVVM

Alternatively, if you're just after switching between two states you can use DataStateBehaviour. I've used this to switch the background when the login page is displayed.

Namespaces

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>

This is made even simpler by using a framework such as Caliburn.Micro.

悲凉≈ 2024-11-14 17:11:43

下面是我在 WPF 中用于 VisualStateManager 状态的 MVVM 支持的类:

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}">
    ...

Here's a class I use for MVVM support of VisualStateManager states in WPF:

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);
    }
}

In your XAML:

<TargetElement utils:MvvmVisualState.CurrentState="{Binding VisualStateName}">
    ...
掌心的温暖 2024-11-14 17:11:43

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

Here's a helper class that works with .NET 4.7.2.

Apparently at some point Microsoft broke support for custom attached properties in static classes. The other answers result in XAML compiler errors about not being able to find stuff in the namespace.

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 );
    }
}

Usage example: local:VisualStateHelper.visualState="{Binding visualState}"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文