【问题标题】:Caliburn.Micro 3.0 equivalent to Xamarin.Forms Navigation.PushModalAsyncCaliburn.Micro 3.0 相当于 Xamarin.Forms Navigation.PushModalAsync
【发布时间】:2016-03-23 18:02:35
【问题描述】:

Caliburn.Micro 3.0(和 Caliburn.Micro.Xamarin.Forms)是否实现了在 Xamarin.Forms 中模仿/支持 Navigation.PushModalAsync 的功能?

【问题讨论】:

    标签: xamarin xamarin.forms caliburn.micro


    【解决方案1】:

    没有。它不是内置的,但很容易增强它。通常,MvvM 框架由 ViewModel 导航。 Caliburn 正在遵循这种模式。所以它需要某种导航服务。此导航服务负责为 ViewModel 创建视图并调用视图框架(在我们的示例中为 Xamarin.Froms)特定的导航功能。 NavigationPageAdapter 是我们正在寻找的东西。现在让我们对其进行增强。

    public interface IModalNavigationService : INavigationService
    {
        Task NavigateModalToViewModelAsync<TViewModel>(object parameter = null, bool animated = true);
        // TODO: add more functions for closing
    }
    
    public class ModalNavigationPageAdapter : NavigationPageAdapter, IModalNavigationService
    {
        private readonly NavigationPage _navigationPage;
    
        public ModalNavigationPageAdapter(NavigationPage navigationPage) : base(navigationPage)
        {
            _navigationPage = navigationPage;
        }
    
        public async Task NavigateModalToViewModelAsync<TViewModel>(object parameter = null, bool animated = true)
        {
            var view = ViewLocator.LocateForModelType(typeof(TViewModel), null, null);
    
            await PushModalAsync(view, parameter, animated);
        }
    
        private Task PushModalAsync(Element view, object parameter, bool animated)
        {
            var page = view as Page;
    
            if (page == null)
                throw new NotSupportedException(String.Format("{0} does not inherit from {1}.", view.GetType(), typeof(Page)));
    
            var viewModel = ViewModelLocator.LocateForView(view);
    
            if (viewModel != null)
            {
                TryInjectParameters(viewModel, parameter);
    
                ViewModelBinder.Bind(viewModel, view, null);
            }
    
            page.Appearing += (s, e) => ActivateView(page);
            page.Disappearing += (s, e) => DeactivateView(page);
    
            return _navigationPage.Navigation.PushModalAsync(page, animated);
        }
    
        private static void DeactivateView(BindableObject view)
        {
            if (view == null)
                return;
    
            var deactivate = view.BindingContext as IDeactivate;
    
            if (deactivate != null)
            {
                deactivate.Deactivate(false);
            }
        }
    
        private static void ActivateView(BindableObject view)
        {
            if (view == null)
                return;
    
            var activator = view.BindingContext as IActivate;
    
            if (activator != null)
            {
                activator.Activate();
            }
        }
    }
    

    我们刚刚声明了扩展INavigationService 的接口IModalNavigationService 并在我们的ModalNavigationPageAdapter 中实现它。不幸的是,Caliburn 将许多函数设为私有,因此我们必须将它们复制到我们继承的版本中。

    在 caliburn 中,您可以通过 navigationservice.For&lt;VM&gt;().Navigate() 导航。我们想遵循这种风格,所以我们必须实现类似navigationservice.ModalFor&lt;VM&gt;().Navigate() 的东西,我们在扩展方法中这样做。

    public static class ModalNavigationExtensions
    {
        public static ModalNavigateHelper<TViewModel> ModalFor<TViewModel>(this IModalNavigationService navigationService)
        {
            return new ModalNavigateHelper<TViewModel>().AttachTo(navigationService);
        }
    }
    

    此方法返回一个ModalNavigateHelper,简化了我们导航服务的使用(类似于Caliburn 的NavigateHelper)。这几乎是一个副本,但对于IModalNavigationService。

    public class ModalNavigateHelper<TViewModel>
    {
        readonly Dictionary<string, object> parameters = new Dictionary<string, object>();
        IModalNavigationService navigationService;
    
        public ModalNavigateHelper<TViewModel> WithParam<TValue>(Expression<Func<TViewModel, TValue>> property, TValue value)
        {
            if (value is ValueType || !ReferenceEquals(null, value))
            {
                parameters[property.GetMemberInfo().Name] = value;
            }
    
            return this;
        }
    
        public ModalNavigateHelper<TViewModel> AttachTo(IModalNavigationService navigationService)
        {
            this.navigationService = navigationService;
    
            return this;
        }
    
        public void Navigate(bool animated = true)
        {
            if (navigationService == null)
            {
                throw new InvalidOperationException("Cannot navigate without attaching an INavigationService. Call AttachTo first.");
            }
    
            navigationService.NavigateModalToViewModelAsync<TViewModel>(parameters, animated);
        }
    }
    

    最后但同样重要的是,我们必须使用我们闪亮的新导航服务而不是旧导航服务。 App 类正在将NavigationPageAdapter 的INavigationService 注册为PrepareViewFirst 中的单例。我们必须改变它如下

    public class App : FormsApplication
    {
        private readonly SimpleContainer container;
    
        public App(SimpleContainer container)
        {
            this.container = container;
    
            container
                .PerRequest<LoginViewModel>()
                .PerRequest<FeaturesViewModel>();
    
            Initialize();
    
            DisplayRootView<LoginView>();
        }
    
        protected override void PrepareViewFirst(NavigationPage navigationPage)
        {
            var navigationService = new ModalNavigationPageAdapter(navigationPage);
            container.Instance<INavigationService>(navigationService);
            container.Instance<IModalNavigationService>(navigationService);
        }
    }
    

    我们正在为INavigationService 和IModalNavigationService 注册导航服务。

    正如你在评论中看到的,你必须自己实现调用PopModalAsync的关闭函数。

    【讨论】:

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