Why does await _navigationService.NavigateAsync not work in OnNavigatedTo function - xamarin

Been at this for days. Very simple logic.
Jump to Home page if bla = true skipping the login page.
I've stripped down the code to demo this.
Login button navigates as normal.
However Login page should actually be skipped as OnNavigatedTo has same NavigateAsync command.
Side affect which makes no sense is HomeViewModel code gets run when the OnNavigatedTo function runs.
public class LoginViewModel : AppMapViewModelBase
{
private readonly INavigationService _navigationService;
public DelegateCommand LoginCommand { get; private set; }
public LoginViewModel(INavigationService navigationService) : base(navigationService)
{
LoginCommand = new DelegateCommand(LoginUserAsync);
_navigationService = navigationService;
}
private async void LoginUserAsync()
{
//This works as expected
await _navigationService.NavigateAsync("/MasterDetail/NavigationPage/Home");
}
public override async void OnNavigatedTo(INavigationParameters parameters)
{
base.OnNavigatedTo(parameters);
//This Executes the code on Home but does not Jump there.
await _navigationService.NavigateAsync("/MasterDetail/NavigationPage/Home");
}
}
The HomeViewModel code which gets run somehow without ever going to the Home page.
public HomeViewModel(INavigationService navigationService) : base (navigationService)
{
Debug.WriteLine("............HomeViewModel Loaded.................................");
}

Changing the Main OnInitialized in App from
await NavigationService.NavigateAsync("NavigationPage/Login");
to
await NavigationService.NavigateAsync("/Login");
stops NavigateAsync("/..... from being passed through App-OnInitialized
No idea why but it works.

Related

Is there a way to come back on last page in Navigation stack, and not execute OnApearing method in Xamarin?

So, I have Page1, with some input fields that my user already filled, and, there is a button that opens a new Page (let's call it Page2).
This is the code I used to go on Page2.
Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(new Page2()));
This is the code I used to came back on Page1.
private async void GoBackButton_Clicked(object sender, EventArgs e)
{
await Application.Current.MainPage.Navigation.PopModalAsync();
}
Now, I'd like to, somehow, when user finishes what he had on Page2, he presses the button, called GoBack, then he comes back on the Page1, and the OnApearing method of Page1's ViewModel is NOT getting executed. Is this doable?
Not sure if important, but I'm using VS22, on Windows10.
This is what I'm using in some of my apps. The logic is: Page1 loads data on the first load and then only reloads data when reload requested.
Here's an example:
Page1.xaml.cs
public partial class Page1 : ContentPage
{
private bool _isFirstLoad = true;
private bool _isReloadRequested;
public Page1()
{
InitializeComponent();
}
private async Task OpenPage2Async()
{
//navigate to Page2
await Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(new Page2(() => _isReloadRequested = true)));
}
protected override void OnAppearing()
{
if (_isFirstLoad)
{
_isFirstLoad = false;
ReloadData();
return;
}
if (_isReloadRequested)
{
ReloadData();
_isReloadRequested = false;
}
}
private void ReloadData()
{
//reload data...
}
}
Page2.xaml.cs
public partial class Page2 : ContentPage
{
private readonly Action _requestReload;
public Page2(Action requestReload)
{
InitializeComponent();
_requestReload = requestReload;
}
private async Task GoBackAsync()
{
//invoke callback to set Page1's _isReloadRequested to true
_requestReload?.Invoke();
//go back to Page1
await Application.Current.MainPage.Navigation.PopModalAsync();
}
}

Getting initial data pattern in Xamarin Forms

I'm trying to understand the pattern to use in Xamarin Forms when a page gets its initial data from a web API.
The page is tied to a ViewModel. Let's use this simple example:
public class DataFeedViewModel : BaseViewModel
{
public DateFeedViewModel()
{
Title = "My Feed";
}
public List<FeedItem> Feed { get; set; }
}
The DataFeedViewModel is bound to the page:
public MainPage()
{
InitializeComponent();
this.BindingContext = new DataFeedViewModel();
}
As I understand it, I use the OnAppearing() method to fetch my initial set of data from the backend API:
protected override async void OnAppearing()
{
base.OnAppearing();
var result = await _myApiService.GetFeed();
// What's next? Do I simply do the following?
// new DataFeedViewModel
// {
// Feed = result
// }
}
Also a second but very important question is whether this pattern is the recommended approach.
As I learn about Xamarin and .NET Maui, I understand, the trend is to go from an event driven model to a more MVVM command driven approach.
I'm a bit confused about how to use a ViewModel to tap into these life cycle methods such as OnAppearing().
create an Init method on your VM
public class DataFeedViewModel : BaseViewModel
{
public DateFeedViewModel()
{
Title = "My Feed";
}
public List<FeedItem> Feed { get; set; }
public async void Init()
{
Feed = await _myApiService.GetFeed();
}
}
and then have your page call it
private DataFeedViewModel VM { get; set; }
public MainPage()
{
InitializeComponent();
this.BindingContext = VM = new DataFeedViewModel();
}
protected override async void OnAppearing()
{
base.OnAppearing();
await VM.Init();
}

BindingContext not working for navigation in a button clicked event

I am attempting to navigate to a registration page from a login page using a button_clicked event. A new view model is created during the event and set as the BindingContext. However it does not appear to work
When I debug on the registration page I have an ICommand in the registration page view model that is attached to a button. The code is
public ICommand RegisterAppUser
{
get
{
return new RelayCommand(RegisterUser, () => Email == ConfirmEmail && Password == ConfirmPassword);
}
}
private async void RegisterUser()
{
var userDto = new RegistrationUserDto(this);
App.User = await App.PlayerManager.RegisterUserAsync(userDto);
}
I assumed the the "this" would represent the view model attached to the view. However when I reach the RegistrationUserDto(this) line I can see that the "this" is an unintialized instance of the registration view model
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new RegistrationView() { BindingContext = new RegistrationViewModel() }); ;
}
}
No compilation errors but the action clearly can't work with no data

ICommand Xamarin Forms

I have a strange issue of getting null exception. I created class that implements ICommand interface, I have two methods.
public void Execute(object parameter)
{
NavigateAsync();
}
private async void NavigateAsync()
{
await App.MainNavigation.PushAsync(new Pages.SettingsPage());
}
When NavigateAsync() is exectude my MainNavigation is always null, even that I can see that parameter inside Execute is set.
In my App.xaml.cs file I have created public static INavigation MainNavigation { get; set; }
public partial class App : Application
{
public static ViewModels.MainViewModel ViewModel { get; set; }
public static INavigation MainNavigation { get; set; }
public App ()
{
InitializeComponent();
MainPage = new NavigationPage(new Paperboy.MainPage());
}
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
protected override void OnResume ()
{
// Handle when your app resumes
}
}
Se when clicking icon i can se that command is executed but App.MainNavigation inside NavigateAsync() in null. So command is not executing PushAsync to SettingsPage.
You never instantiate your static MainNavigation property...
If I can give you 2 remarks:
instead of using a static property declared into your App.xaml.cs, maybe a better implementation could be to embed a Navigation getter into a specific 'service' class or directly into your Command definition:
public class MyCommand : ICommand
{
...
// Navigation getter
// There are better places for this prop but it's better
// than in app.xaml.cs
private INavigation MainNavigation
{
get => Application.Current?.MainPage?.Navigation;
}
public void Execute(object parameter)
{
NavigateAsync();
}
private async void NavigateAsync()
{
try
{
await MainNavigation?.PushAsync(new Pages.SettingsPage());
}
catch(){ ... }
}
}
Another point I would like to note, is that your Command looks like asynchronous. Maybe you already know, but here is a good implementation of async commands to avoid app crashes: Asynchronous commands
I hope it can help you. Happy coding !

Windows Phone Page navigation in MVVM

I am using MVVM in one of the app. I have created different project for Model, View and View Model.
I need to navigate to another XAML from ViewModel. I found some solution using MVVM light. Is there any way of implementing navigation from view model without using MVVM light.
Simple as that,
IF you want to navigate from page1 to page2,
private void MoveToPage2FromPage1()
{
NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
}
How to perform page navigation on Windows Phone 8
You can store current page url on a notify property of Shared ViewModel in App. After that, it is easy to catch the change of this url and navigate to the correct url by observing it.
public class AppViewModel : INotifyPropertyChanged
{
public string CurrentPageURL { get; set; }
private string _currentPageURL;
public string CurrentPageURL
{
get { return _currentPageURL;}
set
{
if (_currentPageURL==value)
return; // to prevent reload the same page.
_currentPageURL = value;
NotifyPropertyChangedCurrentPageURL
}
}
// INotifyPropertyChanged implementations
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
// Store in static singleton instance of AppViewModel
public class App : Application
{
private static Lazy<AppViewModel> _ViewModel=new Lazy<ViewModel>();
public static AppViewModel ViewModel { get { return _ViewModel.Value; } }
....
public App()
{
AppViewModel.PropertyChanged=(s,a) =>
{
if (a.PropertyName=="CurrentPageURL")
{
NavigationService.Navigate(new Uri(AppViewModel.CurrentPageURL, UriKind.Relative));
};
}
}
}
// Usage sample
public class Page1ViewModel
{
private btnMoveNextPage_Click(object s, EventHandler a) {
App.ViewModel.CurrentURL="~/Page2.xaml";
}
}

Resources