c# - ViewModel依存関係を持つICommand

原文 c# wpf mvvm icommand

ICommandを使用するときに、アプリケーションでSOLID原則を維持するためのパターンを検索しています。基本的に私の問題は、コマンドの実行にビューモデルとの依存関係があるのに、ビューモデルにコマンドとの依存関係があることです(コンストラクターによってそれらを挿入します)。プロパティのみでビューモデルを保持したいので、これは私の現在の実装の例です:

public class MyViewModel : INotifyPropertyChanged
{
   public ICommand MyCommand { get; private set; }

   public string Message { get; set; } // PropertyChanged ommited

   public MyViewModel()
   {            
   }

   public void SetCommand(ICommand myCommand)
   {
       this.MyCommand = myCommand;
   }

   ....
}

internal interface IMyViewModelCommandManager
{
    void ExectueMyCommand();
}

internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
   private readOnly MyViewModel myViewModel;

   public MyViewModelCommandManager(MyViewModel myViewModel)
   {
       this.myViewModel = myViewModel;
   }

   public ExectueMyCommand()
   {
        MessageBox.Show(this.myViewModel.Message);
   }
}

internal class MyViewModelFactory: IMyViewModelFactory
{
   private readonly IContainerWrapper container;

   public MyViewModelFactory(IContainerWrapper container)
   {
      this.container = container;
   }

   public MyViewModel Create()
   {
       MyViewModel viewModel = new MyViewModel();

       IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(new ResolverOverride[] { new ParameterOverride("viewModel", viewModel) });

       ICommand myCommand = new DelegateCommand(manager.ExecuteMyCommand);

       viewModel.SetCommand(myCommand);

       return viewModel;
   }
}


したがって、SetCommandメソッドの使用を回避します。私は2つの解決策を考えましたが、それらがエレガントであるかどうかはわかりません。

1つ目は、viewmodelの依存関係をコンストラクターから次の方法でコードを更新するメソッドに移動することです。

public class MyViewModel : INotifyPropertyChanged
{
   public ICommand MyCommand { get; private set; }

   public string Message { get; set; } // PropertyChanged ommited

   public MyViewModel(ICommand myCommand)
   {
       this.MyCommand = myCommand;            
   }

   ....
}

internal interface IMyViewModelCommandManager
{
    void ExectueMyCommand(MyViewModel viewModel);
}

internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
   public MyViewModelCommandManager()
   {
       ....
   }

   public ExectueMyCommand(MyViewModel viewModel)
   {
        MessageBox.Show(myViewModel.Message);
   }
}

internal class MyViewModelFactory: IMyViewModelFactory
{
   private readonly IContainerWrapper container;

   public MyViewModelFactory(IContainerWrapper container)
   {
      this.container = container;
   }

   public MyViewModel Create()
   {
       IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(..);

       ICommand myCommand = new DelegateCommand<MyViewModel>(manager.ExecuteMyCommand);

       MyViewModel viewModel = new MyViewModel(myCommand);
       return viewModel;
   }
}


もちろん、xamlコードはCommandParameterを使用します。

<Button Content="Show Message" Command="{Binding MyCommand}" CommandParameter="{Binding .}" />


私が考えた他の解決策は、viewModelのWrapperを作成するトリックを使用することであり、commandManagerはviewModelではなくWrapperに依存します。

internal class MyViewModelCommandContext
   {
      public MyViewModel ViewModel { get; set; }
   }

   public class MyViewModel : INotifyPropertyChanged
    {
       public ICommand MyCommand { get; private set; }

       public string Message { get; set; } // PropertyChanged ommited

       public MyViewModel(ICommand myCommand)
       {
           this.MyCommand = myCommand;            
       }

       ....
    }

    internal interface IMyViewModelCommandManager
    {
        void ExectueMyCommand();
    }

    internal class MyViewModelCommandManager : IMyViewModelCommandManager
    {
       private readonly MyViewModelCommandContext context;

       public MyViewModelCommandManager(MyViewModelCommandContext context)
       {
           this.context = context;
           ....
       }

       public ExectueMyCommand()
       {
            MessageBox.Show(this.context.myViewModel.Message);
       }
    }

    internal class MyViewModelFactory: IMyViewModelFactory
    {
       private readonly IContainerWrapper container;

       public MyViewModelFactory(IContainerWrapper container)
       {
          this.container = container;
       }

       public MyViewModel Create()
       {
           MyViewModelCommandContext context = new MyViewModelCommandContext();

           IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(new ResolverOverride[] { new ParameterOverride("context", context) });

           ICommand myCommand = new DelegateCommand(manager.ExecuteMyCommand);

           MyViewModel viewModel = new MyViewModel(myCommand);
           context.ViewModel = viewModel;
           return viewModel;
       }
    }


私の意見では、最初の問題はこの問題の最善の解決策です。あなたは何が最善の解決策だと思いますか。別のソリューションを適用しますか?
答え
私見両方のソリューションは非常に複雑です。 SOLIDは素晴らしい、KISSはより良い。

あなたのMyViewModelCommandManagerは、後者のMyViewModelを必要とするため、現在直接Messageに結合されています。それで、それらを分離する利点は何ですか? MyViewModel内にコマンドを実装しないのはなぜですか?

MyViewModelへの依存関係の注入が多すぎる場合は、実際にコマンドを実行するために何が必要かを考え、不要なものをすべて抽象化します。


コマンドはメッセージを表示します。
メッセージはMyViewModelによって保留されています
MyViewModelの外でメッセージを表示したい(おそらく他のビューモデルもメッセージを表示する必要があり、コードを再利用したいですか?)
したがって、本当に必要なのは、メッセージを表示したい、またはメッセージが表示されるような何かが発生したという、MyViewModelからの通知です。


可能な解決策:


IMessageDisplayServiceMyViewModelを挿入します。 MyViewModelはそれをメッセージとともに呼び出します。
上記と同様にMyViewModelにコールバックを挿入します。
MyViewModelに、EventArgとしてメッセージを含むイベントを発生させます。


上記のソリューションの推定責任は微妙に異なります。


MyViewModelが担当することを意味します。メッセージを表示したい。
それほど明確ではありません。 MyViewModelはコールバックを呼び出す必要があることを知っていますが、それが何をするのか本当に知りませんし、気にしません。
2に似ていますが、さらに分離されています。複数のものがイベントをサブスクライブまたはサブスクライブ解除できますが、MyViewModelは無知のままです。


これらの3つはすべて、メッセージを表示するものがMyViewModelについて知る必要がないことを意味します。あなたはそれらを切り離しました。必要な配線を行うのはMyViewModelFactoryです。
関連記事

c# - WCF独自のEndpointBehavior読み取りクライアントセッション

c# - Convert.ChangeType()が日付文字列(dd / MM / yyyy形式)を日時タイプに変換しないのはなぜですか?

c# - 辞書からnull可能値を削除し、型を再キャストする

c# - C++からC#への複雑な構造体のマーシャリング

c# - C#:テキストファイルへの変数の挿入

c# - エンティティフレームワークで作成されたデータベースからヘルパーのドロップダウンリストにアイテムをロードする

c# - Web APIのリクエストに一致する複数のアクションが見つかりました

c# - 日付ピッカーのドロップダウンリストから日付を無効にする

c# - TFSがビルドのgit reposを複製しているときのAppDomainUnloadedException

c# - SQL例外をスローするASP.Netフォーム