I'm implementing the MVVM pattern in a Silverlight Application that utilizes 3rd party software (ESRI's Silverlight API). This 3rd party control only exists in the XAML:
<esri:Map x:Name="map" ... />
The ViewModel has an ObservableCollection of object which are bound to a ListBox. When a user double clicks on a specific item, the Model (which is bound to a user control inside that ListBox) fires an event which the "Main" view model is subscribed to.
My question is, in the "Main" ViewModel where the esri:Map resides, how do I call functions from this 3rd party control with data from the event (ex. map.ZoomTo( result ); )? The only solution I have so far is to move the event code (in the ListBox user controls) from the ViewModel into the Code-Behind and then subscribe to those events in the "MainPage" xaml and fire the code in the code behind.
Is this the best option utilizing the MVVM pattern??
Sounds like you want either a Blend Trigger or Behavior, depending upon the exact scenario. Here's an introduction to them.
That's a perfectly valid approach for "ViewModel First" MVVM. Another approach is to have the Code-Behind be ViewModel Aware, and directly call methods in the View Model from the event handlers - which is suitable for a "View First" approach to MVVM.
I am a huge fan of separating the whole thing, and using a good MVVM framework like Caliburn.Micro. With a framework like such, you could do something more OOPish, very SOLID, still MVVM, by utilizing a MEF:
Your View would simply trigger events in an Event Aggregator, with a message payload with information to whatever receiver handles it. The View doesn't care who handles the event, all it cares about is to raise the event to the aggregator.
Your View Models would handle whatever events are appropriate for each one. They don't care who raises the event, only that they are in charge of handling events from the Aggregator.
You will find that direct ViewModel first and View first implementations, through event handlers, of MVVM begin to grow cumbersome as your enterprise solition grows: it's scalability is limited.
Related
I am having difficulties in making TabControl run flawlessly in MVVM architecture. Currently what I am doing is having TabControl's ItemsSource property bound to ObservableCollection Screens property. Each time I want to add new tab, I create adequate ViewModel, add it to Screens, and throuh data templates adequate View will be shown.
Problems:
1) it seems that desctructor for my ViewModels are not fired until complete application closes. I am not sure if Data templates are the cause of this. When I remove ViewModel from Screens collection, it should be available for GC, since all I did was added it to Screens collection, which showed the View, and then when command to close the view was issued, I removed from collection. After that I tried to force Gc.Collect, but still dctor fires only on application close. I am not sure why is this happening...
1) in some data structures it is not possible to bind to ViewModel's property through ElementName, so one way around is to use RelativeSource binding. However, this creates lots of binding exceptions when element (ex View) is being closed. Similar problem to mine is described here:
How to avoid Binding Error when parent is removed
In my case TargetNullValue and FallbackValue do not help, and the only way around I have found is to have ViewModel as StaticResource. Problem with this approach is that when using Screens collection and data templates to connect Views and ViewModels, you cannot create viewmodels the usual way:
<UserControl.Resources>
<vm:SomeViewModel x:Key="someViewModel" />
</UserCpntrol.Resources>
So, is there an alternative approach to using TabControl in MVVM scenario, or I am doing something wrong here?
Regarding the destructor part, should not use the destructor. It is the recommended approach to implement the IDisposable interface instead. This will help you to automate the cleanupd of your objects and lets the GC do the dirty work for you:
Use this method to close or release unmanaged resources such as files,
streams, and handles held by an instance of the class that implements
this interface. By convention, this method is used for all tasks
associated with freeing resources held by an object, or preparing an
object for reuse.
My favorite MVVM tutorial uses a Tabcontrol as a central UI control: WPF Apps With The Model-View-ViewModel Design Pattern. This may give you hints to a nice and working approach.
I have a view that containing a user control.
I want to make the view model to notify the user control to do some action (refresh its' data).
I can pace some bool property in my VM and bind it to the user control so it will trigger it, but I think it's a little abuse of the property.
I feel I missing something, and can't find a solution. I will appreciate any comment.
My solution:
I'm going to solve the problem by registering an event of collection changed in my UserControl, since I'm binding to that control a view of a collection thru CollectionViewSource.
My original problem was how to make a chart control inside the UserControl to get updated when I filtering the data source. After the filtering operation from the VM, an event will raise and I will make the chart to get refresh either in the UserContol's code behind
Since you've indicated MVVM tag solution would be pretty straightforward - just bind control to a data by exposing data items/whatever by ViewModel so any data updates would be automatically dispatched to View via bndings if you would provide INotifyPropertyChanged / INotifyCollectionChanged by a ViewModel.
If you've bound your UserControl correctly, you shouldn't need to manually refresh the data since WPF will automatically update the UI when INotifiyPropertyChanged triggers
That said, if you wish to send a message from the ViewModel to the View, I usually use an messaging system of some kind to keep the Views and ViewModels separate. The two I've used and would recommend are MVVM Light's Messenger, or Microsoft Prism's EventAggregator
Either system will allow your ViewModel to broadcast a message, and your View can subscribe to receive these messages and handle them as needed.
There seems to be a lot of "behind the scenes magic" involved in TreeViewDragDropTarget.
All the posts I've seen so far, stop at the presentation of the treeview without explaining what's going on with collection of data the TreeView is bound to.
How does the TreeViewDragDropTargt communicate back to the ViewModel property that my TreeView is bound to? Are there any MVVM oriented walkthroughs or tutorials about TreeView Drag and Drop ala MVVM?
UPDATE:
"If an item is dropped onto the drag drop target, it is added to the nested control if the nested control is bound to an ObservableCollection (or any collection that implements INotifyCollectionChanged and contains the same type of items as the item that was dropped)."
And what that means is that I can hook into the CollectionChanged event of the ObservableCollection my TreeView is bound to keep my model in sync.
Is there a better option?
I haven't seen a better option so far. Remember that the whole point behind the MVC/MVP/MVVM patterns is to put as much code as possible under tests.
For Drag/Drop, I've used the Presentation -> Code Behind -> Messenger Raise message "Items Added" ... and catch the event from the ViewModel.
If there's custom logic in the Code Behind, for example, validate that the "source" is of a valid status for example, or of the right type etc, you could delegate this checking in a "service" (Dependency Injected) that would be specialized in handling these odd cases. The Code Behind becomes miminal, and the "drag service" becomes testable.
Do you need really need a "Drag Service", and if so, should you have one "Drag Service" per ViewModel, or one per application ? I'll answer with your classic answer "It Depends".
I'm using MVVM Light toolkit (which I love). I currently have messaging in place for some interaction originating from the ViewModel and intended for consumption by the View. Typically these types of messages indicate the View should do something like hide itself, show a confirmation message that data was saved, etc.
This works. In the constructor for the View, I register with the Messenger:
Messenger.Default.Register<NotificationMessage<PaperNotification>>(this, n => HandlePaperNotification(n));
When I'm using the Messenger to communicate cross-cutting concerns between ViewModels (like identity), I can see that when the ViewModel is cleaned up in the ViewModelLocator, the base class for ViewModels (ViewModelBase) unregisters any subscribed messages. I don't have to do anything, as MVVM Light Toolkit handles that for me. However, when I use them in the Views, I have to expressly unregister them at Closing time, like so:
Messenger.Default.Unregister(this);
I suppose I could implement a base class for Views to inherit from.
However, it strikes me that perhaps this is a code smell to be using the Messenger in the View... it works, but it might not be the best way. I'm wondering if I should instead create a property on the ViewModel and bind whatever part of the View's elements to it. In the example of hiding a form, a property could be a boolean called "Show". As I think about it, I can see that in many cases this will result in having to write a ValueConverter. One way seems less testable. The other way seems to require much more code and perhaps the introduction of excess ValueConverters, which could become a code smell in themselves.
So (after all that build up) my question is this:
Is it preferable to use messages within the View or is it better to add properties (and potentially ValueConverters) to allow the ViewModel to drive it in a more bindable fashion?
In MVVM. ViewModel comunicates with View through DataBinding and Commands. If you need some other functionality, you need to implement it using this means.
Messaging is supposed to be only for ViewModels. Views are supposed to be "stupid" visualisers of your data in ViewModel.
The Messaging logic in MVVM Light is there for communication between ViewModels. I've never run into any communication between View and ViewModel that I couldn't solve with binding and/or commands. Sometimes I need Value Converters and sometimes I need code in the code-behind, but I've never had to make the ViewModel directly push data to the View.
This is an interesting discussion and I found this thread when I was wondering about view model to view communication. Interestingly, MVVMLight's creator seems to find it perfectly acceptable to send messages from a view model to a view. Another example of differing opinions about what is a good MVVM design.
Sometimes a view model needs to raise notifications, that a view should handle and do something in response, esp. when these can't be modeled as properties and property change notifications.
Anything in MVVM Light that can allow the view to listen to events and translate view model notifications into user interface actions via declarative Xaml markup?
Personally I find the technique of raising events from the VM and catching them in the view acceptable in certain circumstances. I typically prefer to work with the Messenger for such occasions though, especially if you need custom event args (because it is quite a lot of work to declare a new event args class and a new delegate).
Also, the event handler is a tight coupling between view and viewmodel, while you would typically prefer a loose coupling, but if you are aware of that fact and of the consequences, then why not...
Another technique (for example for navigation, dialogs etc) is to declare an interface with the methods you need (for example IDialogService with AskConfirmation and ShowMessage methods). Then have a class implement that interface (that can be the MainWindow/MainPage itself) and pass it to the ViewModel (for example in the View's constructor right after InitializeComponent was called). In the VM, call these methods when needed. This has the advantage of being quite easy to test (simply mock the IDialogService and check that the methods are called).
I typically move between Messenger and IDialogService depending on various factors. I tend to favor the interface based approach lately though, because it is a little easier to test (but the Messenger is quite testable too so YMMV).
Cheers,
Laurent
In a "pure" MVVM solution, the only thing that should connect the View to the ViewModel is Bindings. There is nothing stopping you from casting your DataContext to your ViewModel type and hooking an event in the view, but it kind of defeats the purpose of using the MVVM approach. As an alternative, try to rethink of why you think you need to raise an event to the view:
Need to display a popup? Some variety of bound list of "popup notifications" can be used, with a proper template, to create popups on the view as the viewmodel "inserts" notification objects into the bound collection.
Need to force a drop down open, or some similar UI action? Bind the appropriate property on the UI to a view model property, set the mode to two-way, and set as appropriate on the view model.
etc, etc.
You are right that sometimes the ViewModel needs to communicate with the View. One way to do this is that the ViewModel raises a CLR event which the View listens to. This can be done in the code-behind of the View.
MVVM is not about eliminating the code-behind of the Views! It’s about separation of concerns and improving the testability with unit tests.
Another way to enable the communication between the ViewModel and the View is by introducing an interface (IView). More information about this approach can be found on the WPF Application Framework (WAF) project site.
There is indeed a supported technique in MVVMLight for handling sending Messages from your ViewModel to your View. Look inside the GalaSoft.MvvmLight.Messaging namespace. There is a better way to send dialod messages then the below sample, however this is just a quick example.
Example
ViewModel
public MainPageViewModel()
{
Messenger.Default.Send("Payment");
}
View
public MainPage()
{
Messenger.Default.Register<string>(this, DialogRequested);
}
private DialogRequested(string message)
{
MessageBox.Show(message);
}