views:

481

answers:

3

I am writing an app using the MVVM (Model-View-ViewModel) pattern and am leveraging the Prism and Unity bits from the Microsoft P&P team.

I have a View with a list of items. These items are contained with an ObservableCollection in the ViewModel to which a listbox in the View is databound (the ViewModel is set as the DataContext of the View). In the ViewModel, I have a timer running that fires a poll of the server for new data every 30 seconds. When the data returns I marshal it over to the UI thread and add the new data to the ObservableCollection. This all works really well.

The problem I have is that I need the timer to stop when the view is closed. I am not sure what event to watch for here. Is there something in Unity that will tell me when the view has been replaced in the region? Is there an event which would be best to use for this from the view, and perhaps pass that event (or a facade) on to the ModelView? My View is a UserControl per P&P examples. There is no "Unload" event I could find nor a method to override.

I am already thinking of writing my own service to handle view changes (some sort of facade for the RegionManager), and might just implement a common interface on my Views to do cleanup or implement IDisposable on them when they are removed from a view. However, if there is a clean way of doing this with the confines of the core Silverlight framework or Unity/Prism, I would prefer to take that path.

Edit - Answer:

I ended up marking Anderson Imes's answer simply by picking the one that was closest to what I am doing for my solution. But really, I am using parts from from PL and GraemeF as well and up-voted everyone. This was a great response for me, as it gave me some better insight into the regions, gave me another framework to look at, and verified that I was probably going down the right path with implementing a service to handle view changes instead of just calling into RegionManager.

+2  A: 

Caliburn addresses this missing piece of Prism with its IPresenter Component Model and works well with Prism. You would implement the IPresenter interface (or more likely use one of the base classes) on your ViewModels, and stop the timer in OnShutdown and/or OnDeactivate.

You could either use Caliburn yourself, or have a look and see how it is implemented.

GraemeF
Looking into Caliburn now. Its funny you recommend a framework with presenters, as that is what I am actually calling my ViewModel classes since I have done MVP before (own grown framework).
Jason Jackson
+1  A: 

I had this exact problem and ended up basically creating an interface for this:

public IApplicationEvents
{
     void OnClose();
}

I centralized my close view code and basically just looked for this interface before removing the view. If it was there, I could call it (I checked both the View itself and also the DataContext property if it was a UIElement).

I gussied it up a little using an attached property and the EventAggregator, but this is the basic idea and it works well.

You are right to need to shut this down when you view closes, especially if you are using DispatcherTimer (as you should). This thing causes some really bad memory leaks if you don't.

Anderson Imes
I am actually using System.Threading.Timer. As part of the dependency injection I am passing around an IDispatchBroker, which is just an interface I wrote that does thread dispatching. When the app bootstraps this dependency to use a really simple facade that just calls the RootVisual's dispatcher. When I am unit testing I set up a mock IDispatcherBroker that just calls straight through instead of doing the whole "BeginInvoke" thing with the UI. I pass this IDispatchBroker into the code that runs the timer. The polled data returns and is dispatched over to the UI.
Jason Jackson
Just out of curiosity, why didn't you use IDisposable instead of creating your own interface? Was it because of the semantics of IDisposable assuming that unmanaged resources were in use (which still might apply, just not always)?
Adrian Anttila
Right. I generally consider IDisposable for collecting unmanaged resources. This is not always the case here.
Anderson Imes
+2  A: 

I think what you are looking for is IActiveAware interface from Region Manager framework (see RegionActiveAwareBehavior):

The RegionActiveAwareBehavior is responsible for notifying a view if it is active or inactive. The view must implement IActiveAware to receive these change notifications. This active aware notification is one-way to the view; the view cannot affect its active state by changing the active property on the IActiveAware interface.

PL
Nice! I am already using the region manager.
Jason Jackson
This doesn't notify you if a view is closed, only if it is active.
Anderson Imes
It might do for me. I could active and deactive my timer when the window is active and inactive. I might combine this with writing my own region manager, similar to the answer Anderson gave.
Jason Jackson
Right, the idea is though that you'd Deactivate the view before removing it, thus giving view a chance to perform whatever deactivations tasks it might need to. If for some reason it's too much to ask then it helps that Remove method on the Region class is virtual. Thus it's quiet easy to extend it and override Remove method so that the view will be deactivated on Remove automatically.
PL
You get activation deactivation events from tabcontrols/etc as well.
Anderson Imes