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
Related
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 read about "ViewModel to ViewModel navigation" and "View Model Lifecycle" from here:
https://github.com/MvvmCross/MvvmCross/wiki/ViewModel--to-ViewModel-navigation
https://github.com/MvvmCross/MvvmCross/wiki/View-Model-Lifecycle
I can use Init() or Start() methods to initialise current ViewModel.
Also I can pass parameters from one ViewModel to another and receive it in the Init() method.
So my question:
When I created windows phone apps I used "NavigateTo" and "NavigateFrom" methods.
Init() is similar to "NavigateTo".
But I didn't find alternative for "NavigateFrom" method in mvvmcross and I don't know how to reload data when I move 'back' by "Close(this)" or using back button on windows phone.
Could you hint me?
Thanks in advance!
updated
I found out that Messenger (MvvmCross plugin) can help me with informing first ViewModel, when an other second ViewModel has changed data (for example add an item to a collection).
So when the second ViewModel add a new item, first ViewModel reloads the data in the OnCollectionChanged(CollectionChangedMessage obj) method.
Stuart showed how to work with this plugin in the NPlus1DaysOfMvvmCross/N-13-CollectABull-Part2.
Link here: https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-13-CollectABull-Part2
But I didn't find alternative for "NavigateFrom" method in mvvmcross and I don't know how to reload data when I move 'back' by "Close(this)" or using back button on windows phone.
In general, you don't need to reload data in this event - because the ViewModel is already created and initialised from the previous navigation in the forwards direction.
If you do want to do some refresh of the ViewModel when navigating back, then the IVisible pattern in the N=42 video may help but you'll need to add this to your View and ViewModel yourself - see http://slodge.blogspot.co.uk/2013/11/n42-is-my-viewmodel-visible-can-i-kill.html
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.
Another subject and a difficulty along with it surfaced while playing with WPF and it lies in an attempt to call a function from within a dynamically added user control passing it a value.
For current example I have a user control that has a property "Secret" and a button in it named "PublishSecret", constructor sets the value of the Secret to a random number. In MainWindow I have another button named "AddSecretContainer" that adds to the window an additional instance of this user control, next to it I have a textbox named "PublishedSecret". A simple method in MainWindow also was defined, here it is:
public void PublishSecret(int secret)
{
this.PublishedSecret = secret;
}
Now without passing this Secret I could use routed commands with their bubbling effect. But how can I call PublishSecret providing it with the local value of Secret from user control when PublishSecret button was clicked?
If your UserControl was added dynamically, then I assume that you have access to a reference of it in your main view model/code behind. This makes your situation nice and simple. You said there is a Button named PublishSecret, well I also assume that clicking it initiates some functionality.
My first suggestion would be the simplest. Put that functionality into a public method which would be called from your Click or ICommand handler and then simply call that method from your main view model:
childUserControl.PublishSecret();
Please let me know if I have misunderstood your problem.
UPDATE >>>
Ok sorry, I've got you now. You can declare a delegate, add a property of that type to your child UserControl and attach a handler in the main view model. Then you can call the delegate and in the handler in the parent view model, you can call your method. For more information on this technique, please see my answer to the Passing parameters between viewmodels question.
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
}