web-dev-qa-db-ja.com

[VisualStateManager]ビューステートをMVVMビューモデルにバインドしますか?

コントロールのVisualStateManager状態をビューモデルのプロパティにどのようにバインドしますか?それはできますか?

31
aL3891

実際にできます。秘訣は、添付プロパティを作成し、実際に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は他の要素によっても設定できますが、それでもテキストボックスのビューステートが更新されます。

ステータスへのバインドには通常のバインディングを使用しているため、コンバーターなど、通常実行できるものを適用できます。したがって、ビューモデルは、実際に視覚的な状態名であるStateを設定していることを認識する必要はありません。ブール値または列挙型などの可能性があります。

.net 3.5のwpftoolkitを使用してこのアプローチを使用することもできますが、targetではなくControlFrameworkElementをキャストする必要があります。

視覚的状態に関するもう1つの簡単な注意点として、視覚的状態に名前を付けないようにしてください。そうすることで、何をしているのかわからない限り、組み込みの状態と競合するようになります。検証エンジンは、バインディングが更新されるたびに状態(= /// =)を設定しようとするため、これは検証に特に当てはまります(その他の場合も同様)。 ここに移動 異なるコントロールの視覚的な状態名のリファレンス。

30
aL3891

私は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プロパティ値に対応する名前が付いていることを確認するか、列挙型、コンバーターなどで複雑にします。

28
Olav

この記事を読んでください: Silverlight 4:MVVMでの状態アニメーションにVisualStateManagerを使用

または、2つの状態を切り替えた直後の場合は、 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 などのフレームワークを使用することでさらに簡単になります。

11
Town

これは、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}">
    ...
1
Drew Noakes