2016-03-23 31 views

回答

6

不是,它不是內置的,但很容易增強它。通常,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(); 
     } 
    } 
} 

我們剛剛宣佈延長INavigationService在我們ModalNavigationPageAdapter實現它的接口IModalNavigationService。不幸的是,Caliburn使得很多函數都是私有的,所以我們必須將它們複製到我們的繼承版本中。

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

該方法返回一個ModalNavigateHelper,簡化了我們的導航服務的使用(類似於卡利的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班正在登記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); 
    } 
} 

我們註冊我們的導航服務INavigationServiceIModalNavigationService去改變它。

正如您在評論中看到的那樣,您必須實現自己調用PopModalAsync的關閉函數。