I am trying to understand how MvvmCross manages memory on Windows Phone.
I try to mark my view and viewmodel as IDisposable, but the Dispose method is never called.
What I need to do is to make sure that I can free up some resources while my app is running location tracking in the background.
Christian
In MvvmCross:
each View references its ViewModel
the platform-specifiic operating system decides when to dereference the View - when this occurs it normally calls a method on the View (but this does depend on whether the view is a page, a tab, a flyout, a dialog, a custom control, etc)
the .Net or Mono Garbage Collector then decides when to collect both the Views and ViewModels from memory
If you want to do more "active" monitoring of when the View is visible for "page-level" Views, then you need to monitor "page-level" View-specific events like:
ViewDidAppear \ ViewDidDisappear \ removeFromParentViewController (iOS)
OnResume \ OnPause`OnFinish` (Droid)
OnNavigatedTo`OnNavigatedFrom` (Windows)
A generalised form of these events can then be easily called on a custom interface on your ViewModel (this can be IDisposable if you want - this is your application code).
Update: I have blogged about this and published a sample - see http://slodge.blogspot.co.uk/2013/11/n42-is-my-viewmodel-visible-can-i-kill.html
There's a bit more info on this in:
ViewModel LifeCycle, when does it get disposed? (see "There's no easy universal way to know when to dispose the ViewModel - especially once you start mixing and matching ViewModel presentation styles to include navigations, tabs, splitviews, flyouts, fragments, lists, etc. and as you include more and more platforms")
https://github.com/MvvmCross/MvvmCross/wiki/View-Model-Lifecycle#viewmodel-deactivation-activation-and-destruction
Related
I'm using MVVM Light and everything is fine except when launching my Windows Phone 8.1 WinRT app as a Share Target.
When I try to assign MainViewModel viewModel = ServiceLocator.Current.GetInstance<MainViewModel>(); I get an exception for ServiceLocator.Current.
Exception Message: ServiceLocationProvider must be set.
Do I need to do something extra in App.xaml.cs OnShareTargetActivated event to insure the Locator is running?
UPDATE:
A ShareTarget page needs to be thought of as a small extension of your app. It seems that not all of the app's resources are loaded (including app-wide resources in App.xaml). So I just created a new instance of MainViewModel in the share page's constructor, loaded only the things I need for the share to complete, save the information and call ShareOperation.ReportCompleted. This returns the user back to the app that is sharing.
I still haven't found a good solution for getting other resources in my ViewModel, but this works for now.
This indicates that the following line has not been executed:
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
This line will instruct the ServiceLocator class to use the SimpleIoc.Default instance as its ServiceLocator.Current. When you run your app as a Share target, the initialization is slightly different and probably the ViewModelLocator doesn't get initialized. You need to find a good location to perform the initialization before you use the ServiceLocator.
Cheers
Laurent
I have a command that shows a view model:
private void DoShowImportCommand()
{
this.ShowViewModel<GeometryImportViewModel>();
}
but I only want to execute it if that view model isn't already shown. Is there a way to
detect if that view model is already on screen and if so don't execute the command?
MvvmCross doesn't track this by default - what is currently shown depends on the UI and can be interpreted in different ways in different situations (popups, tabs, pivots, dialogs, back stacks, etc)
If you want to track this in your own application, you could do it using UI project components (e.g. custom presenters) or you could do it using a shared code component - e.g. you could add "alive" tracking to the Views/ViewModels (see the N=42 video on http://mvvmcross.blogspot.com) and could then use some service to track which viewmodels are shown.
In my Windows Phone App there's a simple hierarchical model consisting of a class containing a collection of other domain objects.
In my xaml i have declared an ItemsContainer control that renders the items in the above mentioned collection as simple rectangles.
Now, at the VM level i have a structure that resembles my model with a parent VM having a collection of children VMs. Each child-VM encapsulates its own model.
Whenever the user taps the view bound to a child-VM a method of the parent-model object should be invoked taking the relevant child-model as parameter. This will in turn change some internal state that will be reflected (possibly) on all the child-views (not just the tapped one).
SO... given that i'm using the MVVM Light framework my current implementation is as follows:
Child-VM exposes a command
The command Execute method will use the messenger to notify the parent-VM of the tap event. The message (GenericMessage class) content will be the domain object encapsulated by the VM
The parent-VM executes the method of the parent-model using the message content as parameter
If the operation succeeds the parent-VM sends a new message to inform child-VMs of this fact. Once again the message content is the model object used as parameter in the method that was just invoked
Child-VMs raise a couple of PropertyChanged events that, finally, will update the bound views
It works but i fill it's a bit cumbersome. The thing that bugs me the most is the fact that when a child-view is tapped the associated VM will broadcast its encapsulated model object. Do you feel that there would be a better way of implementing such a system?
Thanks in advance for your precious help
Could you not just put the command on the parent viewmodel and pass the child viewmodel as the command parameter?
The parent view model can then just call methods on the child viewmodels to update them. I'm not sure I see the need for all these messages?
We have a windows phone 8 application in which we are using mvvm light having four , five views , and about same number of view models. One day we observed that the size of the application is increasing with usage and eventually reaches more than 100 mb and eventually crashes.After lot of testing what we are able to understand is that every time we navigate to a view , its instance is created and stored in the memory.It was observed that all the instances of the view and the view model are living in the memory and thus increasing the space on the ram. We also confirmed the same by defining finializer on view class and view model , on closing the application the finializer is called exactly the same number of times the page was navigated to. We are binding the datacontext of the view to respective view model in xaml. One of the main view has an ad control , so size increases very fast if user navigates to that view multiple times. How to resolve this issue. What I am unable to understand is the view should be destroyed once the user presses the back button, but this is not happening . Any help would be much appreciated.
We found a solution to this by adding below line of code to code behind.
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
Messenger.Default.Unregister(this);
if (e.NavigationMode == NavigationMode.Back)
{
DataContext = null;
}
}
What we are doing above is that we are unregisterring all the message handlers for the page and assigning the DataContext to null. In our case the datacontext was assigned in the xaml only and messenge handlers were registered in the OnNavigatedTo event of the page. But what is still unclear that on navigating back from the page , the page object should have died automatically . And should this line of code be added to all the mvvm light project pages and if so then why is it not common practice.
The reason you are leaking View memory is because you are in some way subscribing to events of the ViewModel from inside your Views. Either refactor those subscriptions to be WeakEvent subscriptions or remove them inside your OnNavigatedFrom
Use an IOC Container to maintain a single instance of all the ViewModels.
One of the options would be to use SimpleIoc that comes with MVVM Light.
Best tutorial to learn MVVMLight SimpleIoc
I'm creating a flash campaign which will be loaded into a client's framework, which I have no control over. The framework will already have loaded a few things such as locale, fonts and copy, and will pass these things to my swf upon initialization.
Since the size of my swf (let's call it the shell) is restricted it will in turn display a campaign-specific preloader and then load another swf (let's call this the campaign) with the rest of the site.
The shell and the campaign will both be PureMVC modules. The shell will create a few proxies and populate these with data passed from the framework (locale constants, fonts etc), before loading in the campaign.
When the campaign is loaded it too will need locale and fonts etc. so my question is, what is the best way to pass this data along to the campaign module from the shell module?
I could create the same proxies in the campaign module and load the data again, which will be cached, but this obviously feels like the wrong way to go.
I've investigated the use of the pipes utility but this seems like a bit of an overkill in my case since the communication will be one-way and will just happen once during the initialization of the campaign.
Would it be "ok" from a design pattern point of view to pass the proxies to an init method of the campaign module and then register these proxies in the campaign module startup command? This seems wrong since these proxies have references to my shell application facade through notification names. Would it be ok if I move the notification names to some "NotificationConstants" class which both modules can use?
I could create similar proxies in the campaign module but this time populate them with the data objects from my old proxies passed to the previously mentioned init method? Spontaneously this feels like the best way to do it since the data objects don't have any references to my shell module but the "old" proxies do..
The solution I usually use is to create an interface:
interface Campaign {
function set campaignDetails(value:CampaignDetails):void;
//...
}
The campaign-module should implement this interface - in the implementation I recommend you to use a different proxy in the module, so that you would avoid having duplicated notifications and references.
When the shell is ready with the loading of the module it just has to:
if (module is Campaign)
{
(module as Campaign).campaignDetails = ...;
}
I'm sure I'm telling you nothing new. You just need to make sure to keep the acquaintance between the shell and the module only on an interface level. Then you just pass the data and leave the module MVC core to deal with it independently from the shell.