【问题标题】:View Doesn't Change During Xamarin Forms Prism NavigationXamarin Forms Prism 导航期间视图不会更改
【发布时间】:2017-09-14 19:12:40
【问题描述】:

我的背景是 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“打破”了这种方法......

提前致谢!

【问题讨论】:

  • 如果没有完整的复制,就很难准确地说出您遇到问题的原因。我会说,尽管您可以将代码导航到 UserLoginPage 简化为_navigationService.NavigateAsync($"UserLoginPage?ChosenDealer={ChosenDealer}")。您可以查看gist 了解调试此问题的一些技巧
  • @DanS。 - 这个要点非常有帮助 - 这种方法有助于暴露在运行时被吞下的异常。我看到System.InvalidOperationException: The current type, Prism.Navigation.INavigationService, is an interface and cannot be constructed. Are you missing a type mapping?。我还没有找到一个快速的答案,但至少这个问题似乎得到了更好的记录。除非您有任何进一步的建议,否则我会在找到解决方案时更新帖子。
  • 我不确定您提供的代码是否与您的解决方案中的代码完全相同,但这听起来更像是您有错字,并且您的 UserLoginPageViewModel 没有准确命名 NavigationService navigationService
  • @DanS。 - 我想也可能是这样,但这是一个精确的剪切和粘贴(省略了一些东西),我仔细检查了。如果我只用静态测试代码替换 OnNavigatingTo 中的异步代码以制作字符串列表并将其分配给该属性,那么问题就会消失,因此异步运行 OnNavigatingTo 代码可能是禁忌,或者我有一个那里的错误?但是,当我设置断点时,我得到了我期望的数据。即使导航按预期工作(因为我使用的是虚拟代码),我仍然得到 InvalidOperationException,所以这可能是一个红鲱鱼......

标签: xamarin.forms prism


【解决方案1】:

要处理设计时/模拟服务与生产服务,您可以使用多种技术。但是你会想要一种在它们之间轻松切换的方法,为此我建议使用编译器符号。您需要在项目中创建一个新的构建配置文件,例如,您可以创建一个名为 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>

【讨论】:

  • 谢谢!我从来没有玩过这样的东西,但它看起来很有希望。我需要尽快收工,但如果我能让它工作,我明天会告诉你。希望 Xamarin 或 Prism 在未来的修订版中为此内置一些内容,但您的建议看起来可行。
【解决方案2】:

我想我已经解决了这个问题,这与 Prism 或 Xamarin.Forms 无关;我只是对我的 MVVM 技能集生疏了。

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

2) 我应该记得,这仍然不起作用,因为即使填充了列表,视图中的 ListView 也显示为空。要解决此问题,我必须添加 using System.Collections.ObjectModel; 并将 List&lt;string&gt; 更改为 ObservableCollection&lt;string&gt;

** 注意 ** 这解决了我的大部分问题 - 我将正确答案授予 Dan S.,感谢他在故障排除方面的帮助以及他对我的额外学分问题的有效解决方案!

【讨论】:

    猜你喜欢
    • 2016-06-06
    • 1970-01-01
    • 2018-02-09
    • 2016-11-21
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多