Master-Detail relation with inheritance in a WPF MVVM application - wpf

I think this is a rather basic question, but I haven't been able to find an answer to this.
I have the following scenario:
Without using any form of EntityFrameWork I have a PersonViewModel and a PersonDetailsViewModel, which inherits from the PersonViewModel. In my PersonView I display a collection of PersonViewModels in a grid. I have properties like Name, DateOfBirth etc, as columns. When I double click on a person a PersonDetailView pops up which is bound to a PersonDetailsViewModel. In this View there is extra information shown about the person (Gender, SocialSecurity number etc.). The user I allowed to edit all properties.
Now I wonder what the best/common approach is to make sure that the PersonViewModel gets updated with the values that have been editted in the PersonDetailsViewModel.
I can think of several options. For starters I could opt for not using different ViewModels, but instead use PersonDetailViewModels to show in the grid, but the downside to that is that I would need to retrieve a lot of unnecessary data per ViewModel.
I can also synchronise the corresponding properties after the PersonDetailsView closes.
The third option I can think of is that instead of inheriting from the PersonView I will include a property in PersonDetailsView that is of the type PersonView and expose it's properties and use it for binding in the PersonDetailsView. All other extra properties in PersonDetailsViewModel will then be retrieved in it's constructor.

In my experience, the best way to update view models who share the same data is to use the Mediator pattern to send a notification message that the data has updated.
In order to have a good object oriented design we have to create lots
of classes interacting one with each other. If certain principles are
not applied the final framework will end in a total mess where each
object relies on many other objects in order to run. In order to avoid
tight coupled frameworks, we need a mechanism to facilitate the
interaction between objects in a manner in that objects are not aware
of the existence of other objects.
Source http://www.oodesign.com/mediator-pattern.html
It is syntactically important to your design that the notification says what has happened (the data was updated) and not what should happen (data gets reloaded) because that response may not stay the same as the system evolves.
Often, common MVVM libraries have Mediator implementations in them. For example, Prism's EventAggregator.

in Addition to Sheridans answer. i would expose the Detail as a property of type PersonDetailsViewModel
public class PersonViewModel
{
public PersonDetailsViewModel Detail {get;set;}
}
then your xaml looks simply like that
<TextBlock Text="{Binding Detail.Gender}"/>
EDIT1: in addition to the comments above
i would not do any inheritence (thats what user1087702 wrote in his question). i would simply create 2 classes: PersonVM and PersonVMDetail. And if the request is, to show Details from my person object, why in hell shouldn't i just create a public Property in my PersonVM class of type PersonVMDetail - to fullfill this request?

The simplest way to achieve your requirements would be to add a constructor to your PersonViewModel class that takes a PersonDetailsViewModel instance and updates its common properties:
public PersonViewModel(PersonDetailsViewModel personDetailsViewModel)
{
Name = personDetailsViewModel.Name;
...
DateOfBirth = personDetailsViewModel.DateOfBirth;
}
...
PersonViewModel = new PersonViewModel(PersonDetailsViewModel);
Of course, this doesn't have to be in the constructor... it could just as easily be a method in the PersonViewModel class, or even a helper method in a separate Factory Pattern class, it's up to you.

Related

Correct way to handle commands that rely on multiple view models

I'm relatively new to WPF and MVVM and i am trying to understand how to use commands correctly when they have dependencies in more than 1 view model.
A couple of examples:
In my current application i have a RelayCommand which causes a save action to occur in a couple of different view models (they write a couple of different files). Currently i am handling this using a the mvvmlight messenger to send a message to those view models to get them to do the save which i think is the correct way to do it as it avoids having to provide some kind of delegate or event to/on those view models.
I have a RelayCommand in a view model that has a CanExecute method which relies on the state of 2 other view models. I've currently handled this via the mvvmlight messenger as well by having changes in the view models the CanExecute method depends on message that their state is now valid for the operation. This seems messy but the only alternative i could think of was to use a delegate or event effectively weaving the view models together which i believe i should be avoiding.
Is there some generally accepted way to deal with this which i am missing?
In general your view model layer should have a 1:1 relationship with your view, there should be no good reason for a "Save" function to exist in a view model which is then called by another view model.
What it sounds like you should be doing is putting that logic into a service i.e. something like this:
public interface ISerializationService
{
void Save(SomeData data);
}
Then you need an implementation for this service that does the actual work:
public class SerializationService : ISerializationService
{
void Save(SomeData data)
{
// actual save happens here
}
}
Your view models should then contain properties that point to instances of these services:
public class MyViewModel : ViewModelBase
{
[Inject]
public ISerializationService SerializationService { get; set; }
// called when the user clicks a button or something
private void ButtonClickCommand()
{
this.SerializationService.Save(this.SomeData);
}
}
The only question remaining is "What sets the value of SerializationService?", and for that you need a dependency injection framework. There are plenty out there, MVVMLight installs one itself, but Ninject is the de-facto standard. When implemented properly the injection framework will create all view models for you and then "inject" the dependencies, i.e. your SerializationService property, of type ISerializationService, will be initialized with an instance of your SerializationService class (which in a case like this will also be configured to be a singleton).
Dependency Injection takes a bit of work to get your head around but once you start using it you'll never look back. It facilitates complete separation-of-concerns whilst alleviating the need to pass pointers to everything all up and down your architectural hierarchy.

Notification from ViewModel to View

I need to send notification from ViewModel to View in MVVM WPF application. In most cases it will be simple MessageBox on View side. Is it unacceptable violation of MVVM pattern to use types like System.Windows.MessageBoxResult or System.Windows.MessageBoxImage in ViewModel (in this case VM must reference UI-specific libraries)?
I need to pass title, message and notification type from VM and by creating custom enumerations I am in fact copying existing functionality from .NET Framework.
You don't need notification mechanism. That's one option. Another is using simple service class:
public class ViewModel
{
IDialogService _dialogService; //ctor injection or use service locator
public void CommandExecute()
{
_dialogService.ShowMessageBox(...);
}
}
public interface IDialogService
{
bool? ShowMessageBox(params....);
}
public class DialogService : IDialogService
{
public bool? ShowDialog(params...)
{
MessageBox.Show(params...);
}
}
I find this approach more straightforward, easier to understand and easier to debug. Messaging may easily turn into memory leak and in this case I don't see any benefit over my approach.
EDIT:
Will you use custom enum in ShowMessageBox parameters, or will you use
System.Windows.MessageBoxImage?
First of all, ViewModels belong to presentation layer. It is OK to use enums like System.Windows.MessageBoxImage in ViewModel.
In MVVM, ViewModels are separated from Views because of following reasons:
ViewModels expose data and logic from Model in such way that is easily consumable from Views (e.g DataBinding)
ViewModels are easier testable
ViewModels provides better design time support (Blendability, test data)
ViewModels separates application logic from the actual presentation markup which makes it easier to understand (readbility)
Does using enum like System.Windows.MessageBoxImage breaks any of the points above? The answer is no.
If you wanted to reuse IDialogService and your ViewModels on multiple platforms, for example WPF, UWP and Xamarin, then you need to create your own enum, because it may not exist on all platforms. The golden rule is: don't add another layer of abstraction if you don't need it.
You could have the view implement an interface, say INotificationService and then pass the view to the view model constructor. That won't violate MVVM, and you will still be able to bind the view model to the view using the DataContext property on the view.
I decided to keep ViewModel as clean of UI (View) stuff as possible, so I will create custom enumeration inspired by MessageBox and tailored exactly to my needs.

How do I structure MVVM with Collections?

I'm having trouble understanding how to apply the MVVM pattern when Lists/Collections are involved.
Say the MainModel has a few properties and methods, as well as a list that contains other DetailModel objects. The DetailModel objects can be added, removed, or re-ordered.
The MainView will show a few controls related the the root model, and have a ListBox populated from the list. Each item will have it's own sub-view via a DetailModelView UserControl.
Finally, there is a MainViewModel. This has properties backed by the MainModel's properties and methods, bound to the Main View, with change notification keeping everything in sync. (Up to this point, I am comfortable with the pattern - more stating this in case there is something fundamental I am missing...)
When it comes to handling the list, I get confused. I have come across several examples where the MainViewModel simply exposes the list of DetailModels to the view, and the DetailModelViews are bound directly to the models. This functions, but is problematic. It does not consistently following the pattern (no DetailViewModel exists), and it drives me to include some UI-related code in my detail models. It seems clear to me that the MainViewModel should expose a list of DetailViewModels for the UI to bind, but I am stuck on how to implement such a thing!
How should manage the two lists (DetailModels and DetailViewModels)? I am really confused as where I initially populate the DetailViewModel list, and how I should handle adding, removing, or changing the order of the items to keep them synchronized!
Usually Models are nothing more than data objects. They shouldn't contain any code to do things like add/remove items from a list. This is the ViewModel's job.
In your case, I would create a MainViewModel that has the following properties:
ObservableCollection<DetailViewModel> Details
ICommand AddDetailCommand
ICommand RemoveDetailCommand
If your MainModel class is a data object, you can either expose it, or it's properties from the MainViewModel as well. Exposing it's Properties is the "MVVM purist" approach, while exposing the entire Model is sometimes more practical.
Your MainViewModel is in charge of creating the initial list of DetailViewModels, and it is in charge of Adding/Removing these items as well. For example, in the PropertyChanged event for the MainViewModel.MainModel property, it might rebuild the MainViewModel.Details collection, and the CollectionChanged event for the MainViewModel.Details property would update MainViewModel.MainModel.Details
You are right to have a separate DetailModels list and DetailViewModels list. The DetailViewModels list should be a property of type ObservableCollection<DetailViewModel>. You can populate the observable list when you set the Model (or at construction time, if you pass the model into the constructor of your ViewModel.)
private ObservableCollection<DetailViewModel> m_details;
public IEnumerable<DetailViewModel> Details
{
get { return m_details; }
}
You can the subscribe to m_details.CollectionChanged. This is where you can handle re-ordering the contents of the list in the Model.
I hope this helps.
In my experience, the only time you get away with exposing model objects to the view is if you're doing simple read-only presentation, e.g. displaying a string property in a ComboBox. If there's any kind of actual UI involving the object (especially one involving two-way data binding), a view model is needed.
Typically, a master VM's constructor will look like this:
public MasterViewModel(MasterModel m)
{
_Model = m;
_Detail = new ObservableCollection<DetailViewModel>(m.Detail);
}
where MasterModel.Detail is a collection of DetailModel objects, and _Detail is a backing field for a Detail property that's exposed to the view.
As far as adding, removing, and reordering items in this list is concerned, in the UI at least this will be done through commands on the MasterViewModel, which must manipulate both MasterModel.Detail and MasterViewModel.Detail. That's a bit of a pain, but unless you want to repopulate MasterViewModel.Detail after every change to MasterModel.Detail, it's really unavoidable.
On the other hand, if you've been wondering "why would I ever need to write unit tests for view models?", now you know.
Here is an answer that I think addresses this issue very nicely using an ObservableViewModelCollection<TViewModel, TModel>
It's nice and lazy. It takes an ObservableCollection and a ViewModelFactory in the ctor. I like it because it keeps state at the model layer where it belongs. User operations on the GUI can invoke commands at the VM which manipulate the M via public methods on the M. Any resulting changes at the M layer will be automatically handled by the class in this link.
https://stackoverflow.com/q/2177659/456490
Note my comment regarding SL vs. WPF

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").

WPF/MVVM: Delegating a domain Model collection to a ViewModel

A domain model collection (normally a List or IEnumerable) is delegated to a ViewModel.
Thats means my CustomerViewModel has a order collection of type List or IEnumerable.
No change in the list is recognized by the bound control. But with ObservableCollection it is.
This is a problem in the MVVM design pattern.
How do you cope with it?
UPDATE: Sample of how I do it:
public class SchoolclassViewModel : ViewModelBase
{
private Schoolclass _schoolclass;
private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();
public SchoolclassViewModel(Schoolclass schoolclass)
{
_schoolclass = schoolclass;
_schoolclass.Pupils = new List<Pupil>();
foreach (var p in schoolclass.Pupils)
Pupils.Add(new PupilViewModel(p));
}
public Schoolclass GetSchoolclass
{
get { return _schoolclass; }
}
public int ID { get; set; }
public string SchoolclassName
{
get { return _schoolclass.SchoolclassName;}
set
{
if(_schoolclass.SchoolclassName != value)
{
_schoolclass.SchoolclassName = value;
this.RaisePropertyChanged("SchoolclassName");
}
}
}
public ObservableCollection<PupilViewModel> Pupils
{
get{ return _pupils;}
set
{
_pupils = value;
this.RaisePropertyChanged("Pupils");
}
}
}
I deal with this by not doing it the way you describe.
If I need to present a Foo object and its related Bar objects in the view, the FooViewModel will generally implement a Bars property of type ObservableCollection<BarViewModel>.
Note that this is irrespective of whether or not the underlying Foo class has a Bars property of type IEnumerable<Bar>. The Foo class might not. The application might not even need to be able to iterate over all of the Bar objects for a Foo, except in the UI.
Edit
When my view is a simple representation of the application's object model, I pretty much do things as you do in your sample. The code in my constructor is generally a bit more compact:
_Bars = new ObservableCollection<BarViewModel>(
_Foo.Bars.Select(x => new BarViewModel(x)));
but it's essentially the same thing.
But this assumes that Foo actually exposes a Bars property. It might not. Or maybe only some Bar objects should appear in the view. Or maybe they should appear intermingled with other objects, and the FooViewModel should expose a CompositeCollection of some kind.
The point I'm making is that the view model is a model of the view. This doesn't necessarily have a direct correspondence to the underlying object model.
To pick a simple example: My program may give the user a way of putting items into five different categories by dragging and dropping them into five different ListBox controls. Ultimately, doing this sets a Category property on the Item object. My view model is going to have a collection of CategoryViewModel objects, each with a property of type ObservableCollection<ItemViewModel>, so that dragging items back and forth between collections is simple to implement.
The thing is, there may not even be a Category class in the application's object model, let alone a collection of Category objects. Item.Category might just be a property of type string. The CategoryViewModel isn't mirroring the application's object model. It only exists to support the view in the UI.
Ok, I'll go ahead and add my thoughts as an answer instead of in the comments. :)
I think the bottom line is that this is just the reality of the way WPF and databinding work. In order for two-way databinding to operate, collections need a means of notifying controls that are bound to them, and the standard lists and collections used in most domain objects don't/won't/shouldn't support this. As I mentioned in a comment, being required to implement INotifyPropertyChanged for non-collection properties is another requirement that may not be met by a standard domain object.
Domain objects are not intended to to be viewmodels, and for this reason you may find that you need to map back and forth between the two types of objects. This is not dissimilar to having to map back and forth between domain objects and data access objects. Each type of object has a different function in the system, and each should be specifically designed to support their own role in the system.
All that said, Agies's idea of using AOP to automatically generate proxy classes is very interesting, and something I intend to look into.
What I do is instead of using ObservableCollection in my domain model is use my own collection type that implements the INotifyCollectionChanged interface amongst other useful standard and custom interfaces. My way of thinking is that much like Rockford Lhotka suggests in his book that change notification is useful in to more than just a presentation layer since other business objects within the domain layer often need some sort of notification when state changes in another object.
With this methodology you could create your own collection type that still has the benefits of change notification and as well as what ever custom behaviors you need. The base class for your collection could be implemented as purely infrastructure code and then a subclass could be created that could contain business logic using the subtype layering techinque used in this book. So in the end you could have a collection that can wrap types of IEnumerable<> and provide the change notification stuff your looking for as well for both your domain model and presentation code.

Resources