Search This Blog

Sunday, 13 February 2022

Xamarin Navigation From One Page To Another Page Using ViewModel

Example : 

Navigation to Other Page.

Navigating to another Page From App.xaml.cs (Start Up)

var navigationService = DependencyService.Resolve<INavigationService>();
navigationService.NavigateToAsync<LoginViewModel>(isRootPage: true);

Navigating to another Page From ViewModel

NavigationService.NavigateToAsync<LoginViewModel>(isRootPage: true);


Navigating to another Page With Parameter

var obj = new 
{
     firstName="Hello",
     age = 26
}
NavigationService.NavigateToAsync<LoginViewModel>(obj,isRootPage: false);

Accessing Passed Parameter

All ViewModels are inherited BaseViewModel and it contains Initialize method that you just need to override in All ViewModels.

protected internal override void Initialize(object navigationData, Page uiRef = null)
{
     base.Initialize(navigationData);
     string firstName = TryGetValue(navigationData,nameof(firstName));
     int age = TryGetValue<int>(navigationData,nameof(age));
}

Naming Convention of Page & ViewModel

Page Name: Any Name Append With View
ViewModel: Same as page Name Append With Model.
Example: LoginView is page name and LoginViewModel is ViewModel Name.
              TestView is page name and TestViewModel is ViewModel Name.


Now Create INavigationService Interface that Includes the following methods.

INavigationService.cs

 public interface INavigationService
 {
        T GetPageViewModel<T>() where T : new();
        //T GetPageView<T>() where T : Page, new();
        Task RemovePages<T>() where T : new();
        Task NavigateToAsync<TViewModel>(object parameter = null, bool isRootPage = false, bool isPopupPage = false, bool isSubscribeOnAppear = false, bool isFetchUI = false) where TViewModel : BaseViewModel;
        void InsertPageBeforeLastPage<TViewModel>(object parameter = null, bool isSubscribeOnAppear = false, bool isFetchUI = false) where TViewModel : BaseViewModel;
 }


Usage:

T GetPageViewModel<T>() where T : new();
This method used for getting page view model object from Navigation List.
Task RemovePages<T>() where T : new();

This method will removed all pages till it reach the specified ViewModel Name.

Task NavigateToAsync<TViewModel>(object parameter = null, bool isRootPage = false, bool isSubscribeOnAppear = false, bool isFetchUI = false) where TViewModel : BaseViewModel;

This method used for navigating from one page to another page.

parameter: It will accept any type of object that you want to bring to another page.

isRootPage: It will set the page as a root page, like App.Current.MainPage = new NavigationPage(Page Name)

isSubscribeOnAppear: This Will Subscribe OnAppear() Method of page.

isFetchUI: This flag used for getting view reference in ViewModel.

TViewModel: BaseViewModel: Create BaseViewModel Class that contains Some Common Property and INavigationService Declaration.

          

BaseViewModel.cs

public class BaseViewModel : INotifyPropertyChanged
{
        #region Properties
        private string _title;
        public string Title
        {
            get => _title;
            set => SetProperty(ref _title, value);
        }
        private bool _isBusy;
        public bool IsBusy
        {
            get => _isBusy;
            set => SetProperty(ref _isBusy, value);
        }
        private bool _isRefreshing;
        public bool IsRefreshing
        {
            get => _isRefreshing;
            set => SetProperty(ref _isRefreshing, value);
        }
        public bool IsRefreshData { get; set; } 
        #endregion
 
        #region Services
        protected readonly INavigationService NavigationService;
        #endregion
 
        #region Commands
        public ICommand ItemSelectedCommand { get; protected set; }
        public ICommand RefreshCommand { get; protected set; }
        #endregion
 
        #region Construtor
        public BaseViewModel()
        {
            NavigationService = DependencyService.Resolve<INavigationService>();
        }
        #endregion
 
        #region Methods
        protected internal virtual void Initialize(object navigationData, Page uiRef = null) { }
 
        protected internal virtual void OnAppearing() { }
        public static T TryGetValue<T>(object navigationData, string propertyName = "") where T : new()
        {
            var returnValue = new T();
            try
            {
                if (navigationData != null)
                {
                    if (string.IsNullOrWhiteSpace(propertyName))
                    {
                        returnValue = (T)navigationData;
                    }
                    else
                    {
                        returnValue = (T)navigationData.GetType().GetProperty(propertyName).GetValue(navigationData);
                    }
                }
            }
            catch (Exception ex)
            {
 
            }
            return returnValue;
        }
        public static string TryGetValue(object navigationData, string propertyName = "")
        {
            var returnValue = string.Empty;
            try
            {
                if (navigationData != null)
                {
                    if (string.IsNullOrWhiteSpace(propertyName))
                    {
                        returnValue = (string)navigationData;
                    }
                    else
                    {
                        returnValue = (string)navigationData.GetType().GetProperty(propertyName).GetValue(navigationData);
                    }
                }
            }
            catch (Exception ex)
            {
 
            }
            return returnValue;
        }
        #endregion
 
        protected bool SetProperty<T>(ref T backingStore, T value,
           [CallerMemberName] string propertyName = "",
           Action onChanged = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;
 
            backingStore = value;
            onChanged?.Invoke();
            OnPropertyChanged(propertyName);
            return true;
        }
 
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;
 
            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
}


Create NavigationService class that Implement INavigationService.cs Interface.

NavigationService.cs

public class NavigationService : INavigationService
{
        public async Task NavigateToAsync<TViewModel>(object parameter = null, bool isRootPage = false, bool isPopupPage = false, bool isSubscribeOnAppear = false, bool isFetchUI = false) where TViewModel : BaseViewModel
        {
            Type viewModelType = typeof(TViewModel);
            Type pageType = GetPageTypeForViewModel(viewModelType);
 
            if (pageType == null)
                throw new Exception($"Cannot locate page type for {viewModelType}");
 
            try
            {
                BaseViewModel viewModel = Activator.CreateInstance(viewModelType) as BaseViewModel;
                ContentPage page = Activator.CreateInstance(pageType) as ContentPage;
                page.BindingContext = viewModel;
                if (isRootPage)
                    Application.Current.MainPage = new NavigationPage(page);
                else
                {
                    var navigationPage = Application.Current.MainPage as NavigationPage;
                    if (navigationPage != null)
                        await navigationPage.PushAsync(page, true);
                    else
                        Application.Current.MainPage = new NavigationPage(page);
                }
 
                if (!isFetchUI)
                    viewModel.Initialize(parameter);
                else
                    viewModel.Initialize(parameter, page);
 
                if (isSubscribeOnAppear)
                {
                    page.Appearing -= new EventHandler(OnAppearing);
                    page.Appearing += new EventHandler(OnAppearing);
                }
            }
            catch (Exception ex)
            {
 
            }
        }
 
        private void OnAppearing(object sender, EventArgs e)
        {
            var viewModel = ((Page)(sender)).BindingContext as BaseViewModel;
            if (viewModel.IsRefreshData) viewModel.OnAppearing();
        }
 
        private Type GetPageTypeForViewModel(Type viewModelType)
        {
            var viewModelAssemblyName = viewModelType.GetTypeInfo().Assembly.FullName;
 
            var viewName = viewModelType.FullName.Replace("ViewModel", "View");
            var viewAssemblyName = string.Format(
                        CultureInfo.InvariantCulture, "{0}, {1}", viewName, viewModelAssemblyName);
            var viewType = Type.GetType(viewAssemblyName);
            return viewType;
        }
        public T GetPageViewModel<T>() where T : new()
        {
            var mainPage = Application.Current.MainPage as NavigationPage;
            var pageNeedToReturn = mainPage.Navigation.NavigationStack.Where(f => f.BindingContext.GetType() == typeof(T)).FirstOrDefault();
            if (pageNeedToReturn != null)
            {
                return (T)pageNeedToReturn.BindingContext;
            }
            return default(T);
        }
 
        //public T GetPageView<T>() where T : Page, new()
        //{
        //    var mainPage = Application.Current.MainPage as NavigationPage;
        //    var pageNeedToReturn = mainPage.Navigation.NavigationStack.Where(f => f.GetType() == typeof(T)).FirstOrDefault();
        //    if (pageNeedToReturn != null)
        //    {
        //        return (T)pageNeedToReturn;
        //    }
        //    return default(T);
        //}
 
        public void InsertPageBeforeLastPage<TViewModel>(object parameter = null, bool isSubscribeOnAppear = false, bool isFetchUI = false) where TViewModel : BaseViewModel
        {
            Type viewModelType = typeof(TViewModel);
            Type pageType = GetPageTypeForViewModel(viewModelType);
 
            if (pageType == null)
                throw new Exception($"Cannot locate page type for {viewModelType}");
 
            try
            {
                BaseViewModel viewModel = Activator.CreateInstance(viewModelType) as BaseViewModel;
                Page page = Activator.CreateInstance(pageType) as Page;
                page.BindingContext = viewModel;
                var lastPage = App.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
 
                if (!isFetchUI)
                    viewModel.Initialize(parameter);
                else
                    viewModel.Initialize(parameter, page);
 
                if (isSubscribeOnAppear)
                {
                    page.Appearing -= new EventHandler(OnAppearing);
                    page.Appearing += new EventHandler(OnAppearing);
                }
                App.Current.MainPage.Navigation.InsertPageBefore(page, lastPage);
            }
            catch (Exception ex)
            {
 
            }
        }
 
        /// <summary>
        /// Specified the page name (it will remove pages till specified page name)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task RemovePages<T>() where T : new()
        {
            var returnValue = new T();
            var pages = App.Current.MainPage.Navigation.NavigationStack.ToList();
 
            for (int maxPageIndex = pages.Count - 1; maxPageIndex >= 0; maxPageIndex--)
            {
                var currnetPage = pages[maxPageIndex];
                if (currnetPage != null)
                {
                    if (currnetPage.BindingContext.GetType() == returnValue.GetType())
                    {
                        break;
                    }
                    else
                    {
                        App.Current.MainPage.Navigation.RemovePage(currnetPage);
                    }
                }
            }
            await App.Current.MainPage.Navigation.PopAsync();
  }
 


Registration of NavigationService on App.xaml.cs

DependencyService.Register<INavigationService, NavigationService>();


No comments:

Post a Comment

Popular Posts