在Xamarin Forms棱镜导航期间查看不会更改

时间:2017-04-18 19:38:04

标签: xamarin.forms prism

我的背景是WPF(使用MVVMLight Toolkit)和WinForms,但我是Xamarin Forms和Prism的新手。我正在尝试为我从Swift移植的应用程序实现两步登录。在第一步中,用户键入一个客户ID(我们每个客户都有许多员工。)在第二步中,会显示该客户ID的用户列表,并选择他们的名字并输入密码。

我正在使用Xamarin Studio 6.3 for Mac,Xamarin Forms 2.3.4.231和Prism / Unity 6.3.0

我的问题是,当我输入客户ID并点击提交时," OnNavigatingTo"第二个登录视图中的代码运行,我成功从我的Web服务检索用户列表(此代码位于第二个ViewModel中),但第二个视图永远不会出现。当我单步执行代码时,所有业务逻辑都按预期运行,但从用户的角度来看,视图永远不会改变。我确信我遗漏了一些简单的东西,但在阅读了关于Prism导航的多篇博文并在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(登录的第一步 - 也是初始视图/ 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(登录过程的第二步 - 导航目标)

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;
                }
            });
        }
    }

}

我正在导航到第二个视图(而不是ViewModel),并且第二个ViewModel中的预期代码正在成功运行,因此似乎所有内容都已正确连接。有谁看到我做错了什么?为什么观点没有改变?

* EXTRA CREDIT * :根据我是否处于设计模式,是否有人知道我的app.xaml.cs中创建if-else语句来注册相应的存储库?我已经看到了解决方法,你可以检查是否App.Current == null来确定你是否处于设计模式,但显然是Xamarin.Forms 6.2&#34;打破&#34;那种方法......

提前致谢!

2 个答案:

答案 0 :(得分:0)

要处理设计时/模拟服务与生产服务,您可以使用许多技术。但是你需要一种方法在它们之间轻松切换,为此我建议使用编译器符号。您需要在项目中创建新的构建配置文件,例如,您可以创建一个名为Mock的配置文件,然后为该配置文件MOCK定义一个符号。

  • 您可以做的第一件事就是使用编译器指令来注册相应的服务

#if MOCK Container.Register<IFooService,MockFooService>(); #else Container.Register<IFooService,FooService>(); #endif

  • 您可以使用的另一种技术是为文件添加条件编译。此技术将两个文件保存在解决方案资源管理器中,并允许您实际命名类完全相同(尽管文件本身需要以不同方式命名)。

您的csproj文件可能如下所示:

<ItemGroup>
    <Compile Include="Services\MockFooService.cs" Condition=" '$(Configuration)' == 'Mock' " />
    <Compile Include="Services\FooService.cs" Condition=" '$(Configuration)' != 'Mock' " />
</ItemGroup>

答案 1 :(得分:0)

我认为我已经解决了这个问题,它对Prism或Xamarin来说并不具体。形式;我的MVVM技能已经生锈了。

1)因为我正在异步加载数据,所以视图在分配值之前尝试绑定到DealerUsers属性,因此视图可能会抛出并吞下NullReferenceException并且无法加载(这会更容易诊断是否有更多的可见性。)在ViewModel构造函数中,我不得不新建列表(DealerUsers = new List<string>();,然后在异步方法中分配它(DealerUsers = getUsers.Result,而不是需要遍历结果并将getUsers.Result中的每个字符串一次添加到现有(空)列表中。

2)正如我应该记住的那样,这仍然不起作用,因为即使填充了列表,View中的ListView也会显示为空。要解决此问题,我必须添加using System.Collections.ObjectModel;并将List<string>更改为ObservableCollection<string>

**注意**这解决了我的大部分问题 - 我正在向Dan S.提供正确答案,以帮助他排除故障并帮助他解决我的额外信用问题!