Caliburn.Micro 3.0相当于Xamarin.Forms Navigation.PushModalAsync

nor*_*gie 3 caliburn.micro xamarin xamarin.forms

Caliburn.Micro 3.0(和Caliburn.Micro.Xamarin.Forms)是否实现了模拟/支持Navigation.PushModalAsyncXamarin.Forms的功能?

Sve*_*übe 9

不,它不是内置的,但很容易增强它.通常,MvvM框架由ViewModels导航.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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在校准中,您可以浏览navigationservice.For<VM>().Navigate().我们想要遵循这种风格,所以我们必须实现navigationservice.ModalFor<VM>().Navigate()我们在扩展方法中所做的事情.

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

此方法返回一个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后但同样重要的是,我们必须使用我们闪亮的新导航服务而不是旧的导航服务.所述App类被注册NavigationPageAdapterINavigationService在作为单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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

正如您在注释中看到的那样,您必须实现PopModalAsync自己调用的close函数.