I have a couple of questions regarding MVVM view models. I have 3 view models in my scenario, which I have put together a shorter sample below. I was looking for a nice solution for 3 views working in conjunction with one another in a small part of a large application. There are 3 classes in play here:
ParentViewModel
Represents a view that presents a combo box with source of MyLookups to the user to select a value and store in SelectedLookup.
There is a button which will invoke the ShowDialogCommand which will show the DialogVM1 via the IDialogService.
DialogViewModel
Represents a view where the user will enter some field information into NewItemTemplate. When they click OK the dialog will close and the NewItemTemplate is passed to ChildVM1 by calling SetSomeInformation.
ChildViewModel
Within ChildViewModel the template data is then transformed into a list of data but this is irrelevant. The ChildViewModel provides a grid where the user can modify a list of SomeData. The DefaultItem Lookup is populated from a combo in the grid who's data source is MyLookups.
IViewModelLocatorService
This uses my IoC container to get the view instance. The views themselves have a variable number of service dependencies. The purpose of the IViewModelLocatorService was to create my view instance when I needed it. However, whilst writing this post, I am thinking I could just inject the view instance into ParentViewModel constructor. All of my view models are registered in the IoC container.
ILookupService
Its purpose is to populate a list of items which will be displayed as a combo box on all 3 views. The FindAll will basically hit the database (calling Entity Framework) and populate a new list. This creates a new DbContext each time. At the moment Lookup does not override Equals so although the data will be the same based on Id, but the x == y would be false - so where the data is being passed around, I am working with 3 different lists.
Currently all services are registered as a singleton.
public class ParentViewModel
{
private readonly ILookupService _lookupService;
private readonly ISomeService _someService;
private readonly IViewModelLocatorService _vmLocatorService;
private readonly IDialogService _dialogService;
public ParentViewModel(
ILookupService lookupService,
ISomeService someService,
IViewModelLocatorService vmLocatorService,
IDialogService dialogService,
ChildViewModel1 ChildViewModel // should we inject this?
)
{
...
MyLookups = _lookupService.FindAll(); // actually done from a load command but added here for simplicity sake
}
public ChildViewModel1 { get; private set; }
public ObservableCollection<Lookup> MyLookups { get; set; }
public Lookup SelectedLookup { get; private set; }
public ICommand ShowDialogCommand => new RelayCommand(
execute: () =>
{
// I want to show DialogViewModel here
var dialogVM = new DialogViewModel(...);
var dialogVM = _vmLocatorService.NewInstance<DialogViewModel>();
dialogVM.DefaultLookup = this.SelectedLookup;
_dialogService.Show(dialogVM, this);
if (dialogVM.DialogResult == true)
{
// pass some information to the child VM
ChildViewModel1.SetSomeInformation(dialogVM.NewItemTemplate);
}
},
canExecute: () => true);
}
public class ChildViewModel1
{
private readonly ILookupService _lookupService;
public ChildViewModel1(ILookupService lookupService)
{
...
MyLookups = _lookupService.FindAll();
}
public void SetSomeInformation(Lookup newLookupSelection)
{
...
}
public ObservableCollection<Lookup> MyLookups { get; set; }
}
public class DialogViewModel
{
private readonly ILookupService _lookupService;
private readonly ISomeOtherService1 _someOtherService1;
private readonly ISomeOtherService2 _someOtherService2;
public DialogViewModel(ILookupService lookupService, ISomeOtherService1 someOtherService1, ISomeOtherService2 someOtherService2)
{
...
MyLookups = _lookupService.FindAll(); // actually done from a load command but added here for simplicity sake
}
public SomeData NewItemTemplate { get; private set; }
public ObservableCollection<Lookup> MyLookups { get; set; }
}
public class SomeData
{
public long Id { get; set; }
public string Name { get; set; }
public Lookup DefaultItem { get; set; }
...
}
My questions are:
Is IViewModelLocatorService a reasonably valid way to get a view model instance in a view model or is it best to keep the dependency explicit by injecting the child view model as a constructor dependency? I've read things about this being a bad practice in the past although I can't find anything after a quick look tonight.
With the ILookupService, there is currently 3 different lists - each with pretty much the same data. Is there a nice way to make the 3 views use the same list of data? The only idea I have is to forcefully set the list on the DialogVM and ChildViewModel via a property.
ParentVM
as "parent virtual machine". Stick to the well-established convention ofParentViewModel
and your code will be much easier for others to read.