I'm new the the MVVM pattern.
The view has a login button and a progress bar.
I have a view model called LoginViewModel which exposes the command LoginCommand and is hooked up to my view LoginPage.xaml.
When the login command is executed the Login button should be disabled and the progress bar should become visible.
If login fails the the Login button should become enabled and the progress bar should be hidden.
What I am unsure about is where this presentation logic should happen. Should it happen in the view mode or in the code behind of the page?
Currently I have a boolean property on the view model called LoggingIn which is set to true when the login process begins and then false if it fails. This boolean is the hooked to the IsEnabled and Visibility property of the button and progress bar, respectively.
This felts wrong to me so I tried making a couple of events, OnBeginLogin and OnEndLogin and hooking these up in the code behind of the page which code to control the visual state of the controls. This however required much more code than the previous solution.
I also though that I could expose two properties in the view model which are specific to the controls, LoginButtonEnabled and ProgressBarVisible so that I can control the visual state from the view model. But if I add a cancel button let say, then I would need to add another property called CancelButtonEnabled.
I think that the presentation logic should not be handled in the view model so adding an event seems to be the best solution but I'm wondering what is the best practice or standard/common way of doing it?
Also when login is successful, should returning the user to the previous page or another page be handled in the view model or in the code behind of the page? again I feel that this isn't something for the view model but I'm not sure.
Thanks for your help.
I'm not sure why it feels wrong to you, but your first approach seems the most correct to me. The view model is correctly exposing the application's state to the view, and then you're using data binding to control how this translates to presentation.
Related
I am trying to setup this scenario.
The basic premise is this. I have a message window, with couple of text fields and button, controlled by a viewmodel and a model. Originally window is displayed with showdialog() with button invisible while some background checks are going on.
If a an error occurs i would like the text in the window to change accordingly and a button become visible. I would also like this button to execute a specific action, a delegate or a static somewhere.
I would like to be able to pass this action to the viewmodel to be executed. The reason is i want to be reuse this window for different screen and button actions may change.
Thank you.
1.Define a command in your ViewModel(as a property with INotifyPropertyChanged).
Bind your button to this command:
Command={Binding MyCommand}
where MyCommand=property of yours viewmodel.
In case you need to change command action, just set property MyCommand to the implementation of command you are currently in need of.
Once I found out MVVM I really liked it. The whole concept of binding, separating view from logic, testability, etc was very encouraging. It was a nice alternative for the messy, never-ending code behind. Than I learned there are commands that can be bound, which I also liked at first.
Having written a couple of controls using MVVM though I found out that my view models start to look more or less like code behind. Full of commands that did almost exactly what was previously done in code behind by event handlers.
Let me give you some examples.
There is a control with a button "Details" which opens another window.
[approach 1]
The first (and worst) thing you can do is call something like this in your command:
new DetailsWindow().ShowDialog();
That makes the view model strongly reference the presentation layer - ugly.
[approach 2]
Lets fix this problem using a weak reference and create something like IDialogService. We can inject a simple implementation that creates and opens the window. Now we got rid of the strong reference to the presentation layer and the command can look like this:
_dialogService.ShowDetailsWindow();
I still don't like this approach. To me it feels like the view model is not something that should decide whether to show a window or not. It should serve and handle data.
[approach 3]
The elegant way to totally separate the view model from the presentation layer would be to inject the command itself. Than the view model would not be aware of presentation layer. It would only execute the injected action - no matter what it is.
Question 1:
Which approach would be best? I guess the number 3 is the winner.
Question 2:
Should this even be a part of the view model? I think it shouldn't as it seems to be the concern of the presentation layer. Maybe it's best to put it in a simple event handler in code behind?
The second example is more complex. In the same control we have a "Delete" button. It should open a dialog asking user for confirmation and if he says 'yes' it should delete something. In this case it makes more sense to put it in the view model as it really affects the data.
Question 3
This case is the most tricky for me. I can't use my favorite approach number 3, because I have to display a dialog which is a presentation layer's job, but also I have to perform some logic depending the dialog's result - which on the other hand is view model's job. What is the best approach here?
Please bare in mind that I really don't want to use approaches 1 and 2. I want the view model to be clean and unaware of anything related to the presentation layer - not even by weak references.
One thing that comes to my mind it to split the view model layer into two layers. Something like:
view --> presentation view model --> logic view model
presentation view model
used as a control's context
contains logic view model as a public property for direct bindings
uses approach number 2 - now it's acceptable as the whole class is meant to perform presentation related actions
logic view model
it is 'presentation free'
references specialized logic services
some commands could be bound directly to the view
some commands could be executed by the presentation view model which owns it
Maybe this is the right way to go?
[EDIT]
In response to the suggestions in comments about using a framework:
Using a framework will surely make it easier to handle windows, but it's not the solution to the problem. I don't want the 'logic view model' to handle windows at all, not even with a help of a framework. Referring to the approach I suggested at the end I would still put it in the 'presentation view model'
Your ViewModel should just notify all the subscribers that the command was executed by firing a simple event. The View should subscribe to that event and handle the event by displaying a new Window...
ViewModel:
public event EventHandler<NotificationEventArgs<string>> DisplayDetailsNotice;
private DelegateCommand displayDetailsCommand;
public DelegateCommand DisplayDetailsCommand
{
get { return displayDetailsCommand ?? (displayDetailsCommand = new DelegateCommand(DisplayDetails)); }
}
public void DisplayDetailsWindow()
{
//
Notify(DisplayDetailsNotice, new NotificationEventArgs<string("DisplayDetails"));
}
View (note that the VM is already the DataContext of the View):
private readonly MyViewModel mvm;
//Get reference to VM and subscribe to VM event(s) in Constructor
mvm = DataContext as MyViewModel;
if (mvm == null) return;
mvm.DisplayDetailsNotice += DisplayDetails;
private void DisplayDetails(object sender, NotificationEventArgs<string> e)
{
DetailsWindow = new DetailsWindow { Owner = this };
DetailsWindow.ShowDialog();
}
This way, when the command is executed the VM raises the event and the View which is subscribed to VM events handles the event by displaying the details windows using the ShowDialog() method. Since all the VM does is publish an event it remains agnostic of and decoupled from the View leaving the MVVM pattern intact. Since VM is already the DataContext of the View and the View binds to properties of the VM as it is obtaining a reference to the VM is also not breaking the MVVM pattern...
Note that this syntax is valid with Simple MVVM Toolkit MVVM framework. As suggested in comments you should start using an MVVM framework (my recommendations are Simple MVVM Toolkit or MVVM Light, Caliburn is too complex for no reason and no gain IMO) in order to avoid having to deal with the plumbing yourself...
I am quite new to MVVM, so sorry for probably simple question. However, I can not understand which mechanism from MVVVM (I am using MVVMLight if that is of any consequence) to use in order to program the simple following scenario:
I have textbox TB, where user can fill in URL. Than I have a button B and webview WV. If user clicks on button, the app should take the text from TB and display it in the WV.
I knwo that I can create a property in viewmodel and bound it to TB.Text. I understand probably also that I should create command which will be boudn from button B, but what should I do in the command. How I can call WV.navigate(url), when I do not have reference to WV. Should this be solved by something, which I did not grasp correctly like behaviours? What is the best way to do this?
You should use the messenger pattern for this problem:
http://msdn.microsoft.com/en-us/magazine/dn745866.aspx
http://www.codeproject.com/Tips/696340/Thinking-in-MVVMLight-Messenger
http://mytoolkit.codeplex.com/wikipage?title=Messenger
The idea is that the view can register for specific message classes (in this case for example an own NavigateToUriMessage class) and the view model can send an instance of this message class to whoever listens to the message type. In the command implementation you simply send this message, the view receives the message and changes the URI of the web view.
BTW: The idea of this messenger pattern is that you can better write Unit Tests and use the view model for other platforms (where the reaction to the message may differ).
Another way is to create an attached property for the WebView class where you can bind an Uri property to. The attached property calls Navigate when the bound value changes.
Check out this blog:
http://blogs.msdn.com/b/wsdevsol/archive/2013/09/26/binding-html-to-a-webview-with-attached-properties.aspx
I am trying to create a simple onscreen keypad created using buttons (currently a User-control), on those buttons i have a click event, when i click/touch a button i want the value of that button sent to a Text-block in my Main-window.
I can't/don't understand how to make the User-control (keypad) see the Text-block (in Main-window) to add in the value that i need.
I have seen solutions that use command Bindings and solutions that use the visual tree traversing but all of them are the main window accessing the user control, not the other way around.
All the examples are the other way around because that is how a UserControl is supposed to work.
A UserControl is a packaged piece of re-usable functionality. It should not know anything about the code that is using it.
Instead you should expose routed events in your UserControl for things like a when number was selected, and subscribe to them in your main window.
There are many ways to achieve what you want. If your MainWindow.xaml has a UserControl and you want to react to a change from the control in the MainWindow.xaml.cs file, then you could add a delegate to the UserControl code behind and register a handler for it in the MainWindow.xaml.cs file. Implementing new delegates are generally somewhat simpler than implementing RoutedEvents, which is another way that you could handle this situation.
Using a delegate like this will enable you to effectively pass a signal to the main view from the child UserControl code behind, which you can react to in any way you want to. Rather than explain the whole story again here, please see my answers from the Passing parameters between viewmodels and How to call functions in a main view model from other view models? posts here on Stack Overflow for full details on how to achieve this.
I'm having a problem understanding something about MVVM. My application relies on dialogs for certain things. The question is, where should these childwindows originate from? According to MVVM, viewmodels should contain only businesslogic and have zero actual knowledge about UI. However, what other place should I call my childwindows from, considering they're UI elements?
Doesn't this create tight coupling between elements?
Since you tagged the question with Prism, I'll suggest the way I've done it in the past using Prism. Have the IEventAggregator injected into your ViewModel, and then when you want to pop open the dialog, publish a "ShowDialogEvent" or something like that. Then, have another Module called "DialogModule" or whatever, which upon initialization subscribes to that event, and shows the dialog. Furthermore, if you want to pass data back to the original ViewModel, have the ViewModel of the dialog publish a "DialogCloseEvent" or something like that with a payload of the data you need. You can then subscribe to that event back in your main ViewModel.
See Handling Dialogs in WPF with MVVM
In the past, I have accomplished this by using Unity to resolve a custom interface that has a Show() method and a completed event. Then in the ViewModel I would call IScreen screen = container.Resolve<IScreen>(Resources.EditorWindowKey); and then just call screen.Show();.
The big advantage of this is that I can then just simply change my Unity configuration to remove the view when I'm testing my VM's.
The primary route I've been using to do this is to create a command inside your View layer. That command object accepts a parameter that is the ViewModel object that you want to display. The command then finds the appropriate ChildWindow, creates it and displays it with the parameter set as the content or however you will set it up. This way you can just bind a button's command property to that command, and its commandparameter to the object you want to show in the popup and your ViewModel objects never have to care how it's being displayed.
Prompting for user input (like saving a dirty file or something) doesn't work in this scheme. But for simple popups where you manipulate some data and then move on, this works very well.
The ViewModel sample application of the WPF Application Framework (WAF) demonstrates how to show a Modal Dialog.
I would suggest to use a controller in this scenario, say DI'ed dialogController backed up with a dialog shell. The source viewmodel(ie from where the request to open a dialog is originating) will make a call to dialogController.ShowDialog(<<ViewNameToHostInRegion>>,<<RegionName>>).
In Order to transfer the data to and from the dialog and sourceview you can use MessageBus. So essentially when you invoke the ShowDialog() you populate the messagebus, and when the close command of target View(The view hosted in Dialog shell) invoked - say in "Select" button -- Let the target view add/update the messagebus. So that source view model can work with updated data.
It has got many advantages :
1) Your source view works with dialog controller as BlackBox. ie it doesnt aware of what exactly the Dialog view is doing.
2) The view is being hosted in Dialog Shell -- so you can reuse the dialog again and again
3) Unit testing of source view is limited to test the actual functionality of the current viewmodel, and not to test the dialog view\view model.
One heads-up which I came across with this one is, while creating the test cases you may need to write testable Dialog controller which do not show the actual dialog while running the testcases in bunch. So you will need to write a TestableDialogController in which ShowDialog does nothing (Apart from deriving from IDialogController and provide blank implementation of ShowDialog().
Following is the psudeo code :
LocalMessageBus.AddMessage(<MessageKey>,<MessageActualContentAsObject>);
dialogController.ShowDialog(<TargetViewName_SayEmployeeList>);
Employee selectedEmployee = LocalMessageBus.GetMessage(<MessageKey>) as Employee;
if (selectedEmployee != null)
{
//doSomework with selected employee
}