My background is WPF (with MVVMLight Toolkit) and WinForms, but I am new to Xamarin Forms and Prism. I am trying to implement a two-step login for an app that I am porting from Swift. In step one, the user types a customer ID (each of our customers has many employees.) In step two, they are shown a list of users for that customer ID, and much choose their name and enter a password.
I am using Xamarin Studio 6.3 for Mac, Xamarin Forms 2.3.4.231, and Prism/Unity 6.3.0
My problem is that when I type the customer ID and click submit, the "OnNavigatingTo" code in the second login view runs, and I successfully retrieve the user list from my web service (this code is in the second ViewModel), but the second view never appears. When I step through code, all of the business logic runs as expected, but from the user's perspective, the view never changes. I am sure I am missing something simple, but I haven't found any help after reading through multiple blog posts on Prism navigation and looking through similar questions on S.O.
app.xaml.cs:
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync("DealerLoginPage");
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<DealerLoginPage>();
Container.RegisterTypeForNavigation<UserLoginPage>();
Container.RegisterTypeForNavigation<SalesSummaryPage>();
// Uncomment this section for design data
//Container.RegisterType<ISalesDataRepo, DesignSalesDataRepo>();
// Uncomment this section for runtime data
Container.RegisterType<ISalesDataRepo, AzureSalesDataRepo>();
}
}
DealerLoginPageViewModel.cs (step one of login - also initial view/VM)
public class DealerLoginPageViewModel : BindableBase
{
private string _chosenDealer;
public string ChosenDealer
{
get { return _chosenDealer; }
set { SetProperty(ref _chosenDealer, value); }
}
private INavigationService _navigationService;
public DelegateCommand Submit { get; set; }
public DealerLoginPageViewModel()
{
}
public DealerLoginPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
Submit = new DelegateCommand(Submit_Clicked);
}
void Submit_Clicked()
{
NavigationParameters parameters = new NavigationParameters();
parameters.Add("ChosenDealer", ChosenDealer);
_navigationService.NavigateAsync("UserLoginPage", parameters);
}
}
UserLoginViewModel.cs (step two of login process - navigation target)
public class UserLoginPageViewModel : BindableBase, INavigationAware
{
private List<string> _dealerUsers;
public List<string> DealerUsers
{
get { return _dealerUsers; }
set { SetProperty(ref _dealerUsers, value); }
}
private string _selectedUser;
public string SelectedUser
{
get { return _selectedUser; }
set { SetProperty(ref _selectedUser, value); }
}
private string _selectedDealer;
public string SelectedDealer
{
get { return _selectedDealer; }
set { SetProperty(ref _selectedDealer, value); }
}
private INavigationService _navigationService;
private ISalesDataRepo _salesDataRepo;
public DelegateCommand Submit { get; set; }
public UserLoginPageViewModel()
{
}
public UserLoginPageViewModel(INavigationService navigationService, ISalesDataRepo salesDataRepo)
{
_navigationService = navigationService;
_salesDataRepo = salesDataRepo;
Submit = new DelegateCommand(Submit_Clicked);
}
void Submit_Clicked()
{
// unrelated code
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
}
public void OnNavigatingTo(NavigationParameters parameters)
{
// a breakpoint here gets hit - this code runs, but related view never appears!
if (parameters.ContainsKey("ChosenDealer"))
SelectedDealer = (string)parameters["ChosenDealer"];
if (DealerUsers == null)
{
// we have the dealership, but not the dealership's users yet. Get them asynchronously
var getUsers = _salesDataRepo.GetUserListAsync(SelectedDealer);
getUsers.Start();
// set up the listview once we have the users.
var setUsers = getUsers.ContinueWith((antecedent) =>
{
if (getUsers.Status == System.Threading.Tasks.TaskStatus.RanToCompletion)
{
DealerUsers = getUsers.Result;
}
});
}
}
}
I am navigating to the second view (not the ViewModel), and the expected code in the second ViewModel is running successfully, so it seems like everything is wired up correctly. Does anyone see what I'm doing wrong? Why is the view not changing?
* EXTRA CREDIT *: Does anyone know a way in my app.xaml.cs to create an if-else statement to register the appropriate repository depending on whether I am in design mode? I have seen workarounds where you can check whether App.Current == null to determine whether you are in design mode, but apparently Xamarin.Forms 6.2 "broke" that approach...
Thanks in advance!
_navigationService.NavigateAsync($"UserLoginPage?ChosenDealer={ChosenDealer}"). You can check out this gist for some tips on debugging this problem - Dan SiegelSystem.InvalidOperationException: The current type, Prism.Navigation.INavigationService, is an interface and cannot be constructed. Are you missing a type mapping?. I haven't found a quick answer, but that issue seems to be better documented at least. Unless you have any further suggestions, I will update the post when I find a solution. - Dave SmashUserLoginPageViewModeldoes not name the NavigationService exactlynavigationService- Dan Siegel