【问题标题】:Xamarin forms 4 shell navigation with complex parametersXamarin 形成 4 种带复杂参数的 shell 导航
【发布时间】:2019-08-19 09:54:58
【问题描述】:

我正在将带有 Prism 的 xamarin forms 3.x 应用程序迁移到带有 shell 导航的表单 4。

我是否必须创建自定义解决方案才能将复杂参数传递到新页面,或者 Xamarin 有一些内置功能可以接收字符串参数以外的内容?

谢谢。

【问题讨论】:

    标签: xamarin.forms xamarin.forms.shell


    【解决方案1】:

    据我所知,阅读文档时,唯一的示例涉及传递简单数据,例如导航时的字符串。

    但是,我能够找到一个问题(和拉取请求),用于传递对象/模型,用于下一个版本(我假设您指的是这种情况)。

    你可以追踪它here

    【讨论】:

    • 我得出了相同的结论,然后创建了自己的 hack 以保持迁移,直到他们实施最终解决方案。谢谢!
    • 此答案中提到的 PR 已关闭。我的猜测是他们会在 MAUI 中重新审视这一点(或者我们可以为这种 MAUI 增强输入问题)
    【解决方案2】:

    我已经运行了一些似乎有效的测试。我对 Xamarin 比较陌生,因此建议谨慎,并欢迎对我可能忽略的任何潜在问题提供任何反馈。

    我为 Shell 编写了一个扩展来接受数据对象参数“navigationData”,如下所示:-

    await Shell.Current.GoToAsync(state, navigationData, animate);
    

    扩展...

    namespace Xamarin.Forms
    {
        public static class ShellExtensions
        {
            public static async Task GoToAsync(this Shell shell, ShellNavigationState state, object navigationData, bool animate=false)
            {
                shell.Navigated += async (sender, e) =>
                {
                    if ((Shell.Current?.CurrentItem?.CurrentItem as IShellSectionController)?.PresentedPage is MyContentPage
                        p) await p.InitializeAsync(navigationData).ConfigureAwait(false);
                };
                await shell.GoToAsync(state, animate);
            }
        }
    }
    

    如上图扩展:-

    1. 挂钩到 Shell 'Navigated' 事件,
    2. 将“当前视图(页面)”检索为“MyContentPage”,即 ContentPage 的子类,
    3. 对传入的视图调用 InitializeAsync 方法 导航数据参数
    4. 然后视图调用 InitializeAsync 绑定上下文(视图模型)上的方法传递 navigationData 参数到 viewModel 上。

    在上面的扩展方法中,'MyContentPage' 是 ContentPage 的自定义抽象子类,具有 InitializeAsync(navigationData) 方法,该方法简单地调用 viewModel 上的类似方法(视图的绑定上下文)。 同样,ViewModels 子类化了具有虚拟 InitializeAsync(navigationData) 的自定义 ViewModelBase 类。这可以在 viewModel 中使用所需的实现和导航数据处理来覆盖。

    视图、视图模型和相关基类的简化示例如下所示

    using System.Threading.Tasks;
    using MyXamarinApp.ViewModels;
    using Xamarin.Forms;
    
    namespace MyXamarinApp.Views
    {
        public ItemDetailPage : MyContent<ItemDetailViewModel>{}
    
        public ItemPage : MyContentPage<ItemViewModel>{}
    
        public abstract class MyContentPage<T> : MyContentPage where T : ViewModelBase
        {
            protected T Vm;
    
            protected override ViewModelBase VmBase => Vm as ViewModelBase;
    
            protected MyContentPage()
            {
                BindingContext = Vm = ViewModelLocator.Resolve<T>();
            }
    
            private Comand _showDetailCommand;
            public Command ShowDetailCommand
            {
                get { return _showDetailCommand ??= new Command(async () => 
                    await Shell.Current.GoToAsync("itemDetail", new NavigationDataObject())); }
            }
        }
    
    
        public abstract class MyContentPage : ContentPage
        {
            protected abstract ViewModelBase VmBase { get; }
    
            public virtual async Task InitializeAsync(object navigationData)
            {
                await VmBase.InitializeAsync(navigationData);
            }
        }
    }
    
    public class NavigationDataObject
    {
        'Properties' etc.
    }
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    
    namespace MyXamarinApp.ViewModels
    {
        public ItemViewModel : ViewModelBase{}
    
        public ItemDetailViewModel : ViewModelBase
        {
            private NavigationDataObject _navData;
            public override async Task InitializeAsync(object navigationData)
            {
                if (navigationData is NavigationDataObject navData)
                {
                    _navData = navData;
                }
                await base.InitializeAsync(navigationData);
            }
        }
    
        public abstract class ViewModelBase
        {
    
            public virtual Task InitializeAsync(object navigationData)
            {
                return Task.FromResult(false);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      您总是可以将模型序列化为 JSON 字符串并在另一端取消序列化它吗?

          async void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
          {
              if (e.Item == null)
                  return;
      
              DailyPnL PnLClicked = (DailyPnL)e.Item;
              string jason = await Task.Run(() => JsonConvert.SerializeObject(PnLClicked));
      
              await Shell.Current.GoToAsync($"viewdailypnl?pnlmodel={jason}");
      
              //Deselect Item
              ((ListView)sender).SelectedItem = null;
          }
      

      然后在你的代码后面:

          public string pnlmodel
          {
              set
              {
                  string derulo = Uri.UnescapeDataString(value);
                  viewModel.PnL =  Task.Run(() => JsonConvert.DeserializeObject<DailyPnL>(derulo)).Result;
      
              }
          }
      

      【讨论】:

      • 这对我有用,我只需要在将 json 字符串附加到 uri 之前对其进行转义:Uri.EscapeDataString(JsonConvert.SerializeObject(PnLClicked))。
      • 我想到了我在互联网上所做的所有事情,我为我的变量名双关感到最自豪!
      【解决方案4】:

      有一个框架叫Xamarin.Zerohttps://github.com/markjackmilian/Xam.Zero 它允许您使用 shell,同时为您提供方便的 ViewModel 到 ViewModel 导航、IOC。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多