I'm looking for the right way to dispose objects in a Xamarin Forms application. Currently i'm using XAML and MVVM coding style. Then from my view model i get a reference to a disposable object through the builtin service locator (DependencyService). Ideally i should be able to call Dispose() on the objects from my view model, but other solutions like attaching to ContentPage.OnDisappearing and NavigationPage.Popped could be feasible.
I had pretty much the same requirement a couple of weeks ago. I wanted to make sure that event subscriptions in my view models would be unsubscribed when the page is closed. After a lot of research my conclusion was that the simplest solution was to use the ContentPage.OnDisappearing method.
As you pointed out the object you want to dispose is in your ViewModel, so you need a little bit of infrastructure to make sure your ViewModel is informed when the it's disappearing. To do that I defined a base implementation of my view model that had two key methods OnAppearing and OnDisappearing (note this was a class rather than an interface because I have other base functionality such as IPropertyNotify implementation - not shown here).
public class ViewModelBase
{
/// <summary>
/// Called when page is appearing.
/// </summary>
public virtual void OnAppearing()
{
// No default implementation.
}
/// <summary>
/// Called when the view model is disappearing. View Model clean-up should be performed here.
/// </summary>
public virtual void OnDisappearing()
{
// No default implementation.
}
}
Then I subsclassed ContentPage and override the OnAppearing and OnDisappearing methods and then use them to notify my view model.
public class PageBase : ContentPage
{
/// <summary>
/// Performs page clean-up.
/// </summary>
protected override void OnDisappearing()
{
base.OnDisappearing();
var viewModel = BindingContext as ViewModelBase;
// Inform the view model that it is disappearing so that it can remove event handlers
// and perform any other clean-up required..
viewModel?.OnDisappearing();
}
protected override void OnAppearing()
{
base.OnAppearing();
// Inform the view model that it is appearing
var viewModel = BindingContext as ViewModelBase;
// Inform the view model that it is appearing.
viewModel?.OnAppearing();
}
}
Then when you implement a page just make sure that it is of type PageBase:
<?xml version="1.0" encoding="utf-8" ?>
<pages:PageBase xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Forms.App.Controls;assembly=Forms.App"
xmlns:converters="clr-namespace:Forms.App.Converters;assembly=Forms.App"
xmlns:pages="clr-namespace:Forms.App.Pages;assembly=Forms.App"
x:Class="Forms.App.Pages.LogonPage"
NavigationPage.HasNavigationBar="False"
Title="Logon">
And in your ViewModel you can then override your OnDisappearing method and dispose your objects:
public class FormViewModel : ViewModelBase
{
public override void OnDisappearing()
{
base.OnDisappearing();
// Dispose whatever objects are neede here
}
}
Just one thing to watch out for - if you're using stack navigation the OnDisappearing method gets called when you stack another page on-top of your current page (your page is disappearing temporarily after all). So you will need to cater for this and probably not dispose your object in that case. However if you're not stacking anything on-top of your page there is nothing to worry about. In my case it was just event subscriptions so I attached the event handlers in the OnAppearing and detached them on the OnDisappearing.
I hope that helps you out!
We were getting disposed of object exceptions in Forms when Bindings to ListViews or Labels changed values as pages/fragments were being disposed of. I'm assuming you could dispose of objects in your ViewModel the same place we were removing bindings.
protected override void OnParentSet()
{
base.OnParentSet();
if (Parent == null)
{
//Clear a bunch of bindings or dispose of ViewModel objects
BindingContext =
_listView.ItemsSource = null;
}
}
I have View Models that conform to IDisposable. So I needed a way for the Page's BindingContext to be disposed when the page is no longer needed.
I used the suggestion of Nick which uses OnParentSet being set to NULL to known when the page is no longer needed.
The SafeContentPage class can be used in place of ContentPage. Iff The binding context supports IDisposable will it automatically try to dispose the binding context.
public class SafeContentPage : ContentPage
{
protected override void OnParentSet()
{
base.OnParentSet();
if (Parent == null)
DisposeBindingContext();
}
protected void DisposeBindingContext()
{
if (BindingContext is IDisposable disposableBindingContext) {
disposableBindingContext.Dispose();
BindingContext = null;
}
}
~SafeContentPage()
{
DisposeBindingContext();
}
}
The OnDisappearing method isn't a reliable technique as there are platform differences in terms of when it is called, and just because the page disappeared doesn't mean its View Model is no longer needed.
Related
I really made a search for this topic and did not find anything, and because of that, I am asking the question here.
I have a WPF application with Prism installed.
I have wired the view-model with the view automatically by name convention
<UserControl x:Class="Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
and the model in the 'Model' like this
public class ViewAViewModel {
public ViewAViewModel () {
// time-resource consuming operations
}
}
the automatic binding work perfectly without a problem and the view and its corresponding view-model is matching, but the problem here.
I have a lot of those views say (50) and for every one of them, the view-model will be created with constructor exhausting the processes. This will make the startup of the application longer and also it will create a lot of view-models objects and put them in the RAM without being sure that they will be used at all.
What I need is to create the view-model class when the view is activated (I mean when the view is navigated to). Is this possible and if yes how?
Update
here is how I register the view with the Module, this is causing all the views to be created when the startup of the module.
public class Module1 : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("region1", typeof(View1));
regionManager.RegisterViewWithRegion("region1", typeof(View2));
is there any way to delay the creating of the views, until the navigation request come?
You could use navigation, for each view.
Or you must create an interfaces for your view and view model.
An example:
public interface IMyView
{
IMyViewModel ViewModel { get; set; }
}
public interface IMyViewModel
{
}
In the module or app.cs, in the method RegisterTypes you should register these.
containerRegistry.Register<IMyView, MyView>();
containerRegistry.Register<IMyViewModel, MyViewModel>();
You must implement IMyView interface in your MyView.cs class.
public partial class MyView : UserControl, IMyView
{
public MyView(IMyViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
}
public IMyViewModel ViewModel
{
get => DataContext as IMyViewModel;
set => DataContext = value;
}
}
After you could use it:
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
var firstView = containerProvider.Resolve<IMyView>();
regionManager.AddToRegion(RegionNames.MainRegion, firstView);
}
In such case you shouldn't use ViewModelLocator.AutoWireViewModel in your view.
I am working on Xamrin Form right now. I have problem with clear data of ViewModel.
When I logout and login with different user, it shows me data of previous user because the value of UserProfileViewModel doesn't get clear.
When user logout, I want to clear user data from UserProfileViewModel class file. Currently I do this manually when user click on logout. I want any default method like dispose to clear all class member.
I have tried to inherit IDisposable interface with this.Dispose(); but that also didn't work.
I have also tried with default constructor as following but it throws error of
`System.TypeInitializationException`
on this line in app.xaml.cs: public static ViewModelLocator Locator => _locator ?? (_locator = new ViewModelLocator());
public UserProfileViewModel()
{
//initialize all class member
}
In given code, you can see that on Logout call, I call method
`ClearProfileData` of `UserProfileViewModel`
which set default(clear)
data. It is manually. I want to clear data when user logout.
View Model Logout Page
[ImplementPropertyChanged]
public class LogoutViewModel : ViewModelBase
{
public LogoutViewModel(INavigationService nService, CurrentUserContext uContext, INotificationService inService)
{
//initialize all class member
private void Logout()
{
//call method of UserProfileViewModel
App.Locator.UserProfile.ClearProfileData();
//code for logout
}
}
}
User Profile View Model
[ImplementPropertyChanged]
public class UserProfileViewModel : ViewModelBase
{
public UserProfileViewModel(INavigationService nService, CurrentUserContext uContext, INotificationService inService)
{
//initialize all class member
}
//Is there any other way to clear the data rather manually?
public void ClearProfileData()
{
FirstName = LastName = UserName = string.Empty;
}
}
ViewModel Locator
public class ViewModelLocator
{
static ViewModelLocator()
{
MySol.Default.Register<UserProfileViewModel>();
}
public UserProfileViewModel UserProfile => ServiceLocator.Current.GetInstance<UserProfileViewModel>();
}
Firstly there is no need to cleanup these kinds of primitive data types, the gc will do that for you.
However if you use Messages or any other Strong Reference for that matter you WILL have to Unsubscribe from them otherwise your viewmodal will hang around in memory and will never go out of scope
The garbage collector cannot collect an object in use by an
application while the application's code can reach that object. The
application is said to have a strong reference to the object.
With Xamarin it really depends how you are coupling your View to Viewmodals to determine which approach you might take to cleanup your viewmodals.
As it turns out MVVM Light ViewModelBase implements an ICleanup interface which has an overridable Cleanup method for you.
ViewModelBase.Cleanup Method
To cleanup additional resources, override this method, clean up and
then call base.Cleanup().
public virtual void Cleanup()
{
// clean up your subs and stuff here
MessengerInstance.Unregister(this);
}
Now your just left with where to call ViewModelBase.Cleanup
You can just call it when your View Closes, if you get a reference to the DataContext (I.e ViewModalBase) on the DataContextChanged Event
Or you can wire up a BaseView that plumbs this for you, or you can implement your own NagigationService which calls Cleanup on Pop. It really does depend on who is creating your views and viewmodels and how you are coupling them
Most of my view models subscribe to a common event using Prism's EventAggregator on a WPF project. Basically, a vocal command triggers this event on a view, and as a response the view will publish another event containing its specific message to a text-to-speech module.
However, when I implemented this, I realized that when using RegionManager's RequestNavigate to switch to another view, the previous view model is still somehow active. When I trigger the common event for the most recent view, it is also triggered for the previous view.
Simplified example :
Start at View 1
Trigger common event
Response : message from View 1
RequestNavigate to View 2
Trigger common event
Response : message from View 2, then message from View 1
RequestNavigate to View 3
Trigger common event
Response : message from View 3, then View 2, then View 1
etc.
I placed a breakpoint on View 1, View 2 and View 3's common event, and each time I get a message from a view, its breakpoint is hit.
What I would like is simple : I don't want the previous ViewModel (and possibly View too) to be still somehow active when I'm switching Views. Even better would be for them to be garbage collected, because I also had some weird cases where by navigating to View 1, View 2 and View 1 again, the message for View 1 was sent twice (and its breakpoint also hit twice), so I'm not even sure if multiple references for the ViewModels are created, which could potentially lead to a memory leak.
I tried to reproduce this behavior by creating another project with just the essentials, so here's the code. I'm using Visual Studio 2017 with .net framework 4.5.2 and Ninject.
Shell.xaml
<Window x:Class="PrismTest.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prsm="http://prismlibrary.com/"
mc:Ignorable="d">
<Grid>
<ContentControl Name="MainRegion" prsm:RegionManager.RegionName="MainRegion" />
</Grid>
</Window>
NinjectPrismBootstrapper.cs
public class NinjectPrismBootstrapper : NinjectBootstrapper
{
protected override void InitializeModules()
{
base.InitializeModules();
// Text to speech
Kernel.Bind<SpeechSynthesizer>().ToSelf().InSingletonScope();
Kernel.Bind<INarrator>().To<StandardNarrator>().InSingletonScope();
Kernel.Bind<INarratorEventManager>().To<NarratorEventManager>().InSingletonScope();
// View models
Kernel.Bind<MainPageViewModel>().ToSelf();
Kernel.Bind<SecondPageViewModel>().ToSelf();
// Views
Kernel.Bind<object>().To<MainPageView>().InTransientScope().Named(typeof(MainPageView).Name);
Kernel.Bind<object>().To<SecondPageView>().InTransientScope().Named(typeof(SecondPageView).Name);
Kernel.Bind<Shell>().ToSelf();
var narratorEventManager = Kernel.Get<INarratorEventManager>();
var regionManager = Kernel.Get<IRegionManager>();
regionManager.RegisterViewWithRegion("MainRegion", typeof(MainPageView));
}
protected override DependencyObject CreateShell()
{
return (Shell)Kernel.GetService(typeof(Shell));
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
}
MainPageView.xaml (my starting page)
<UserControl x:Class="PrismTest.Views.MainPageView"
namespaces...>
<StackPanel>
<TextBlock Text="Main page"/>
<Button Content="Narrator speaks" Command="{Binding Path=NarratorSpeaksCommand}" />
<Button Content="Next page" Command="{Binding Path=GoToNextPageCommand}"/>
</StackPanel>
</UserControl>
MainPageView.xaml.cs
public partial class MainPageView : UserControl
{
public MainPageView(MainPageViewModel dataContext)
{
InitializeComponent();
this.DataContext = dataContext;
}
}
MainPageViewModel (View model for MainPageView)
public class MainPageViewModel : BindableBase, IRegionMemberLifetime, INavigationAware
{
private readonly IEventAggregator _eventAggregator;
private readonly IRegionManager _regionManager;
public DelegateCommand GoToNextPageCommand { get; private set; }
public DelegateCommand NarratorSpeaksCommand { get; private set; }
public MainPageViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
{
_eventAggregator = eventAggregator;
_regionManager = regionManager;
ConfigureCommands();
//The original common event triggered by a vocal command is simulated in this project by simply clicking on a button
_eventAggregator.GetEvent<CommonEventToAllViews>().Subscribe(NarratorSpeaks);
}
private void ConfigureCommands()
{
GoToNextPageCommand = new DelegateCommand(GoToNextPage);
NarratorSpeaksCommand = new DelegateCommand(ClickPressed);
}
private void GoToNextPage()
{
_regionManager.RequestNavigate("MainRegion", new Uri("SecondPageView", UriKind.Relative));
}
private void ClickPressed()
{
_eventAggregator.GetEvent<CommonEventToAllViews>().Publish();
}
private void NarratorSpeaks()
{
_eventAggregator.GetEvent<NarratorSpeaksEvent>().Publish("Main page");
}
}
I don't need to put the code for SecondPageViewModel and SecondPageView, because it's the exact same code except RequestNavigate sends the user back to MainPageView and its NarratorSpeaks method sends a different string.
What I tried :
1) Making MainPageViewModel and SecondPageViewModel inherit IRegionMemberLifetime and setting KeepAlive to false
2) Inheriting INavigationAware and returning false in IsNavigationTarget method
3) Adding this to OnNavigatedFrom method from INavigationAware :
public void OnNavigatedFrom(NavigationContext navigationContext)
{
var region = _regionManager.Regions["MainRegion"];
var view = region.Views.Single(v => v.GetType().Name == "MainPageView");
region.Deactivate(view);
}
Worth noting : even without the deactivate part, if I put a breakpoint after var region = _regionManager.Regions["MainRegion"]; and check region.views, there is only one result, no matter how much I switch views.
Nothing worked, events keep being triggered in previous views as I switch views back and forth.
So, I'm kind at a loss here. I'm not sure if it's my way of registering Views and ViewModels in Ninject that triggers this, or something else, but if someone has a suggestion, I'll gladly take it.
Thanks!
I had similar problems in the past. Have you considered unsubscribe from events when navigated from?
Background info
I'm developing a Xamarin Forms (v4.1.1.3, testing on iOS) application in XAML, using MVVM with a View first approach; I'm assigning single-instance ViewModels to Views by using the ViewModelLocator service of MVVMLight:
BindingContext="{Binding [SearchViewModel], Source={StaticResource ViewModelLocator}}"
When navigating to another page, I'm constructing a new instance of the page, which will receive the very same ViewModel instance every time.
var page = new SearchView();
var tabbedPage = Application.Current.MainPage as TabbedPage;
if (tabbedPage != null)
await tabbedPage.CurrentPage.Navigation.PushAsync(page);
The issue
I've implemented a custom control (view?), that is supposed to show search results in a tile-like layout. This control is created when navigating from a search NavigationPage to a search results ContentPage.
Every time I return to the search page and navigate back to search results, the view is reconstructed and the PropertyChanged of the BindableProperties are subscribed. These PropertyChanged events are never unsubscribed, so every time I navigate to the search results view and change the bound ViewModel property, the event is fired increasingly multiple times.
In the following code the OnItemsPropertyChanged is triggered multiple times, based on how many times I've navigated from the search view to the search results view:
public class WrapLayout : Grid
{
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create("Items", typeof(IEnumerable), typeof(WrapLayout), null, propertyChanged: OnItemsPropertyChanged);
public IEnumerable Items
{
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public WrapLayout()
{
...
}
private static void OnItemsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
...
}
}
My questions:
Shouldn't the BindableProperty unsubscribe from PropertyChanged and -Changing by itself?
Does this occur because of the way I associated Views with ViewModels and/or navigate through pages?
Should I handle unsubscribing these events myself, and how?
EDIT; additional navigation info
I have a MainView TabbedPage, which creates SearchView as NavigationPage:
public MainView()
{
InitializeComponent();
Children.Add(new NavigationPage(new SearchView())
{
Title = AppResources.Tab_Search,
Icon = "tab_search"
});
}
SearchView has, upon creation, a single-instance ViewModel assigned by the ViewModelLocator that was mentioned at the start of this topic, using MVVMLight's SimpleIoc container.
When a search command in SearchView is fired, I send a request to an API which returns search results. These results are displayed on another page, to which I navigate to from the SearchView's ViewModel:
await _navigationService.NavigateTo(ViewModelLocator.PageKeyFileResults, searchResult);
Which functionality looks somewhat like this:
public async Task NavigateTo(string pagekey, object viewModelParameter)
{
var constructor = _pagesByKey[pagekey].Constructor; //Gets the Func<Page> that simple creates the requested page, without using reflection.
var page = constructor() as Page;
var viewModel = page.BindingContext as BaseViewModel;
if (viewModel != null)
viewModel.Initialize(viewModelParameter);
var tabbedPage = Application.Current.MainPage as TabbedPage;
if (tabbedPage != null)
await tabbedPage.CurrentPage.Navigation.PushAsync(page);
else
await Application.Current.MainPage.Navigation.PushAsync(page);
}
The constructed page looks somewhat like:
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Views.FileResultsView"
xmlns:pages="clr-namespace:Views.Pages;assembly=Views"
xmlns:controls="clr-namespace:Views.Controls;assembly=Views"
BindingContext="{Binding [FileResultsViewModel], Source={StaticResource ViewModelLocator}}">
<ScrollView>
<controls:WrapLayout
Items="{Binding SearchResults}" />
</ScrollView>
</pages:BaseContentPage>
Where BaseContentPage is:
public class BaseContentPage : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<DialogMessage>(this, "ShowDialog", (dialogMessage) =>
{
if (string.IsNullOrWhiteSpace(dialogMessage.AcceptButton))
DisplayAlert(dialogMessage.Title, dialogMessage.Content, dialogMessage.CancelButton);
else
DisplayAlert(dialogMessage.Title, dialogMessage.Content, dialogMessage.AcceptButton, dialogMessage.CancelButton);
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<DialogMessage>(this, "ShowDialog");
}
}
And where ViewModel is basically like this:
public class FileResultsViewModel : BaseViewModel
{
private IEnumerable<ASRow> _searchResults;
public IEnumerable<ASRow> SearchResults
{
get { return _searchResults; }
set { Set(ref _searchResults, value); }
}
internal override void Initialize(object parameter)
{
base.Initialize(parameter);
if (parameter is AdvancedSearchResponse)
{
var searchResults = parameter as AdvancedSearchResponse;
SearchResults = new List<ASRow>(searchResults.Rows);
}
}
}
Shouldn't the BindableProperty unsubscribe from PropertyChanged and -Changing by itself?
Yes - it should. If it does not it is most certainly a bug
Does this occur because of the way I associated Views with ViewModels and/or navigate trough pages?
That is most likely also an option, since i didn't experience the behaviour you described yet. You would need to share more of your surrounding setup code.
Should I handle unsubscribing these events myself, and how?
It's hard for you to always control unsubscribing, since most of the time it will be the control subscribing to events (unless you do it yourself, in which case it's always your duty to unsub again)
While it is ugly it's sometimes necessary to get a quick workaround, which in your case would be browsing how xamarin holds a list of the change delegates and manually unsubscribe them on page appearing for example.
I hope that answers your question. Feel free to comment if it does not.
Update
In your case i would debug your page base, and verify wether or not
OnDisappearing is called correctly
Your handler is gone after unsubscribe
(This is lazy but i usually unsub an event before subbing it, just to make sure such a bug does not happen, because most EventManagement services won't throw if you're trying to unsub a handler which is not registered.)
at least that's the most likely causes of your issue.
Shouldn't the BindableProperty unsubscribe from PropertyChanged and -Changing by itself?
No. The Binding class takes care of this. Not the BindableProperty.
Does this occur because of the way I associated Views with ViewModels and/or navigate through pages?
You are seeing this because you are forgetting that the Navigation Stack keeps a list of pages in memory. Since multiple pages are pointing to the same BindingContext, there are multiple observers to changes. You would not have this particular issue if you didn't re-use View Models.
Should I handle unsubscribing these events myself, and how?
No. If it is really a concern then set BindingContext to null when a page disappears, and then restore it when reappearing. Keep in mind though that this still has cost to it, especially if your UI is really busy and has lots of dynamic content that is controlled by data bindings.
In the ViewModel, I have Save method where I check isValid property.
If isValid is false, then I want to display an error message.
Since AlertDialog is platform specific, I wonder how do you handle that situation in the ViewModel?
public void Save()
{
if (isValid)
{
OnExit(this, null);
}
else
{
//issue an alert dialog here
}
}
Update
I have used the following plugin and added the following line of code as follows, but it throws an error.
else
{
Mvx.Resolve<IUserInteraction>().Alert("it is not valid");
}
Update 2
Chance.MvvmCross.Plugins.UserInteraction is a namespace but it is used as a type error.
Update 3
I have added Acr.UserDialogs plugin and called as follows, but I have got the same error.
Mvx.Resolve<IUserDialogs>().Alert("it is not valid");
Using ACR User Dialogs is the simplest approach.
In your App.cs (Core/PCL) you will need to register the interface:
public class App : MvxApplication
{
public override void Initialize()
{
// Example Other registrations
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
Mvx.RegisterSingleton<IUserDialogs>(() => UserDialogs.Instance);
}
}
Then you can call your alert form your ViewModel.
Mvx.Resolve<IUserDialogs>().Alert("it is not valid");
Note for Android Platform support
Then if you are supporting Android you will need to initialize UserDialog with an instance of the activity context. This will have to be done in each activity that you will be making use of UserDialogs or if you have a shared base activity you can do it there.
[Activity]
public class MainActivity : MvxActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.activity_main);
// Initialize Acr UserDialogs
UserDialogs.Init(this);
}
}
Alternatively
You can follow the Mvvmcross document on using platform specific implementations of an interface if you need a more custom modal implementation.
This is how I handle the Alert messages in the viewmodel. Try this.
await App.Current.MainPage.DisplayAlert("Active subscription required", "You do not have an active subscription for Part 2 exams", "OK");
There is an existing MvvmCross plugin called User Interaction that allows displaying alerts and collecting inputs from ViewModels.
From the author BrianChance:
Really simple, easy, beautiful ways to show a message box or to collect user input from your ViewModels
Check it out here and NuGet Link Here.
To install the plugin, make sure you override LoadPlugins in your SetUp Class on iOS and Android (and windows phone) like so:
public override void LoadPlugins(MvvmCross.Platform.Plugins.IMvxPluginManager pluginManager)
{
base.LoadPlugins(pluginManager);
pluginManager.EnsurePluginLoaded<Chance.MvvmCross.Plugins.UserInteraction>();
}
My approach is that i use an event for this scenario. My base class for my view models has a EventHandler OnUserNotification, where the views can kinda subscribe to. The UserNotificationType is just an enum and i let the view kinda decide how it reacts to the situation.
The property:
public EventHandler<UserNotificationType> OnUserNotification { get; set; }
The call:
if (OnUserNotification != null)
{
OnUserNotification.Invoke(this, UserNotificationType.ENetworkError);
}
In the view:
private void onUserNotification(object sender, UserNotificationType userNotificationType)
{
// Do Something like showing a Snackbar, AlertDialog, etc...
}
Of course you can make the eventtype more complex if needed.
Didnt try the plugin which got suggested by wishmaster though, so that might be a smoother implementation?
Use Acr.UserDialogs. There is a great examples on github
You can grab it on nuget
It works well with dependency injection or a static singleton UserDialogs.Instance