How to create viewmodels instance when app start time in xamarin forms MVVM

1k Views Asked by At

My aim is to access bindable property across the the App. But My current framework ViewModel Instance create multiple time

My Requirement : I have the cart count in the bottomTray(CheckuoutViewModel) i want to increase the cart count any where in the app page, but in this cart count not update when back click, its only working on forward navigation, the reason behind CheckoutViewModel instance create each and every time. so that i'm try to instant creation at earlier.

Here I'm list out sample ViewModel and calling method

  1. Login ViewModel
  2. Checkuout ViewModel(This view model common for all page)
  3. BaseNavigationViewModel(Its BaseViewModel)

As of now i'm calling when BindinContext each and every time like,

  • new LoginViewMode(navigation)
  • new CheckoutViewModel(navigation) what will do to create all ViewModel instance when app start time like ViewModel Locator?

Im tried

public static ViewModelLocator Locator
    {
        get { return locator ?? (locator = new ViewModelLocator()); }
    }

And ViewModel Locator

public ViewModelLocator()
    {
        navigation = App.Current.MainPage.Navigation;
    }
internal CustomTabBarViewModel CustomTabBarVM
    {
        get
        {
            return customTabBarVM ?? (customTabBarVM = new CustomTabBarViewModel(navigation));
        }
    }

And CustomTabBar.xaml.cs

public CustomTabBar()
    {
        viewModel = App.Locator.CustomTabBarVM;
        InitializeComponent();
        BindingContext = viewModel;
    }

and Expectation

App.Locator.CustomTabBarVM.BadgeCartCount = OrderObject.Instance.ORDER_OBJECT.Items.Count;

This approach is working fine but it's create some navigation issues

2

There are 2 best solutions below

6
Axemasta On

A singleton instance is a common feature of virtually all MVVM frameworks (Prism, FreshMVVM etc). If you aren't using a framework (if you aren't, I would STRONGLY advise you consider using one), below is a solution.

To obtain a single instance of a ViewModel you can use the App class to host the object and access it whenever you need.

  • Create a public static property of your ViewModel:
public static MyViewModel MyViewModelInstance { get; }
  • Create an instance in the constructor of the app
public App()
{
    InitializeComponent();

    MyViewModelInstance = new MyViewModel();

    var myPage = new MyPage()
    {
        BindingContext = MyViewModelInstance
    };

    var navPage = new NavigationPage(myPage);

    MainPage = navPage;
}
  • Whenever you create a new page, access the shared instance
// This method is just an example of how you might create a new page and wire up the view model
async void GoNextClicked(System.Object sender, System.EventArgs e)
{
    var myPage = new MyPage()
    {
        BindingContext = App.MyViewModelInstance
    };

    await this.Navigation.PushAsync(myPage);
}

This approach comes with a few caveats, you are creating instances when the app loads not when they are needed (Eagerly loading). So a performance optimisation would be to use Lazy<T> to handle the creation of these objects. However this is logic that has already been written for you in MVVM frameworks, they are there to help you and you should be using them.

Lazy Load

You can save memory and performance at startup by lazy loading the viewmodel, here is this example rewritten to support this pattern:

public static MyViewModel MyViewModelInstance
{
    get => _myViewModelInstanceFactory.Value;
}

private static Lazy<MyViewModel> _myViewModelInstanceFactory = new Lazy<MyViewModel>(() => new MyViewModel(), true);

public App()
{
    InitializeComponent();

    var myPage = new MyPage()
    {
        BindingContext = MyViewModelInstance
    };

    var navPage = new NavigationPage(myPage);

    MainPage = navPage;
}

Now this object won't be created until it is accessed by your code, and once it has been accessed once it has already been created and will go on to live in memory for the rest of your apps lifecycle.

0
ToolmakerSteve On

Axemasta has good answer about re-use of a shared view model instance.

I'll give an alternative approach to the underlying need given in one comment: how to have a static property (so the value is common), and Bind to it when Binding to an instance.

Use this approach if you do want a different CheckoutViewModel for each new page. For example, if there are other properties that should be set up differently, depending on the page.

public class CheckoutViewModel :  : INotifyPropertyChanged  // or your MVVM library's base class for ViewModels.
{
    public static int SharedCount { get; set; }

    public void IncrementCount()
    {
        Count = Count + 1;
    }

    public int Count {
        get => SharedCount;
        set {
                // Exact code might be simpler if using an MVVM library.
                if (SharedCount != value)
                {
                    SharedCount = value;
                    OnPropertyChanged("Count");
                }
            }
        }
    }
}

LIMITATION: This assumes that only the current instance of CheckoutViewModel is visible; if you need to "notify" OTHER Views (update other CheckoutViewModel instances), then you'll need a "publish/subscribe" solution. In Xamarin Forms, one such solution is MessagingCenter.