I'm trying to clean one view model when i'm leaving back one page as shown in this sketch:
A -> B -> C -> B
when i get back from C->A i want to clean the viewmodel.
I tried to override OnAppearing() and OnDisappearing() from ContentPage, but they called everytime I enter/leave one page. Is it possible to get the navigation direction?
You might want to hook into some of the navigation events . Another approach is to do viewmodel -> viewmodel navigation. in each viewmodel you inject a provider to your data e.g. IWizardDataProvider. Then you use this provider to encapsulate a singleton og have it injected as a singleton/singleinstance. When ever you get to the A page, you call a Create() on your provider which can clear the current instance with collected information. The other pages/viewmodels should the use the .Current to add information.
Related
Can anyone help.
We are working on an app which has a consistent header and footer and therefore ideally we'll use one viewmodel for the "home page" but we want the header and footer to remain.
Before we switched to starting using Prism, this was easy enough to navigate as we could control that in the Pages event and set the page.contentFrame.Navigate method to go where we wanted.
Now we're using the MVVM structure (which is superb and wish I'd done it ages ago) the NavigationService class only navigates the entire page (the VisualStateAware page).
How can I set this up so that when calling the Navigate method on the interface in the viewmodel that only the main content frame is ever navigated? or is there a better approach to this?
Any help would be greatly appreciated.
thank you
The question title seems to, pre-empt the details of the question slightly as a solution. But to share a common view model and visual parts across all pages, within a frame, using the navigation service to navigate between pages here is an overview..
Create a shared ViewModel, say "HeaderViewModel" of type say IHeaderViewModel to be shared between the different pages' view models. Inject this into the constructor of each page's ViewModel.
Then expose this as a property of each page's ViewModel. This property could also be called HeaderViewModel too. You can then reference the properties of this common HeaderViewModel in the bindings in the View, using binding '.' notation.
If you are using Unity with Prism, you can create this shared instance HeaderViewModel in the OnInitialize override of the App.
Create a shared part for each Page/View as a UserControl, which can be positioned on each page in the same place. This enables you to bind to the same properties on your HeaderViewModel.
I am trying to use NFC along with MvvmCross. So I have created an IntentFilter which looks like this:
[IntentFilter(new[]{"android.nfc.action.NDEF_DISCOVERED"},
Categories = new[] {"android.intent.category.DEFAULT"},
DataScheme = "bksv-resource", DataHost = "nmt")]
And in OnResume I am looking for Intent Actions of the type NfcAdapter.ActionNdefDiscovered. What happens is when NDEF is discovered, the Activity gets created and an Intent is set with the data inside. All this works fine in an Activity, however problems occur when wanting to use an MvxActivity or MvxFragmentActivity.
Problem is, when the MvxActivity gets recreated the base.OnCreate() call gives no ViewModel back and no reason is given, only output like this is shown:
mvx:Diagnostic: 6.09 Loading new ViewModel from Intent with Extras
mvx:Warning: 6.13 ViewModel not loaded for view ScanView
I am not entirely sure how the ViewModel is found for a View, however, in this case it seems not to be found. Why could that be? Do I need a custom app start or something?
MvvmCross picks up its ViewModel navigation information from the passed in Intent
You can:
see how MvvmCross creates it's Intents in: MvxAndroidViewsContainer.cs#L120
see how MvvmCross gets the information back out in: MvxAndroidViewsContainer.cs#L87
If you are using a custom non-MvvmCross Intent then MvvmCross obviously won't be able to pick up its navigation information - so you'll have to work out a way to create the ViewModel yourself.
I am slowly migrating my app over to MvvmCross on Android. I have not converted all Activities over to ViewModels yet. Therefore, I need to navigate from an Mvvm-controlled Activity to a regular Activity. To do this, I made my own Presenter and intercepted the Show method and did my own StartActivity. That seemed to work. However, now I need to go the other direction and have my regular Activity go back to the original Mvvm-controlled Activity. I tried just doing a StartActivity on the ViewModel using CLEAR_TOP flag, but I got an error:
"Null Extras seen on Intent when creating ViewModel - this should not happen - have you tried to navigate to an MvvmCross View directly?"
How can I go back to the original Mvvm-controlled activity from a regular Activity?
Simple...
To go back from any standard Android Activity, you can simply ask the Activity to close using Finish()
But beyond that...
If instead you want to go forwards to an MvvmCross View, then you need to know a little about MvvmCross internals: in particular about how MvvmCross navigation conceptually happens between ViewModels rather than between Activities, Pages or UIViewControllers.
If you want to go forwards to a new ViewModel, then you can do this using the IMvxViewDispatcher singleton - how to do this is shown in Show view from non-view/viewmodel in mvvmcross
If you then later want to go back from the current ViewModel, then you can try calling Close(this) within the ViewModel - by default, on Android this will map to Finish(), on Touch to PopViewController, on WpDev to GoBack()
This seemed to work, but is a hack since I use a special string "MvxLaunchData".
Intent i = new Intent(this,typeof(LoginView));
i.AddFlags(ActivityFlags.ClearTop);
var converter = Mvx.Resolve<IMvxNavigationSerializer> ();
MvxViewModelRequest request = MvxViewModelRequest.GetDefaultRequest (typeof(LoginViewModel));
i.PutExtra ("MvxLaunchData", converter.Serializer.SerializeObject(request));
StartActivity(i);
I will try the method shown in the other question you referenced.
I've just created my first c# / XAML application using mvvmlight and I've tried to implement the MVVM pattern as best I can (WP8 app). However, I've slowly morphed my code in to a certain style and I don't think its correctly implementing the pattern! Any advice on how things are supposed to be would help enormously.
For example, using mvvmlight I am making heavy use of the ViewModelLocator. Some of my viewmodels get created right away, like the SettingsViewModel (there is a SettingsView).
SimpleIoc.Default.Register<SettingsViewModel>(true);
And then elsewhere in my project, my other viewmodels will directly access this viewmodel for occasional information via a property or a method... like this;
mySetting = ViewModelLocator.SettingsStatic.GetSomeSetting(var);
My concern is that my viewmodels are talking to each other in this way more and more. The issue with this is that they probably can't be tested independently now because they require or assume the existence of other viewmodels.
Any pointers here would be great!
EDIT: Another example is having a PersonView, and the PersonViewModel has some helper methods for UI display. In some cases I have other views that need to display this info.... and I use the viewmodellocator to get to them, rather than writing the helper methods again in the current viewmodel.
You are right in thinking that viewmodels being dependent on viewmodels is going to cause trouble. When I need to have access to "global" settings in my app, I use an interface that can be injected in the constructor of the view model. So, You can create an ISettingsService that contains the properties and methods you need. You also create a design time setting service that mimics or fakes the data/properties of the ISettingsService Interface
then in your view model locator you use this:
if (ViewModelBase.IsInDesignModeStatic) {
SimpleIoc.Default.Register<ISettingsService, DesignSettingService>();
} else {
SimpleIoc.Default.Register<ISettingService, SettingService>();
}
Create the DesignSettingService and SettingService which both implement the ISettingsService.
As for you vewmodels, the SimpleIOC will resolve/inject the required elements passed into the constructor of the class. If you have a class/viewmodel called MyViewModel and it wanted to use the settingsservice then you would define the constructor like this:
private ISettingsService _SettingsAccess;
public New(ISettingsService SettingsService)
{
_SettingsAccess = SettingsService;
SettingProperty= _SettingsAccess.GetProperty;
}
This keeps the viewmodels decoupled as this service is resolved in the constructor, that way you can change the implementation of your ISettingsService without breaking every viewmodel that uses it.
I use a INavigationService to handle all the navigation events in my app, this allows me to cancel navigation based on another viewmodels properties without needing the other viewmodel to be directly called/referenced by the current one.
One view model should never directly call another view model. Using this method you can pass as many "services" as are needed to the viewmodel. Every viewmodel I use gets a dataservice that connects to my model in addition to the navigationservice. Ie. A viewmodel that deals with people gets an IPeopleDataService where this service contains all the CRUD operations that affect the database. That way I can change the people objects in the database and service functions without having to change the people viewmodel as well.
I hope this helps.
I am programming WP7 application adhering the MVVM pattern.
I have ViewModelLocator which ensures that each instance of ViewModel is only one. These ViewModels are created when application is launched. ViewModels communicating with each other using messages. I use messages for navigatig to next Page (For this I am using NavigationService.Navigate(), which is Raised from MainPage CodeBehind - it is the only functionality that is in CodeBehind ). View and ViewModels are connected together by setting the DataContext in the Page to ViewModelLocator.
Everythig works at first sight.
But during each navigation, there is created new Page instance, which is connected to the ViewModel from ViewModelLocator(which is designed for it). The result is that: when a user often switches between pages, there are multiple instances of a page connected to one ViewModel. Of course, there is visible only one page at one point.
Very simple solution could be setting NavigationCache, but it is readonly in WP7.
I am looking for solution of unwanted behavior.
Every time you Navigate somewhere a new instance is created for that page.
You could avoid this by using NavigationService.GoBack(); where ever you can.
You should also unregister from every event when navigating away from the page, so that way the garbage collector can clear out that page.
I hope this helps.
You may try to declare an instance of your ViewModel at App.xaml.cs, such as,
private static YourViewModel viewModel = null;
public static YourViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = ViewModelLocator.MainStatic;
return viewModel;
}
}
Then from the page you will navigate to, you can reference it as App.ViewModel.
If your page is in different assembly form your main application, you could declare following in your App.xaml,
<vm:ViewModelLocator x:Key="VMLocator" />
Where vm referencing to your main app, then you can use is as following,
((ViewModelLocator)Application.Current.Resources["VMLocator"]).YourViewModel;
Hope it would help.