2016-03-23 22 views

Antwort

6

Nein. Es ist nicht eingebaut, aber es ist einfach, es zu verbessern. Normalerweise navigieren MvvM-Frameworks mit ViewModels. Caliburn folgt diesem Muster. Es braucht also eine Art Navigationsdienst. Dieser Navigationsservice ist verantwortlich für die Erstellung der Views für die ViewModels und ruft die View-Framework (Xamarin.Froms in unserem Fall) spezifische Navigationsfunktionen auf. NavigationPageAdapter ist das, was wir suchen. Jetzt verbessern wir es.

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

Wir erklärten nur die Schnittstelle IModalNavigationService die INavigationService und implementieren sie in unserer ModalNavigationPageAdapter erstreckt. Leider hat Caliburn viele Funktionen privat gemacht, daher müssen wir sie in unsere geerbte Version kopieren.

In Calibrn können Sie über navigationservice.For<VM>().Navigate() navigieren. Wir wollen diesem Stil folgen, also müssen wir etwas wie navigationservice.ModalFor<VM>().Navigate() implementieren, was wir in einer Erweiterungsmethode machen.

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

Diese Methode gibt ein ModalNavigateHelper, das die Nutzung unseres Navigationsdienstes (ähnlich Caliburn des NavigateHelper) vereinfacht. Es ist fast eine Kopie, aber für die 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); 
    } 
} 

Last but not least, müssen wir unseren glänzenden neuen Navigationsdienst anstelle des alten verwenden. Die Klasse App registriert die NavigationPageAdapter für die INavigationService als Singleton in PrepareViewFirst. Wir haben es zu ändern, wie

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

folgt wir unsere Navigationsdienst Anmeldung erfolgt für INavigationService und IModalNavigationService.

Wie Sie im Kommentar sehen können, müssen Sie Close-Funktionen implementieren, die PopModalAsync selbst aufrufen.

Verwandte Themen