What should compose View with ViewModel and show result? - wpf

I have problem of thinking ideal solution for creating and showing window in WPF MVVM application. Some part of application needs to show some window with some data. I create VM, set its properties, create View, assign its VM (in constructor), then display window. This is done using class that I named ViewController and this class have methods with parameters for every window in my application. I think there can be better solution than this, but not overengineered.

The normal solution is you have a class that wraps and instantiates a View ViewModel pair. This is often called screen. it would look something like this.
public class Screen<TView> where TView : Window
{
public Screen(TView view, object viewModel){
//store view and viewModel props
//display view
//set viewModel as DataContext of view
}
}
This is a very rough example, there are lots of ways you can do it.

In the last I created implementation of IWindowManager, which have methods for showing required windows and these methods have parameters if needed. Methods create view model, set its properties and inject it to window. Only drawback of this solution is when new window is needed, new method must be added to interface and implementation of WindowManager.

Related

Main Window starts to depend on too many things, but I don't want a service locator

I am creating a WPF application. Naturally my entry point is MainWindow.xaml, which is opened up by App.xaml
var mainWindow = container.Resolve<MainWindow>();
Application.Current.MainWindow = mainWindow;
Application.Current.MainWindow.Show();
I am using Dependency Injection and so far all the dependencies are passed as parameters in the ctor of the MainWindow's View Model.
i.e. my Main Window is
public partial class MainWindow : MetroWindow
{
private readonly MainWindowModel mainViewModel;
public MainWindow(MainWindowModel mainViewModel)
{
and its View model is:
public MainWindowModel(IDataRepository dataRepo, ICommand command1, ICommand command2, etc ...)
{
However, I am now starting to realize this might be a problem. Given that the MainWindow is the entry point to the entire app, it seems like any dependency, anywhere in the application will have to first pass through the MainWindow View Model constructor. This seems crazy.
I am coming from the background of ASP.NET MVC and there we have Controllers, which receive only the dependencies that they need. i.e. the concept of a main entry point there is missing and this makes things easier and more manageable.
Here is an example in my WPF app. A control, on the Main View needs to open up a dialog. This dialog is another Window and of course that window receives its ViewModel in its ctor. To me, it seems like to be able to resolve the dialog properly, I need to pass it through the Main Window View Model ctor first, keep it as private readonly field of the Main Window View Model and launch it when necessary. Ok, but what if I have 100 dialogs. That's just one of the examples. I have such issue with the ICommand implementations too.
To sum up my question:
How do I manage the dependencies in WPF properly, without using the Service Locator anti-pattern and without passing every single abstraction through the ctor of the main window view model? I could very easily pass a Container around and let, e.g., the create ABC command solve the ABCDialog before opening it, but I feel this will cause more issues than it would solve.
I am probably doing something wrong. Please advise me what is the best practice.

Caliburn Micro - ActivateItem using Container

I was going through the Caliburn Micro documenation here. Simultaneously, I was trying to put up some rough code for experiment. I am a little confused about how to activate item using a container and how to pass an object to the ViewModel that we are activating.
Lets consider a master/detail scenario. The master contains a list (say datagrid) and the details contain specific row from the master for update(say tab item inside tab control). In the documentation (for ease of understanding), I believe the detail ViewModel was directly instantiated using code like this
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive {
int count = 1;
public void OpenTab() {
ActivateItem(new TabViewModel {
DisplayName = "Tab " + count++
});
}
}
So, to apply the above fundamental concept in real world app, we need to instantiate the DetailViewModel (TabViewModel above) using container(say MEF). The challenge then is to know whether the particular DetailViewModel is already opened in the TAB Control. The immediate crude thing that came to my mind was maintaining a List of the Opened Tabs (DetailViewModels). But then we are again referencing DetailViewModel in the MasterViewModel defeating the purpose. Is there any options available to solve this issue.
The second thing that is troubling me is how to pass the Objects from MasterViewModel (Selected Detail Item) to the DetailViewModel. If we use the EventAggregator here then each of the opened DetailViewModels will receive the event which I am not sure how to handle.
If anyone can throw some light on the above two issues, I would be grateful
Update:
The Master is Conductor like this
public class MainViewModel : Conductor<IScreen>.Collection.OneActive, IShell {
....
}
And the detail is defined like this
public class TabViewModel : Screen {
....
}
Both are in the same Window.
I'm not sure exactly what the issue is. In your conductor of many, you have an Items collection provided by Caliburn.Micro. When you come to display a detail view, you can check this collection for the existence of that detail view (using the primary key which you have from the master view).
If the item is already in the Items collection then just activate it (using the ActivateItem method). If the item isn't in the collection, then instantiate it (presumably using a factory if you're using MEF), and add it to the Items collection, and then activate it.

Pass Single Instance of Model to Multiple View Models in same Module for different views

I am working on a project using PRISM where I have left navigation implemented as Tree View and any click event happens raise event using event aggergation to Enrolment Module which has multiple view model for multiple views (like Wizard Applicaiton where you can go through many views to collect data). I want to have a common or shared or singleton model which can be passed across this view models and save at the end.... users can click on any link any navigation at any time and it should save data in to this singleton model expsosed through different view model. Do you have any samples which are doing something like this... or can you type up a quick one on how to do it? OR it is not possible to do it at all. I am following all patterns from Brian Lagunas's Pluralsight Video for PRISM so try to use that way....
I would have a MasterViewModel which controls the "wizard" pages and current state
It would contain the following properties:
List<ViewModelBase> Pages
int CurrentPageIndex
ViewModelBase CurrentPage, which returns Pages[CurrentPageIndex]
MyClass DataObject
The MasterView that goes with the MasterViewModel would be nothing more than a ContentControl with it's Content bound to CurrentPage. I would probably also define DataTemplates in the MasterView which tells WPF which View to draw with which Page
Your MasterViewModel would be in charge of handling the pages, and passing each page a reference to the data it needs. For example in the constructor it might say,
public MasterViewModel(MyClass dataObject)
{
DataObject = dataObject;
Pages.Add(new InfoPage(DataObject));
Pages.Add(new AddressPage(DataObject.Addresses));
Pages.Add(new PhonePage(DataObject.Phones));
Pages.Add(new SaveMyClassPage(DataObject));
CurrentPageIndex = 0;
}
I have an example here if you're interested
I don't know, is it prism way, or something another, when I build something like wizard, first of all I create instance of all data which wizard collect.
public WizardData wd = new WizardData();
Then, every page of wizard are initialized by this wd instance, i.e.
public FirstWizardPage(WizardData wd)
{
this.wizardData = wd;
}
So, this way allow you to have button Finish on every page, for example. You can initialize your ViewModel with wd, or its properties.
This way is not the best. Its hust one of the possible way.
Another - is to create singleton and use it without reference passing from page-to-page.
When you use Prism you also have a Dependency Injection Container, usually Unity or MEF. To solve your problem you can register your model as singleton to those DI containers. Every view model that asks the DI container to resolve their dependecy, in our special case the model, will get the singleton instance back from the DI container.
Unity example: You register your model as singleton instance:
public void Initialize( )
{
container.RegisterInstance<Model>(new Model(), new ContainerControlledLifetimeManager( ));
}
Now you can resolve your dependencies in your view model:
public ViewModel(IUnityContainer container)
{
Model model = container.Resolve<Model>();
}

MVVM model instantiation

Following WPF MvvmFoundation, linking the View with the ViewModel has many choices like described on http://www.paulstovell.com/mvvm-instantiation-approaches.
However their example has nothing about how to link the ViewModel with the Model.
Traditionally I created the model first and then one or more views that render it. It seems that MVVM pushes people to create the View, which creates the ViewModel, which create the Model. I hope it's not the case as wiring a complex business model with various ModelView can else be tough.
How do you instantiate your business model classes in MVVM and link them with your ViewModels?
I normally pass Model objects as constructor params to VM. I use App class as the controller which will initialize MainWindow, MainWindowViewModel with the main model. There after the MainWindowViewModel takes care of initializing other VMs with appropriate model objects.
private void Application_Startup(object sender, StartupEventArgs e)
{
mainWindow = new MainWindow();
mainWindow.DataContext = new MainWindowViewModel(new Model());
mainWindow.Show();
}
You create your BusinessModel classes inside your ViewModel.
So in your CustomerViewModel you would say this.CurrentCustomer = new CustomerModel(), and your CustomerView would bind to the CurrentCustomer property on the ViewModel
If you are interested, I wrote up a simple sample using MVVM as an example of how the View, Model, and ViewModel interact.
I use dependency injection/MEF to do this. Just export all of my model classes all the way down the chain, and have them imported for me automatically into the ViewModel constructor.
I take a variety of different approaches depending on the situation. I've found that when it comes to getting this data linked, one size does not fit all.
For simple cases, I will have the ViewModel and the Model be the same thing. Obviously not that good for all cases, but sometimes there is just no need to go the extra mile to split the M from the VM. (Great for cases where you have, say, listbox items that have scant information)
Sometimes, especially when the model is a chunk of code you don't have access to (written by another developer) it is easy to subclass the model, and add all of your VM things (observable properties, etc.) on to it.
Lastly, I will use the approach that is mentioned by Souvik. Construct the VM with the model information that you want to use as a parameter, or allow it to be passed in otherwise. This is probably the most common approach for my larger and more complex Model / ViewModel relationships.
I am auto-passing IRepository instance to VM constructor using IoC container and everything VM needs to do with models is done via this repository. Repository is class which: Create, read, update and delete data. When I need to show some view (window), I use IViewService.ShowDialog(viewModel As ViewModelBase). In implementation of IViewService, there are views registered with VMs, so VMs only need to know other VMs and not their views (like "Show me view for this view model").

Is it right for a ViewModel to inject itself into its child ViewModels?

I created a MVVM test application which at runtime reads an XML file to create a menu dynamically and based on what the user selects, loads in that page's UserControl dynamically. The result is a nice MVVM pattern that allows you to have one View/ViewModel pair per page all defined in an XML file. Very nice.
So now I just added the ability for the developer to create a button on one page that goes to another page. The way I do this is in the MainViewModel I build the collection of ViewModels in the ObservableCollection and when I build each, I inject the MainViewModel itself (this) into each UserControl-ViewModel's constructor. This way each UserControl has inside of it the MainViewModel so that the developer can manipulate the application (e.g. call SwitchPage(idCode)) via the MainViewModel.
Also any global state that I need to save I can save in the MainViewModel to which each UserControl has access.
Also, each UserControl (PageItem) has full access to each of the other UserControls which gives me basically control of anything in the application from any UserControl, something that I have been trying to achieve in an MVVM application for a long time.
So my question is: Is this kind of ViewModel injection a useful/known pattern or are there problems with injecting a ViewModel into its child ViewModels? It seems recursive to me but seems to work fine and gives me the functionality I want so far. From what I have learned from the Composite Application Library architecture, this seems to be something similar that is going on there, and that e.g. in the code below I could inject other application objects into my ViewModels as they need them.
public MainViewModel()
{
PageItems pageItems = PageItems.Create("all");
foreach (PageItem pageItem in pageItems.Collection)
{
string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
string viewModelName = assemblyName + ".ViewModels.PageItem" + StringHelpers.ForcePascalNotation(pageItem.IdCode) + "ViewModel";
var type = Type.GetType(viewModelName);
var viewModel = Activator.CreateInstance(type, this, pageItem) as ViewModelPageItemBase;
AllPageItemViewModels.Add(viewModel);
}
CurrentPageItemViewModelIndex = 0;
LoadCurrentPageItemViewModel();
}
I don't see this as wrong or right, but more about how coupled your design is. I normally start with the approach you mentioned (child VM references parent VM) and then decouple if I find it gets too unwieldy. Your first choice doesn't necessarily have to be your last - you can refactor your code as it evolves.
Possible alternatives are:
child VM raises event that parent listens to (observer pattern). Parent VM knows children intimately, but child does not know parent.
child VM publishes event on event hub that parent listens to (mediator pattern). Neither child nor parent are intimately aware of each other. They are aware only of the mediator.

Resources