How to present a modal Login screen using ViewModel First navigation in Xamarin

懵懂的女人 提交于 2021-01-28 08:16:20

问题


So I am using the FreshMVVM Xamarin.Forms library so I can do ViewModel-first navigation in xamarin. My research tells me that this is a highly recommended practice, yet there seem to be not a whole lot of tutorials for this.

What I am trying to achieve is a login page that pops up as a modal once the user opens the app if they are not signed in.

I have modified the demo project slightly by adding the modal display button and associated Command from the ViewModel to the first page, however, I am having trouble invoking this programmatically.

Here is what I am trying to so along with the relevant Command code that I intend to tweak to work with a Login Page:

public NormalOneViewModel()
{
    if (/*user is not already logged in*/) {
        //display the login page as a modal to get the users credentials and log them in
        //see below
    }   
}
...
ICommand _navToChild;
public ICommand NavigatePopup
{
    get
    {
        if (_navToChild == null)
        {
            _navToChild = new Command(async () =>
            {
                await _navService.PushModalAsync<NormalModalViewModel>();
            });
        }
        return _navToChild;
    }
}

This Command is invoked via <Button Text="Show Modal" Command="{Binding NavigatePopup}"/> in the view associated with the viewModel above.

I have:

  • tried to call the command programmatically using NavigatePopup.Execute(null);. This fails with an error explaining that the key cannot be found.
  • tried to call the command programmatically using NavigatePopup.Execute();. This yields the following error:
  • tried using the custom NavigationService that I have set up for this app by calling the await NavigationService.Instance.PushModalAsync<NormalModalViewModel>(); line from the constructor of the detail page's ViewModel. This tells me that I need to make the method async. Additionally, don't really understand xamarin async too well since I am fairly new to Xamarin.Forms and C#
  • Navigation.PushModalAsync(new LoginPage()); seems to work, but only in the contractor of the code-behind of the MasterDetailPage. this is also using the Navigation Class which is apparently built-into xamarin. but this method only accepts a Page and doesn't really conform to my ViewModel-First navigation strategy.

What I am primarily wondering is how do I programmatically invoke a modal (for a login page) when using ViewModel-first patterns?

EDIT 1: I am using Visual Studio for Mac

Apologies for the really confusing setup.


回答1:


Okay, so first off, apologies for the confusion regarding the way I presented this and why I was using PushModalAsync<NormalModalViewModel> in I wanted to display a Login page. The reason for this is I was trying to modify the sample code in the repositories linked in the question in order to see how I would do this before transplanting it into the project I am working on.

My Solution

After tinkering with this for quite a while, I realized that the reason .Execute(); was failing when I was calling it from the constructor with the key not found error was because I was calling it from the constructor. Even after separating the navigation command into a separate method instead of using a lambda (see below) and calling that from the constructor wouldn't work because, from my understanding, the page hadn't been displayed yet and my NavigationService therefore hadn't registered it or something.

ICommand _loginNav;
public ICommand NavigatePopup
{
    get
    {
        if (_loginNav == null)
        {
            _loginNav = new Command(async () => await NavigateModalLogin());
        }
        return _loginNav;
    }
}

async Task NavigateModalLogin()
{
    await _navService.PushModalAsync<NormalModalViewModel>();
}

while using Navigation.PushModalAsync(new LoginPage()); did work for me when calling it from the constructor, it wasn't using my custom navigation and I didn't really like that. So I continued looking for a better way.

Considering my realization from earlier that doing this check in the constructor wasn't going to work, I had the idea to use the OnAppearing() and OnDisappearing() methods provided by the Page class in Xamarin.Forms.

However, since I am using ViewModel-first navigation, the processing of the login would need to be done in the ViewModel. So I wrote this in the code-behind of the page to effectively pass the execution to the ViewModel so I could use it:

protected async override Task OnAppearing()
{
    await viewmodel.OnAppearing();
    base.OnAppearing();
    System.Diagnostics.Debug.WriteLine("*****Here*****");
}

Then in the ViewModel I had the OnAppearing() method call the NavigateModalLogin() function from earlier (yes I know this can be optimized to avoid the extra method call but whatever).

The good part about this is that the OnAppearing() method is an Event Handler in Xamarin. Which means it supports async. Which means I didn't have to figure out how to start off an async/await chain or however that works.

Tl;dr

Use the event handlers that Xamarin provides to call the same method that the command does (the event Handlers can be either synchronous or asynchronous).

Code:

In the code-behind for your page:

protected async override Task OnAppearing()
{
    await viewmodel.OnAppearing();
    base.OnAppearing();
    System.Diagnostics.Debug.WriteLine("*****Here*****");
}

in the ViewModel for your page:

internal async Task OnAppearing()
{
    await NavigateModalLogin();
}

ICommand _loginNav;
public ICommand NavigatePopup
{
    get
    {
        if (_loginNav == null)
        {
            _loginNav = new Command(async () => await NavigateModalLogin());
        }
        return _loginNav;
    }
}

async Task NavigateModalLogin()
{
    await _navService.PushModalAsync<NormalModalViewModel>();
}


来源:https://stackoverflow.com/questions/49480702/how-to-present-a-modal-login-screen-using-viewmodel-first-navigation-in-xamarin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!