web-dev-qa-db-ja.com

MVVMマッドネス:コマンド

私はMVVMが好きです。私はそれが好きではありませんが、好きです。そのほとんどは理にかなっています。ただし、XAMLを記述でき、コードビハインドにコードを記述する必要がないように、多くのコードを記述することを推奨する記事を読み続けています。

例を挙げましょう。

最近、ViewModelのコマンドをListViewMouseDoubleClickEventに接続したいと思いました。どうすればいいのかよくわかりませんでした。幸いなことに、Googleにはすべての答えがあります。私は次の記事を見つけました:

解決策はコマンドの理解には役立ちましたが、問題がありました。前述のソリューションのいくつかは、依存関係プロパティの後に「内部」を追加するという一般的なハックのために、WPFデザイナーを使用できなくしました。 WPF設計者はそれを見つけることができませんが、CLRは見つけることができます。一部のソリューションでは、同じコントロールに対して複数のコマンドを使用できませんでした。一部のソリューションでは、パラメーターが許可されていませんでした。

数時間実験した後、私はこれを行うことにしました:

private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
    ListView lv = sender as ListView;
    MyViewModel vm = this.DataContext as MyViewModel;

    vm.DoSomethingCommand.Execute(lv.SelectedItem);
}

それで、MVVM純粋主義者、これの何が問題なのか教えてください。コマンドをユニットテストすることはできます。これは非常に実用的なようですが、「ZOMG ...コードビハインドにコードがあります!!!!」のガイドラインに違反しているようです。あなたの考えを共有してください。

前もって感謝します。

63
JP Richardson

欠点は純度要件にあると思います。 MVVMを含むデザインパターンは、ツールボックス内のツールであり、それ自体が目的ではありません。よく考えられたケースのモデルの純度を破る方が理にかなっている場合(そして明らかにこのケースを検討したように見える場合)、モデルを破ります。

それがあなたにとってうまくいき、それが過度のメンテナンスの負担であるとあなたが信じないのであれば、あなたがしたことには何も悪いことはないと思います。純粋なMVVMの実装が何であるかにかかわらず、これが問題の合理的な解決策であることを示す立証責任を明確に満たしていると思います。

(私はこの議論をマルチパラダイム言語の議論と同様だと考えています。純粋OOアプローチを適用できますが、より機能的な方法で物事を行う方が適切な場合もあります。純粋関数アプローチは適用すると、トレードオフによってOOテクニックはしばらくの間価値がある以上のものであることが示されることがあります。)

37
Greg D

多くのMVVM-Commandソリューションが複雑すぎることに同意します。個人的には、混合アプローチを使用し、ViewModelのメソッドとプロパティを使用して、ViewModelではなくViewでコマンドを定義します。

XAML:

<Window.Resources>
    <RoutedCommand x:Key="LookupAddressCommand" />
</Window.Resources>
<Window.CommandBindings>
    <CommandBinding Command="{StaticResource LookupAddressCommand}" x:Name="cmdLookupAddress" />
</Window.CommandBindings>

コード(表示):

Private Sub cmdLookupAddress_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs) Handles cmdLookupAddress.CanExecute
    e.CanExecute = myViewModel.SomeProperty OrElse (myViewModel.SomeOtherProperty = 2)
End Sub

Private Sub cmdLookupAddress_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs) Handles cmdLookupAddress.Executed
    myViewModel.LookupAddress()
End Sub

純粋なMVVMではありませんが、シンプルで機能し、特別なMVVMコマンドクラスを必要とせず、MVVM以外の専門家(=私の同僚)がコードを読みやすくします。

13
Heinzi

MVVMパターンを使用する場合はコードビハインドを記述したくないのですが、そのコードが純粋にUIに関連している限り、記述しても問題ないと思います。

ただし、ここではそうではありません。コードビハインドからビューモデルコマンドを呼び出しているため、純粋にUIに関連するものではなく、ビューとビューモデルコマンドの関係はXAMLでは直接わかりません。

添付コマンドの動作 を使用すると、XAMLで簡単に実行できると思います。そうすれば、MouseDoubleClickイベントをビューモデルのコマンドに「バインド」できます。

<ListView ItemSource="{Binding Items}">
   <local:CommandBehaviorCollection.Behaviors>
      <local:BehaviorBinding Event="MouseDoubleClick" Action="{Binding DoSomething}" />
   </local:CommandBehaviorCollection.Behaviors>

    ...
</ListView>

ListViewインターフェイスを使用して、直接参照せずにICollectionViewの選択したアイテムに簡単にアクセスすることもできます。

private ICommand _doSomething;

public ICommand DoSomething
{
    get
    {
        if (_doSomething == null)
        {
            _doSomething = new DelegateCommand(
                () =>
                {
                    ICollectionView view = CollectionViewSource.GetDefaultView(Items);
                    object selected = view.CurrentItem;
                    DoSomethingWithItem(selected);
                });
        }
        return _doSomething;
    }
}
10
Thomas Levesque

「コードビハインドにコードがない」という目標はまさにそれであり、到達する目標であり、絶対的な教義としてとらえるべきものではないと私は信じています。ビューにはコードの適切な場所があります。これは、コードが別のアプローチよりも単純な場所や方法の悪い例であるとは限りません。

添付プロパティや添付イベントなど、リストする他のアプローチの利点は、それらが再利用可能であることです。イベントを直接フックして、実行したことを実行すると、アプリケーション全体でそのコードを複製することになるのは非常に簡単です。その配線を処理する単一のアタッチされたプロパティまたはイベントを作成することにより、配管にいくつかのコードを追加しますが、ダブルクリック処理が必要なすべてのListViewで再利用できるコードです。

そうは言っても、私はより「純粋な」アプローチを使用することを好む傾向があります。すべてのイベント処理をビューから除外しても、テストシナリオ(具体的に対処する)には影響しない場合がありますが、全体的な設計性と保守性には影響します。コードビハインドにコードを導入することで、ビューを常にイベントハンドラーが配線されたListViewを使用するように制限します。これにより、ビューがコードに結び付けられ、デザイナーによる再設計の柔軟性が制限されます。

5
Reed Copsey

@JPが元の質問で説明し、@ Heinziが回答で言及しているのは、難しいコマンドを処理するための実用的なアプローチです。コマンドを呼び出す前にUI作業を少し行う必要がある場合は、コードビハインドでイベント処理コードを少し使用すると特に便利です。

OpenFileDialogの典型的なケースを考えてみましょう。 MVVMツールキットで使用される複雑なメッセージングルーチンを採用するよりも、ボタンでクリックイベントを使用し、ダイアログを表示して、結果をViewModelのコマンドに送信する方がはるかに簡単です。

XAMLの場合:

<Button DockPanel.Dock="Left" Click="AttachFilesClicked">Attach files</Button>

背後にあるコード:

    private void AttachFilesClicked(object sender, System.Windows.RoutedEventArgs e)
    {
        // Configure open file dialog box
        Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
        dlg.FileName = "Document"; // Default file name
        dlg.DefaultExt = ".txt"; // Default file extension
        dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

        // Show open file dialog box
        bool? result = dlg.ShowDialog();

        // Process open file dialog box results
        if (result == true)
        {
            string filename = dlg.FileName;

            // Invoke the command.
            MyViewModel myViewModel = (MyViewModel)DataContext;
            if (myViewModel .AttachFilesCommand.CanExecute(filename))
            {
                noteViewModel.AttachFilesCommand.Execute(filename);  
            }
        }
    }

コンピュータプログラミングは柔軟性がありません。私たちプログラマーは、それに対処するために柔軟でなければなりません。

2
dthrasher

デカップリングは、MVVMの主要な機能の1つです。たとえば、ビューまたはバインドされたモデルを変更したい場合。アプリケーションにとってどれほど簡単ですか?

View1とView2の両方が同じViewModelを共有する例を見てください。次に、両方のメソッドの背後にあるコードを実装します。

また、後の段階でビューのビューモデルを変更する必要がある場合、ビューモデルが変更され、ステートメントが変更されると、コマンドが失敗するとします。

MyViewModel vm = this.DataContext as MyViewModel;

nullを返すため、コードがクラッシュします。そのため、コードビハインドを変更するための追加の負担も発生します。このようにすると、このようなシナリオが発生します。

もちろん、プログラミングで同じことを達成する方法はたくさんありますが、どれが最善かが最善のアプローチにつながります。

2
D J

指揮はチャンプ用です。本物の男性は、UI全体をコードビハインドのイベントに接続します。

1
Pierreten