2016-05-08 7 views
1

で完全なパターンメディエーターは、私はMVVMではMVVM

をMediatorパターンに問題を抱えていることはできませんI'lは私の問題をよりよく理解するために、ほぼすべてのクラスを記述します。 I'vはそれのためにメインウィンドウとビューモデルを得た

  1. 、それは非常に簡単で、auctually私のUserControlの1を保持しているが、何もしない、メインウィンドウにContentControl.ContentにバインドされViewModelにでUserControlプロパティがあります。

  2. UserControlsは同じです。それぞれのボタンには のボタンが1つしかありません。また、clikcsを処理するコマンドを持つ2つのViewModelがあります。

  3. クラスメディエータはsingletoneであると私は私のViewModel間iteractionのためにそれを使用しようとした

だから、私は何をしようとしていることは内部で彼らと彼らのViewModelを作成していない、ユーザーコントロールを切り替えることですMainWindowViewModel。私はボタンをクリックした後に切り替える必要があります。たとえば、FirstUserControlのボタンをクリックすると、MainWindowのContentControlがSecondUserControlに切り替わります。

probleam iはユ​​ーザーコントロールがメディエータNotifyCollegue()関数のパラメータとしてオブジェクト、私は(もちろん、それはMVVMの原理の一つである)それら へのアクセスもを持っていない、それが通るべきUserControlsViewModelsに表示され(例えば、intやstringを渡すなど)問題ではない標準的な型のため、ユーザ型の問題。

私はここ http://www.codeproject.com/Articles/35277/MVVM-Mediator-Pattern

このsolutinを発見したと私はメインウィンドウがContentControlににバインドさ現在のユーザーコントロールを除くすべての明らかになりたいので、なぜ私は、MainWindowViewModelでのUserControlを急がすることはできません。

私は別のsingletoneクラスを作成し、そこにあるすべてのuserControls参照を集めてUserControlsViewModelsの中で使用するか、それとも何か他のものを使うべきでしょうか?

私は自分の問題を明確に記述し、何らかの解決策があることを願っています。

私はどんな質問にも答えてくれることを嬉しく思っており、助けに非常に感謝しています!

ああ、それは本当のアプリではない、私はちょうど

...作成ビューおよびその他のviewmodelsの内側に自分のviewmodelsのViewModelを混合していないではない、のviewmodels間のシステムをmesagingの考え方(概念)を取得したいです

もう一度ありがとう!

MAINVIEW

<Window x:Class="TESTPROJECT.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local="clr-namespace:TESTPROJECT" 
    mc:Ignorable="d" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    Title="MainWindow" Height="500" Width="750"> 



<Grid> 
    <ContentControl Grid.Row="1" Content="{Binding PagesControl}"/> 
</Grid> 

MAINVIEWビューモデル

namespace TESTPROJECT 
{ 
    class MainWindowViewModel : ViewModelBase 
    { 
     private UserControl _pagesControl; 
     public UserControl PagesControl 
     { 
      //Property that switches UserControls 
      set 
      { 
       _pagesControl = value; 
       OnPropertyChanged(); 
      } 
      get 
      { 
       return _pagesControl; 
      } 
     } 

     public MainWindowViewModel() 
     { 
      //Method that will be listening all the changes from UserControls ViewModels 
      Mediator.Instance.Register(
       (object obj) => 
       { 
        PagesControl = obj as UserControl; 
       }, ViewModelMessages.UserWroteSomething); 
     } 
    } 
} 

FirstUserControl

<UserControl x:Class="TESTPROJECT.FirstUserControl" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:TESTPROJECT" 
     xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
<Grid> 
    <Button Command="{Binding GetCommand}"> 
     hello, i'm first user control! 
    </Button> 
</Grid> 

FirstUserControlビューモデル

namespace TESTPROJECT 
{ 
class FirstUserControlViewModel : ViewModelBase 
{ 
    //command that is binded to button 
    private DelegateCommand getCommand; 
    public ICommand GetCommand 
    { 
     get 
     { 
      if (getCommand == null) 
       getCommand = new DelegateCommand(param => this.func(param), null); 
      return getCommand; 
     } 
    } 
    //method that will handle button click, and in it i'm sending a message 
    //to MainWindowViewModel throug Mediator class 
    //and that is allso a problem place because in theory i should 
    //pass the opposite UserControl object , but from here i have no 
    //acces to it 
    private void func(object obj) 
    { 
     Mediator.Instance.NotifyColleagues(
      ViewModelMessages.UserWroteSomething, 
      "PROBLEM PLACE"); 
    } 
} 

}

SecondUserControl

<UserControl x:Class="TESTPROJECT.SecondUserControl" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:TESTPROJECT" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
<Grid> 
    <Button Command="{Binding GetCommand}"> 
     Hello, i'm second user control! 
    </Button> 
</Grid> 

SecondUserControlビューモデル

namespace TESTPROJECT 
{ 
class SecondUserControlViewModel : ViewModelBase 
{ 
    //command that is binded to button 
    private DelegateCommand getCommand; 
    public ICommand GetCommand 
    { 
     get 
     { 
      if (getCommand == null) 
       getCommand = new DelegateCommand(param => this.func(param), null); 
      return getCommand; 
     } 
    } 
    //method that will handle button click, and in it i'm sending a message 
    //to MainWindowViewModel throug Mediator class 
    //and that is allso a problem place because in theory i should 
    //pass the opposite UserControl object , but from here i have no 
    //acces to it 
    private void func(object obj) 
    { 
     Mediator.Instance.NotifyColleagues(
      ViewModelMessages.UserWroteSomething, 
      "PROBLEM PLACE"); 
    } 
} 

}

クラスメディエータ と 列挙ViewModelMessages

namespace TESTPROJECT 
{ 
//this enum holding some kind of event names fro example UserWroteSomething 
// is a name of switching one UserControl to another 
public enum ViewModelMessages { UserWroteSomething = 1 }; 

class Mediator 
{ 
    //Singletone part 
    private static Mediator instance; 
    public static Mediator Instance 
    { 
     get 
     { 
      if (instance == null) 
       instance = new Mediator(); 
      return instance; 
     } 
    } 
    private Mediator() { } 
    //Singletone part 

    //collection listeners that holds event names and handler functions 
    List<KeyValuePair<ViewModelMessages, Action<Object>>> internalList = 
     new List<KeyValuePair<ViewModelMessages, Action<Object>>>(); 


    //new listener registration 
    public void Register(Action<object> callBack, ViewModelMessages message) 
    { 
     internalList.Add(
      new KeyValuePair<ViewModelMessages, Action<Object>>(message, callBack)); 
    } 

    // notifying all the listener about some changes 
    // and those whose names fits will react 
    public void NotifyColleagues(ViewModelMessages message, object args) 
    { 
     foreach(KeyValuePair<ViewModelMessages, Action<Object>> KwP in internalList) 
      if(KwP.Key == message) 
       KwP.Value(args); 
    } 
} 

}

アプリケーションの出発点は、

public partial class App : Application 
{ 
    private void Application_Startup(object sender, StartupEventArgs e) 
    { 
     FirstUserControl first = new FirstUserControl() { DataContext = new FirstUserControlViewModel() }; 
     SecondUserControl second = new SecondUserControl() { DataContext = new SecondUserControlViewModel() }; 

     new MainWindow() 
     { 
      DataContext = new MainWindowViewModel() { PagesControl = first } 
     }.ShowDialog(); 
    } 
} 

答えて

2

私が正しくあなたを理解していれば、あなたは別のビューに移動します(または現在アクティブなビューモデル上の特定のアクションが発生したとき(例:それぞれビューモデル)あなたはボタンを押す)。あなたはこのためにあなたのメディエータを使用したい場合、あなたはこのようにそれを構造化することができ

public class Mediator 
{ 
    // These fields should be set via Constructor Injection 
    private readonly MainWindowViewModel mainWindowViewModel; 
    private readonly Dictionary<ViewModelId, IViewFactory> viewFactories;   

    public void NotifyColleagues(ViewModelId targetViewModelId, ViewModelArguments arguments) 
    { 
     var targetFactory = this.viewModelFactories[targetViewModelId]; 
     var view = targetFactory.Create(viewModelArguments); 
     this.mainWindowViewModel.PagesControl = view; 
    } 

    // other members omitted to keep the example small 
} 

あなたはその後、すべてのビューのために工場を作成します - ビューモデルの組み合わせを。 ViewModelArgumentsでは、他のビューモデルから作成された新しく作成されたビューモデルに情報を渡すことができます。 ViewModelIdはViewModelMessageのような単純な列挙型にすることもできますが、ビューモデルのTypeを使用することもできます(私が追求することをお勧めします)。

さらに、Mediatorクラスでプライベートコンストラクタを使用しないことをお勧めします。そうしないと、mainWindowViewModelとビューファクトリのディクショナリを渡すことができないためです。 Application-Startupメソッドでこれを設定できるはずです。

また、MVVMアプリケーションを構造化する他の多くの方法があります。ビューモデルのビューをインスタンス化するためにデータテンプレートを使用していますが、あなたの小さな例ではあまりにも伸びていると思います。

+0

申し訳ありませんが、私はプログラミングとwpf、mvvmで新しいです。私はちょうど "どのビュー - ビューモデルの組み合わせのための工場"なのか分かりません、私に説明してください、リンクを教えてください?また、あなたの例からviewModelFactoriesがどこに来たのか、MainWindowViewModelをMediatorクラスの中に入れたのはなぜですか? – kotki

+0

さて、オブジェクト指向プログラミングでは、通常、SOLIDの原則と呼ばれる5つの原則に従います。これらは、相互作用して形成する比較的小さな機能(さまざまな問題を解決するのに適した)一貫性のある作業全体。そして、これは "Composition Root"というコンストラクトにつながり、必要なすべてのオブジェクトをインスタンス化し、それらをまとめます(LEGOのように考える)。私のYouTubeチャンネル用に作ったビデオシリーズをチェックしてください:https://www.youtube.com/watch?v=cF7WRPKm8-A&index=1&list=PLIMrZfX3DMVFM_8EgFXmfVhOUNfMvXLLt – feO2x

+0

これは基本的にメディエーターがmainWindowViewModelへの参照を持っている理由です現在アクティブなビューを保持して表示する場合、前者はビューをインスタンス化してモデルを表示し、他のビューモデルから引数を渡すことができます。これらのオブジェクトは、一連のファクトリとともに、「別のビューにナビゲートする」という問題を解決します。 – feO2x