Invoke user control function from view model - c#

How do I call a function on a user control from a separate view model?
In my scenario, I have a "main" view with a ScoreDisplay UserControl:
<local:ScoreDisplay/>
This control will display the score of n number of players in my game. The main view model is hooked up to a game controller that feeds it the score updates. I need to get these updated scores to the UserControl (and run some animations, non-trivial logic, etc.)
I see a few options:
Create a "Scores" dependency property and bind it to the view models collection of scores. The only problem I see with this approach is that whenever it is modified, I need to go look and see what changed so I can run the appropriate animations. Doing this is certainly possible, but doesn't seem "right".
Have the ViewModel invoke a "UpdateScore" function on the UserControl. The only problem, of course, is that the ViewModel shouldn't know anything about the View and so shouldn't have the reference necessary to do this.
Have the UserControl register for a "ScoreUpdated" event on the view model. This seems like the best option, but I have no idea on how to get the reference to the ViewModel in order to register for the event.
Which option, if any, is the correct approach? If (2) or (3), how can I implement this?
EDIT:
To be clear, the values in the scores collection are changing (the collection itself remains the same). I could put a wrapper around the score int and listen to PropertyChanged, but again, this seems like an overcomplicated approach to a simple problem. If that is the best solution though, please let me know!
EDIT 2:
UpdateScore is a function that (in theory) accepts the index of the updated score and the value to add to that player's score (it could accept the whole score). It then causes the player's peg to move along a cribbage track to its new position.
It gets called whenever a player gets points (this is a Cribbage game, so this happens a lot). The view model is attached to a game controller that raises an event to notify the VM that a player has been awarded points. The view model basically just needs to pass this information to ScoreDisplay for display/animation etc.

In this case we can apply the Mediator pattern, if this is succeed, it will not need the event.
There are several embodiments of the Mediator pattern, but I like the most the implementation by XAML Guy, it is simple and clear - The Mediator Pattern.
Implementation code
public static class Mediator
{
static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();
static public void Register(string token, Action<object> callback)
{
if (!pl_dict.ContainsKey(token))
{
var list = new List<Action<object>>();
list.Add(callback);
pl_dict.Add(token, list);
}
else
{
bool found = false;
foreach (var item in pl_dict[token])
if (item.Method.ToString() == callback.Method.ToString())
found = true;
if (!found)
pl_dict[token].Add(callback);
}
}
static public void Unregister(string token, Action<object> callback)
{
if (pl_dict.ContainsKey(token))
{
pl_dict[token].Remove(callback);
}
}
static public void NotifyColleagues(string token, object args)
{
if (pl_dict.ContainsKey(token))
{
foreach (var callback in pl_dict[token])
callback(args);
}
}
}
Communication via a Mediator is carried out as follows:
Mediator.NotifyColleagues("NameOfYourAction", ObjectValue);
In this case, we notify NameOfYourAction that you need to pass the ObjectValue for him. In order to NameOfYourAction successfully received the data, it is necessary to register it in the class or in the ViewModel like this:
private void NameOfYourAction_Mediator(object args)
{
MyViewModel viewModel = args as MyViewModel;
if (viewModel != null)
viewModel.PropertyA = someValue;
}
// Somewhere, may be in constructor of class
Mediator.Register("NameOfYourAction", NameOfYourAction_Mediator);
In your case, the value is passed ScoreData in ViewModel, where which will be changes.
For more example of using pattern Mediator, please see this answer:
One ViewModel for UserControl and Window or separate ViewModels

Anatoliy Nikolaev's answer above is a good one.
As another option I would also suggest looking into the Event Aggregator. This is a great pattern and has many uses. They would both get a reference to the Event Aggregator and then one could publish an event and the other would receive it and could take action. If you enable something like this in your application it becomes trivial for multiple ViewModels to communicate in a completely decoupled way. And as an added bonus testing becomes simple as you can easily mock out your Event Aggregator to supply any data needed.

I found a way to do this:
I made a dependency property of my view model type:
public GameViewModel BaseViewModel
{
get { return (GameViewModel)GetValue(baseViewModelProperty); }
set { SetValue(baseViewModelProperty, value); }
}
public static readonly DependencyProperty baseViewModelProperty =
DependencyProperty.Register("BaseViewModel", typeof(GameViewModel), typeof(ScoreDisplay), new PropertyMetadata(null, RegisterForScoreChange));
and changed my XAML to:
<local:ScoreDisplay BaseViewModel="{Binding}"/>
Then in the PropertyChanged event handler of the dependency property, I was able to wire up my event.
(e.NewValue as GameViewModel).ScoreUpdated += (d as ScoreDisplay).UpdateScore;

Related

wpf application mvvm how to structure properly

Im trying to learn MVVM, but I'm having some trouble. Im new to xaml and c#.
What I have so far:
A person class, that define a person object: name, age and other info
A model class people, owns a private linkedlist (list of person), which also contains methods such as get, remove, add and do some calculations
a viewmodel class, doing casting/parsing/transforming between xaml behind code and the model.
A xaml behind code file mainWindow.xaml.cs, that listen to button click and such, and invoke methods from the viewModel class, and do some simple binding such as total.Content = objModelView.getTotal().
I didnt use INotifyPropertyChanged ObservableCollection, still trying to wrap my head around it. While my program does what I want I'm not sure how to structure it better.
Basically I have 2 main questions:
I see examples online where people store/initiate the list of items in viewmodel, shouldn't I keep the list in model instead, that should be where all the data be stored right?
Let's say I'm suppose to display all the items (in the list of the model class) onto a dataGrid. Right now in my program: mainWindow.xaml.cs will detect the button click, then it ask viewModel to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd }); Is this bad practice? Dont get how to use ObservableCollection here, can it somehow detect a change in the list of my model class, and then remove and add the rows to the datagrid?
I've been reading for the whole day but I'm struck here, hope I can get some direction
The model stores data, the view display it, the viewmodel is the bridge between the two.
That doesn't mean that the view have a direct access to model data because you don't always need to display all data in model layer. So we have a viewmodel layer that makes only useful information accessible.
The viewmodel is very useful when you want to display the same data multiple times but displayed differently: you don't have to replicate data, you only need to define twice what information you need from those data and how to display them.
What you're doing in your second question is using model in view : This is not MVVM. What you want to do is bind the ItemsSource DP of Datagrid to a list of PersonVM which will fetch information from Person.
You code could be structured like that:
public class Person {
public String Name {get; set;}
public int Age {get; set;}
}
public class PersonVM {
public PersonVM(Person model) {
_model = model;
}
private readonly Person _model;
internal Person Model {get {return _model;}}
public String Name {
get { return _model.Name; }
set { _model.Name = value; }
}
public int Age {
get {return _model.Age;}
set { _model.Name = value; }
}
}
//PersonV.xaml
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
public class People : ObservableCollection<Person> {
}
public class PeopleVM : ObservableCollection<PersonVM> {
public PeopleVM(People model) {
_model = model;
foreach(Person p in _model) {
Add(new PersonVM(p));
}
_model.CollectionChanged += CollectionChangedHandler;
}
private void CollectionChangedHandler(Object sender, NotifyCollectionChangedEventArgs args) {
switch (notifyCollectionChangedEventArgs.Action) {
case NotifyCollectionChangedAction.Add:
foreach(People p in args.NewItems) {
if(!this.Any(pvm => pvm.Model == p)) {
this.Add(new PersonVM(p));
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach(People p in args.OldItems) {
PersonVM pvm = this.FirstOrDefault(pe => pe.Model == p);
if(pvm != null) this.Remove(pvm);
}
break;
case NotifyCollectionChangedAction.Reset:
Clear();
break;
default:
break;
}
}
private readonly People _model;
}
//PeopleV.xaml
<ItemsControl ItemsSource={Binding}>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type PersonVM}">
<PersonV/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public class AppVM {
public AppVM() {
People p = ServiceLayer.LoadPeople(); //load people
People = new PeopleVM(p);
}
public PeopleVM People {get; set;};
}
//MainWindow.xaml
<Window ...
>
<Window.DataContext>
<local:AppVM/>
</Window.DataContext>
<PeopleV/>
</Window>
Answer to your post can be as long as one wishes to explain, perhaps a whole lengthy blog itself. I will try to just answer 2 of your specific questions here. I am not going to show the code for each sub-answer, you have to take it as home work. :)
I didnt use INotifyPropertyChanged ObservableCollection, still trying
to wrap my head around it. While my program does what I want I'm not
sure how to structure it better.
Why? If you don't use these magic sticks, it's better you write a WinForms app and not a WPF one. Forget everything and dive into these two. You must (no escape) understand and use them in MVVM/WPF. You can even defer reading my further answer for that matter.
I see examples online where people store/initiate the list of items in
viewmodel, shouldn't I keep the list in model instead, that should be
where all the data be stored right?
They are not wrong. Person class in model layer represents a real world entity and is must, however, I would not bother about having People class in model. It's just a collection that could easily be accommodated by ViewModel. I personally would prefer that way always.
Let's say I'm suppose to display all the items (in the list of the
model class) onto a dataGrid. Right now in my program:
mainWindow.xaml.cs will detect the button click, then it ask viewModel
to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge,
address = newAdd }); Is this bad practice? Dont get how to use
ObservableCollection here, can it somehow detect a change in the list
of my model class, and then remove and add the rows to the datagrid?
That's not MVVM, trust me. At the maximum what you should be required to write in view code behind, is initializing view model and setting it as view's data context.
To handle view events (Button.Click for ex) you should use ICommand implementation that will be bound to Button.Command property in XAML. This way you decouple control's event handler from the code behind.
You need to have a ObservableCollection<Person> in your viewmodel which will be bound the DataGrid in view. So when click a button to add person, button's command object will update this collection and view will be refreshed automatically without having you to add it manually to data grid.
You aren't using MVVM at all. It sounds like you are using MVP, which is a completely different pattern.
Before you continue, you need to understand what MVVM was designed for, because its a highly complicated (seemlying over engineered pattern) with a huge number of abstractions just to write the ubiquitous To-Do list.
But you must do all of it, otherwise its not MVVM.
The Zen of MVVM
MVVM grew out of the realisation that writing good, bug-free, safe UI code is hard. Testing UI code is harder, and involves hiring human testers, that are slow and can get it wrong.
So the solution that they came up with was simple.
DON'T WRITE ANY CODE IN YOUR UI
Done.
Except, not. Now, your UI doesn't do anything, it just looks pretty. So they added an extra layer between the UI and the Program/Business Logic/Model, and they called it the ViewModel.
The job of the ViewModel was to tell the UI what to do. But the trick was to get it to tell the UI what to do, without the ViewModel knowing about the UI at all.
(MVP has a similar concept, but the Presenter DOES know about the UI)
By having the ViewModel not know about the UI at all, we can write clean code that can easily be debugged and tested using our usual bag of tricks. Such as unit testing, refactoring, static code analysis, dependency injection etc etc...
Times are good!
Except the View Model still doesn't know about the UI. So we know what the UI should look like, but the UI doesn't know, because no one is telling it...
So they added the Binding class. The binding class's job is to watch the ViewModel, and then update the UI whenever something changes in the ViewModel.
In the world of MVVM there have been two different approaches to how the Binding class works.
You have the WPF way of doing things, which is to implement an event that tells the Binding class that the ViewModel has been updated. This is fast and flexible, but really annoying to write.
And you have the AngularJS way of doing things, which is to poll the ViewModel for updates. This is ridiculously slow and buggy.
If you have been following me thus far, you will note that MVVM defines a flow of data from your Model to your View. A break in any part of this chain will make it "not work".
It all so COMPLICATED, why bother?
The only reason I've found that justifies MVVM's excessive complexity is that you can write a GUI which you can have 90% test coverage, as the view only covers a tiny part of your program.
If you think automated testing is overrated, then you shouldn't use MVVM.
I am also pretty new to WPF, C# and MVVM. I have read quite a fair bit for these two to three months, so maybe I'll share what I understood.
You seem to have the same question that I had a week or two ago. Data should not be stored in the model. Models are just data structures. Databases (or simulated alternatives like Lists) are the actual storages that store these data. Some people would simply keep them in ViewModel, while some would move them to something outside of MVVM (e.g. a "Repository" class).
You are doing it all wrong. In MVVM, Views do not interact with ViewModels in this manner - they interact via Commands and Bindings. The fact that your View is directly manipulating the list means that it's definitely wrong.
Example:
View (Window):
<Window.Resources>
<vm:MyViewModel x:Key="MyVM" />
</Window.Resources>
<Window.DataContext>
<StaticResourceExtension ResourceKey="MyVM" />
</Window.DataContext>
<DataGrid ItemsSource="{Binding PeopleList}" ..... >
<Button Command="{Binding StoreCommand}" .... >
ViewModel:
public static readonly DependencyProperty PeopleListProperty =
DependencyProperty.Register("PeopleList",
typeof(ObservableCollection<Person>),
typeof(ViewModel));
public ObservableCollection<Person> PeopleList
{
get
{
return GetValue(PeopleListProperty) as ObservableCollection<EmissionEntry>;
}
set
{
SetValue(PeopleListProperty, value);
}
}
private ICommand _storeCommand;
public ICommand StoreCommand
{
get
{
if (_storeCommand == null)
_storeCommand = new MyCommandImplementation();
return _storeCommand;
}
}
Person is your model class with name/age etc. The list is kept in ViewModel, unless you want to have a repository somewhere.
You probably haven't really read anything about ICommand, so I suggest reading it up first. It is too long to give a tutorial here, but you can ask questions after you have read up some.

How to catch an exception thrown during a PropertyChanged event

I am using MVC\MVVM and WPF. I have a form that is bound to a model and a controller that is catching the PropertyChanged events and evaluating business rules and validations.
Well, I want to pop-up a message when a validation is false telling the user what is wrong and how to fix it. But I'm not sure the "correct" way from the controller. I'd like to throw an exception that can be caught by the view but I can't figure out how.
I've tried Dispatcher.Invoke() but that just got me an unhandled exception at the application level.
How do I trap exceptions that are generated from the PropertyChanged event handler in the Controller?
EDIT: Specifically I have a combobox that has a list of discounts in it. I can't allow an inappropriate selection, but I have to notify the user why the selection is inappropriate. This is not as obvious as a textbox that has integers in it. I need to tell the user the date the customer filled out a survey. They can't use that discount twice. I don't want to exclude the survey discount from the list since that would just look like an error to the user. I need to show them the discount and tell them the customer has used that discount and cannot use it again.
EDIT 2: I have looked over the ValidationRule class and since I have to use a database lookup I don't see how I'm going to keep everything in the Model and still have the business rules in the Controller. I've looked at IDataErrorInfo but that requires me to wrap my Model in my Controller and bind to the Controller instead, but only for one field. I think the best course of action in this case is to have the Controller call a method on the View and pop-up a message.
You are walking down the wrong path.
A good way to handle validation in MVVM is by implementing IDataErrorInfo and setting ValidatesOnDataErrors to true on your bindings. You will also almost certainly want to enable ValidatesOnExceptions for completeness, and NotifyOnValidationError so that the binding engine triggers the Validation.Error attached event on the control that you have bound the property to.
For more details, see the Validation section on the MSDN documentation for data binding in WPF.
Some tips:
.NET 4.5 introduces INotifyDataErrorInfo and the corresponding ValidatesOnNotifyDataErrors binding property that provide beefed-up validation functionality in comparison to IDataErrorInfo; you might want to look into that.
You do not need to actually do anything meaningful inside IDataErrorInfo.Error because it is used by the Windows Forms infrastructure and is ignored in WPF. You can even have the getter throw NotImplementedException.
There is good reading material illustrating this approach, complete with examples and code, here and here.
Update: clarification and example code
This validation model does not involve implementing a ValidationRule yourself at all; your models (i.e. the binding sources) simply need to implement one of the two interfaces. How to implement the interface is entirely up to you; in a past project I implemented basic async validation with
public interface IDelegatedValidation : IDataErrorInfo
{
/// <summary>
/// Occurs when validation is to be performed.
/// </summary>
event EventHandler<DelegatedValidationEventArgs> ValidationRequested;
}
public class DelegatedValidationEventArgs : EventArgs
{
public DelegatedValidationEventArgs(string propertyName)
{
this.PropertyName = propertyName;
}
public string PropertyName { get; private set; }
public bool Handled { get; set; }
public string ValidationError { get; set; }
}
The model implements IDelegatedValidation by exposing an event and with
string IDataErrorInfo.this[string columnName]
{
get { return this.GetValidationError(columnName); }
}
private string GetValidationError(string propertyName)
{
var args = new DelegatedValidationEventArgs(propertyName);
this.OnValidationRequested(args);
return args.ValidationError;
}
protected virtual void OnValidationRequested(DelegatedValidationEventArgs args)
{
var handler = this.ValidationRequested;
if (handler == null) {
return;
}
foreach (EventHandler<DelegatedValidationEventArgs> target in handler.GetInvocationList()) {
target.Invoke(this, args);
if (args.Handled) {
break;
}
}
}
So the workflow goes like this:
When the model is about to be wrapped by a viewmodel, some appropriate entity that can perform the validation subscribes to its ValidationRequested event.
The view binds to the model; at some point validation is triggered.
The model calls GetValidationError.
Event handlers get invoked one by one; the first handler that produces a validation error sets args.ValidationError and args.Handled to true so that the chain is stopped.
The validation error is returned to the view.
There is an added twist in that if the viewmodel needs to know whether its model is valid or not (e.g. to enable/disable a "save" command) it needs to also tap into this process.
There's really nothing that you cannot do with IDataErrorInfo / INotifyDataErrorInfo because how you implement them is entirely up to you. Definitely have a look at the Silverlight version of the latter's documentation for more examples; there is also a lot of helpful material like this on the internet.

ViewModels opening dialogue

Is anything really wrong with a ViewModel opening additonal dialogues? Lets say I have a MainView and a MainViewModel. The MainViewModel is the datacontext for the MainView and does not, in fact, know or have any dependency on the mainview itself.
However, there are cases when the main view need to open dialogues that will affect the ViewModel data. For example, I may want show a dialogue and diplay some items to allow the users to select from. So, what I have settled on is this:
In my ViewModel, I have the following methods: AddItem, EditItem, and DeleteItem. However, in order to supply the items to add or edit, I need to present a list in some dialogue for the user to to choose from. Right now I have the ViewModel doing this only because I don't want to implement additional levels of abstraction for such simple tasks. Having the ViewModel do this means it can provide the list to be displayed to the user and, when the user finishes editing or selecting, it can easily update its member collections/properties.
Should I be shot for settling with this approach?
Shot? No. But there are good reasons for not doing this.
First, it kills testability of your ViewModel, as there's now a visual component in place. When you try to write automated unit tests against it, you'll still have to interact with it. You could mock it out, but it becomes more difficult to do so when you're calling UI methods.
Second, your viewmodel shouldn't care about what gets displayed. There's a real "separation of concern" issue when you start combining these things.
Third, it just has a "code smell."
There are a few things you can do to circumvent this issue. The first thing I would suggest is Don't use dialogs. Dialogs have their place, but programmers tend to overuse them. Rethink your design, and try to figure out how you can get the job done without interrupting the user.
Second, consider using a messaging framework to send messages between your viewmodel and view to do the navigation to the dialogs (if you absolutely have to use them). Messages are very easy to mock out and/or write unit tests around.
the easy way to do this: use a dialogservice - easy to use, easy to unittest!
see this.
I don't see any problems with ViewModels communicating with each other. The problem is if they start accessing the Views or other Dialogs since that will affect the systems testability.
If you really want a more loosely coupled system you could use some sort of messaging system for communication, but I doubt you need that here :-)
I always use a Seelctor service(just a basic dialog service) to do this - it's testable and mockable and keeps the code very SOLID.
class ViewModel
{
public ICommand ShowListSelectorForCounterparties { get; set; }
public IListSelectorService ListSelector { get; set; }
public void OnExecuteShowCounterpartySelector()
{
this.Counterparty = this.ListSelector.Select<Counterparty>();
}
}
where IListSelectorService can, at runtime, instantiate your dialog, present your list and return the selected item. The main good thing about running it this way is that your unit tests can mock the IListSelectorService.
I'm not sure if you are still looking for any help, but the approach that I have taken when it comes to dialogs is to have the view model raise an event that the view can then handle. The view can now do whatever it wants to get the data to the view model, so you can disply the dialog in the view without a problem. You pass the response from the dialog to the EventArgs of your event so that the view model has the data it is looking for in order to proceed.
For example:
Public Class View
Private WithEvents _VM AS new ViewModel()
Private Sub _VM_AddingItem(Sender AS Object, E AS ViewModel.ItemEventArgs)
Dim Dialog As new SomeDialog()
If Dialog.ShowDialog then
E.Item = Dialog.Item
Else
E.Cancel = True
End If
End Sub
End Class
Public Class ViewModel
Public Sub AddItem(Item AS Object)
Do Some Work here
End Sub
Private Sub _AddItem()
Dim Args AS New ItemEventArgs()
OnAddingItem(Args)
If not Args.Cancel Then AddItem(Args.Item)
End Sub
Protected Sub OnAddingItem()
RaiseEvent AddingItem(me, ItemEventArgs)
End Sub
Public Event AddingItem(Sender AS Object, E As ItemEventArgs)
Public Class ItemEventArgs
Public Property Item AS Object
Public Property Cancel AS Boolean = false
End Class
End Class
Then just wire up your command to the private _AddItem method which just raises the event to collect the necessary data for the AddItem method. I hope this helps :)

MVVM: Binding to Model while keeping Model in sync with a server version

I've spent quite some time to try and find an elegant solution for the following challenge. I've been unable to find a solution that's more than a hack around the problem.
I've got a simple setup of a View, ViewModel and a Model. I will keep it very simple for the sake of explanation.
The Model has a single property called Title of type String.
The Model is the DataContext for the View.
The View has a TextBlock thats databound to Title on the Model.
The ViewModel has a method called Save() that will save the Model to a Server
The Server can push changes made to the Model
So far so good. Now there are two adjustments I need to make in order to keep the Model in sync with a Server. The type of server is not important. Just know that I need to call Save() in order to push the Model to the Server.
Adjustment 1:
The Model.Title property will need to call RaisePropertyChanged() in order to translate changes made to the Model by the Server to the View. This works nicely since the Model is the DataContext for the View
Not too bad.
Adjustment 2:
Next step is to call Save() to save changes made from the View to the Model on the Server. This is where I get stuck. I can handle the Model.PropertyChanged event on the ViewModel that calls Save() when the Model gets changed but this makes it echo changes made by the Server.
I'm looking for an elegant and logical solution and am willing to change my architecture if it makes sense.
In the past I 've written an application that supports "live" editing of data objects from multiple locations: many instances of the app can edit the same object at the same time, and when someone pushes changes to the server everyone else gets notified and (in the simplest scenario) sees those changes immediately. Here's a summary of how it was designed.
Setup
Views always bind to ViewModels. I know it's a lot of boilerplate, but binding directly to Models is not acceptable in any but the simplest scenarios; it's also not in the spirit of MVVM.
ViewModels have sole responsibility for pushing changes. This obviously includes pushing changes to the server, but it could also include pushing changes to other components of the application.
To do this, ViewModels might want to clone the Models they wrap so that they can provide transaction semantics to the rest of the app as they provide to the server (i.e. you can choose when to push changes to the rest of the app, which you cannot do if everyone directly binds to the same Model instance). Isolating changes like this requires still more work, but it also opens up powerful possibilities (e.g. undoing changes is trivial: just don't push them).
ViewModels have a dependency on some kind of Data Service. The Data Service is an application component that sits between the data store and the consumers and handles all communication between them. Whenever a ViewModel clones its Model it also subscribes to appropriate "data store changed" events that the Data Service exposes.
This allows ViewModels to be notified of changes to "their" model that other ViewModels have pushed to the data store and react appropriately. With proper abstraction, the data store can also be anything at all (e.g. a WCF service in that specific application).
Workflow
A ViewModel is created and assigned ownership of a Model. It immediately clones the Model and exposes this clone to the View. Having a dependency on the Data Service, it tells the DS that it wants to subscribe to notifications for updates this specific Model. The ViewModel does not know what it is that identifies its Model (the "primary key"), but it doesn't need to because that's a responsibility of the DS.
When the user finishes editing they interact with the View which invokes a Command on the VM. The VM then calls into the DS, pushing the changes made to its cloned Model.
The DS persists the changes and additionally raises an event that notifies all other interested VMs that changes to Model X have been made; the new version of the Model is supplied as part of the event arguments.
Other VMs that have been assigned ownership of the same Model now know that external changes have arrived. They can now decide how to update the View having all pieces of the puzzle at hand (the "previous" version of the Model, which was cloned; the "dirty" version, which is the clone; and the "current" version, which was pushed as part of the event arguments).
Notes
The Model's INotifyPropertyChanged is used only by the View; if the ViewModel wants to know whether the Model is "dirty", it can always compare the clone to the original version (if it has been kept around, which I recommend if possible).
The ViewModel pushes changes to the Server atomically, which is good because it ensures that the data store is always in a consistent state. This is a design choice, and if you want to do things differently another design would be more appropriate.
The Server can opt to not raise the "Model changed" event for the ViewModel that was responsible for this change if the ViewModel passes this as a parameter to the "push changes" call. Even if it does not, the ViewModel can choose to do nothing if it sees that the "current" version of the Model is identical to its own clone.
With enough abstraction, changes can be pushed to other processes running on other machines as easily as they can be pushed to other Views in your shell.
Hope this helps; I can offer more clarification if required.
I would suggest adding Controllers to the MVVM mix (MVCVM?) to simplify the update pattern.
The controller listens for changes at a higher level and propagates changes between the Model and ViewModel.
The basic rules to keep things clean are:
ViewModels are just dumb containers that hold a certain shape of data. They do not know where the data comes from or where it is displayed.
Views display a certain shape of data (via bindings to a view model).
They do not know where the data comes from, only how to display it.
Models supply real data. They do not know where it is consumed.
Controllers implement logic. Things like supplying the code for ICommands in VMs, listening for changes to data etc. They populate VMs from Models. It makes sense to have them listen for VM changes and update the Model.
As mentioned in another answer your DataContext should be the VM (or property of it), not the model. Pointing at a DataModel makes it hard to separate concerns (e.g. for Test Driven Development).
Most other solutions put logic in ViewModels which is "not right", but I see the benefits of controllers overlooked all the time. Darn that MVVM acronym! :)
binding model to view directly only works if the model implement INotifyPropertyChanged interface. (eg. your Model generated by Entity Framework)
Model implement INotifyPropertyChanged
you can do this.
public interface IModel : INotifyPropertyChanged //just sample model
{
public string Title { get; set; }
}
public class ViewModel : NotificationObject //prism's ViewModel
{
private IModel model;
//construct
public ViewModel(IModel model)
{
this.model = model;
this.model.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);
}
private void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Title")
{
//Do something if model has changed by external service.
RaisePropertyChanged(e.PropertyName);
}
}
//....more properties
}
ViewModel as DTO
if Model implements INotifyPropertyChanged(it depends) you may use it as DataContext in most cases. but in DDD most MVVM model will be considered as EntityObject not a real Domain's Model.
more efficient way is to use ViewModel as DTO
//Option 1.ViewModel act as DTO / expose some Model's property and responsible for UI logic.
public string Title
{
get
{
// some getter logic
return string.Format("{0}", this.model.Title);
}
set
{
// if(Validate(value)) add some setter logic
this.model.Title = value;
RaisePropertyChanged(() => Title);
}
}
//Option 2.expose the Model (have self validation and implement INotifyPropertyChanged).
public IModel Model
{
get { return this.model; }
set
{
this.model = value;
RaisePropertyChanged(() => Model);
}
}
both of ViewModel's properties above can be used for Binding while not breaking MVVM pattern (pattern != rule) it really depends.
One more thing..
ViewModel has dependency on Model. if Model can be changed by external service/environment. it's "global state" that make things complicate.
If your only problem is that changes from the server get immediately re-saved, why not do something like the following:
//WARNING: typed in SO window
public class ViewModel
{
private string _title;
public string Title
{
get { return _title; }
set
{
if (value != _title)
{
_title = value;
this.OnPropertyChanged("Title");
this.BeginSaveToServer();
}
}
}
public void UpdateTitleFromServer(string newTitle)
{
_title = newTitle;
this.OnPropertyChanged("Title"); //alert the view of the change
}
}
This code manually alerts the view of the property change from the server without going through the property setter and therefore without invoking the "save to server" code.
The reason you have this problem is because your model doesn't know whether it is dirty or not.
string Title {
set {
this._title = value;
this._isDirty = true; // ??!!
}
}}
The solution is to copy the server changes via a separate method:
public void CopyFromServer(Model serverCopy)
{
this._title = serverCopy.Title;
}

The best approach to create new window in WPF using MVVM

In the neighbour post: How should the ViewModel close the form?
I've posted my vision how to close windows with MVVM usage. And now I have a question: how to open them.
I have a main window (main view). If user clicks on the "Show" button then "Demo" window (modal dialog) should be displayed. What is a preferable way to create and open windows using MVVM pattern? I see two general approaches:
The 1st one (probably the simplest). Event handler "ShowButton_Click" should be implemented in the code behind of the main window in way like this:
private void ModifyButton_Click(object sender, RoutedEventArgs e)
{
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
if (res != null && res.Value)
{
// ... store changes if neecssary
}
}
If we "Show" button state should be changed (enabled/disabled) we will need to add logic that will manage button state;
The source code is very similar to "old-style" WinForms and MFC sources - I not sure if this is good or bad, please advise.
Something else that I've missed?
Another approach:
In the MainWindowViewModel we will implement "ShowCommand" property that will return ICommand interface of the command. Comman in turn:
will raise "ShowDialogEvent";
will manage button state.
This approach will be more suitable for the MVVM but will require additional coding: ViewModel class can't "show dialog" so MainWindowViewModel will only raise "ShowDialogEvent", the MainWindowView we will need to add event handler in its MainWindow_Loaded method, something like this:
((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
(ShowDialog - similar to the 'ModifyButton_Click' method.)
So my questions are:
1. Do you see any other approach?
2. Do you think one of the listed is good or bad? (why?)
Any other thoughts are welcome.
Thanks.
Some MVVM frameworks (e.g. MVVM Light) make use of the Mediator pattern.
So to open a new Window (or create any View) some View-specific code will subscribe to messages from the mediator and the ViewModel will send those messages.
Like this:
Subsription
Messenger.Default.Register<DialogMessage>(this, ProcessDialogMessage);
...
private void ProcessDialogMessage(DialogMessage message)
{
// Instantiate new view depending on the message details
}
In ViewModel
Messenger.Default.Send(new DialogMessage(...));
I prefer to do the subscription in a singleton class, which "lives" as long as the UI part of the application does.
To sum up: ViewModel passes messages like "I need to create a view" and the UI listens to those messages and acts on them.
There's no "ideal" approach though, for sure.
I was thinking about this issue recently too. Here's an idea I had if you use Unity in your project as a 'container' or whatever for dependency injection. I guess normally you'd override App.OnStartup() and create your model, view model, and view there, and give each the appropriate references. Using Unity, you give the container a reference to the model, then use the container to 'resolve' the view. The Unity container injects your view model, so you never directly instantiate it. Once your view is resolved, you call Show() on it.
In an example video I watched, the Unity container was created as a local variable in OnStartup. What if you created it as a public static readonly property in your App class? You could then use it in your main view model to create your new windows, automatically injecting whatever resources the new view needs. Something like App.Container.Resolve<MyChildView>().ShowDialog();.
I suppose you could somehow mock the result of that call to the Unity container in your tests. Alternatively, perhaps you could write methods like ShowMyChildView() in the App class, which basically just does what I described above. It might be easy to mock a call to App.ShowMyChildView() since it would just return a bool?, eh?
Well, that might not really be any better than just using new MyChildView(), but it's a little idea I had. I thought I'd share it. =)
I'm a bit late, but I find existing answers insufficient. I will explain why. In general:
it's ok to access ViewModels from View,
it's wrong to access Views from ViewModels, because it introduces circular dependency and makes the ViewModels hard to test.
Benny Jobigan's anwer:
App.Container.Resolve<MyChildView>().ShowDialog();
this actually does not solve anything. You are accessing your View from ViewModel in a tigtly coupled fashion. The only difference from new MyChildView().ShowDialog() is that you went trough a layer of indirection. I don't see any advantage over directly calling the MyChildView ctor.
It would be cleaner if you used interface for the view:
App.Container.Resolve<IMyChildView>().ShowDialog();`
Now the ViewModel is not tigtly coupled to the view. However I find it quite impractical to create interface for each view.
arconaut's anwer:
Messenger.Default.Send(new DialogMessage(...));
it's better. It seems like Messenger or EventAggregator or another pub/sub patterns are universal solution for everyhing in MVVM :) The disadvantage is that it's harder to debug or to navigate to DialogMessageHandler. It's too indirect imho. For example, how would you read output form the Dialog? by modifying DialogMessage?
My Solution:
you can open window from MainWindowViewModel like this:
var childWindowViewModel = new MyChildWindowViewModel(); //you can set parameters here if necessary
var dialogResult = DialogService.ShowModal(childWindowViewModel);
if (dialogResult == true) {
//you can read user input from childWindowViewModel
}
DialogService takes just dialog's ViewModel, so your viewmodels are totally independent from Views. At runtime, DialogService can find appropriate view (using naming convention for example) and shows it, or it can be easily mocked in unit tests.
in my case I use this interfaces:
interface IDialogService
{
void Show(IDialogViewModel dialog);
void Close(IDialogViewModel dialog);
bool? ShowModal(IDialogViewModel dialog);
MessageBoxResult ShowMessageBox(string message, string caption = null, MessageBoxImage icon = MessageBoxImage.No...);
}
interface IDialogViewModel
{
string Caption {get;}
IEnumerable<DialogButton> Buttons {get;}
}
where DialogButton specifies DialogResult or ICommand or both.
Take a look at my current MVVM solution for showing Modal Dialogs in Silverlight.
It solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.
See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4
I use a controller which handles all information passing between views. All viewmodels use methods in the controller to request more information which can be implemented as dialogs, other views etc.
It looks something like this:
class MainViewModel {
public MainViewModel(IView view, IModel model, IController controller) {
mModel = model;
mController = controller;
mView = view;
view.DataContext = this;
}
public ICommand ShowCommand = new DelegateCommand(o=> {
mResult = controller.GetSomeData(mSomeData);
});
}
class Controller : IController {
public void OpenMainView() {
IView view = new MainView();
new MainViewModel(view, somemodel, this);
}
public int GetSomeData(object anyKindOfData) {
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
...
}
}
My approach is similar to adrianm’s. However, in my case the Controller never works with the concrete View types. The Controller is completely decoupled of the View - in the same way as the ViewModel.
How this works can be seen in the ViewModel example of WPF Application Framework (WAF).
.
Best Regards,
jbe

Categories