views:

280

answers:

2
+1  Q: 

WPF + MvvM + Prism

Hi all, I am new in the Wpf & Mvvm world , but I have found a couple of examples and just found that there is some different way to instantiate the model. I would like to know the best/correct way to do it. both ways are using Unity

What I've foud:

var navigatorView = new MainView();
navigatorView.DataContext = m_Container.Resolve<INavigatorViewModel>();
m_RegionManager.Regions["NavigatorRegion"].Add(navigatorView);

What I did:

var navigatorView = m_Container.Resolve<MainView>;
m_RegionManager.Regions["NavigatorRegion"].Add(navigatorView);

and I changed the constructor to receive viewmodel so I can point the datacontext to it:

public MainView(NavigatorViewModel navigatorViewModel)
{
 this.DataContext = navigatorViewModel;
}  

Other examples I've found another way like:

...vm = new viewmodel 
...m = new model
v.model = vm;

get/set DataContext

cheers

A: 

What you got there makes sense and in both cases is a View-first approach to creating a viewmodel. I.e. the view creates the ViewModel. In the original example the viewmodel is created outside of the view (and is sometimes referred to as marriage pattern), but as far as I am concerned that's the same thing - creation of the view creates the ViewModel.

If this suits your needs stick with it. Another approach you might look into is ViewModel first where the viewmodel takes a dependency on the view like so:

//In the bare-bones(i.e. no WPF dependencies) common interface assembly

interfac IView {
  void ApplyViewModel(object viewmodel);
}    

interface IMainView : IView {
  //this interface can actually be empty.
  //It's only used to map to implementation.
}

//In the ViewModel assembly

class MainViewModel {
  public MainViewModel(IMainView view) {
    view.ApplyViewModel(this);
  }
}

public partial class MainView : UserControl, IMainView {
  void ApplyViewModel(object viewmodel){
    DataContext = viewmodel;
  }
}

Then you can inject this view like so:

IRegion region = regionManager.Regions["MainRegion"];

//This might look strange as we are resolving the class to itself, not an interface to the class
//This is OK, we want to take advantage of the DI container 
//to resolve the viewmodel's dependencies for us,
//not just to resolve an interface to the class.
MainViewModel mainViewModel = container.Resolve<MainViewModel>();

region.Add(mainViewModel.View, "MainView");
region.Activate(ordersView.View);
Igor Zevaka
+1  A: 

I like Igor's suggestion, but without the viewmodel having knowledge of the view. I prefer my dependencies to go one direction (View -> ViewModel -> Model).

What I do is ViewModel-First and just DataTemplate the viewmodel. So I do this:

MainViewModel mainViewModel = container.Resolve<MainViewModel>();

region.Add(mainViewModel, "MainView");
region.Activate(mainViewModel);

With the addition of the ViewModel -> View mapping done with a WPF datatemplate (I don't think this approach is possible with Silverlight, though)

App.xaml:

<Application.Resources>
     <DataTemplate DataType="{x:Type viewModels:MainViewModel}">
          <views:MainView />
     </DataTemplate>
</Application.Resources>

That's it! I love this approach. I like the way it feels like magic. It also has the following advantages:

  • Don't have to modify constructors to suit the mapping
  • Don't have to register type for IMyViewModel in the container... you can work with concrete types. I like to keep my registrations to application services like IViewRegistry or ILogger... those kinds of things
  • You can change the mapping using resources scoped to a particular view that a region is in (this is nice if you want to reuse your ViewModels but want them to look different in different areas of the application
Anderson Imes