Настройка INavigation с помощью Xamarin.Forms и MVVMLight IOC

Я начинаю использовать MVVMLight с Xamarin.Forms, и мне нужна помощь, чтобы настроить IOC для создания моего первого ContentPage в App.GetMainPage.

Мои ViewModels имеют такие конструкторы -

public class NewsType1ViewModel : ViewModelBase
{
    private readonly IContentService contentService;
    private readonly INavigation navigation;

    public List<ContentModel> ContentModels { get; set; }

    public NewsType1ViewModel (INavigation navigation, IContentService contentService)
    {
        this.contentService = contentService;
        this.navigation = navigation;
    }

Мои ContentPages имеют такие конструкторы -

public partial class NewsType1CP : ContentPage
{
    private NewsType1ViewModel vm;

    public NewsType1CP (NewsType1ViewModel vm)
    {
        InitializeComponent ();

Я использую класс ViewModelLocator следующим образом:

public class ViewModelLocator
{
    public ViewModelLocator ()
    {
        ServiceLocator.SetLocatorProvider (() => SimpleIoc.Default);

        // Services 
        if (App.StubMode) {
            SimpleIoc.Default.Register<IContentService, ContentServiceStub> ();
        } else {
            SimpleIoc.Default.Register<IContentService, ContentService> ();
        }

        // How do I wire up INavigation?
        // I could not just register default NavigationPage() as it has 2 
        // possible constructors so tried [PreferredConstructor] on my derived class
        SimpleIoc.Default.Register<INavigation, AppNavigationPage> ();

        // View Models
        SimpleIoc.Default.Register<NewsType1ViewModel> ();
        SimpleIoc.Default.Register<NewsDetailsViewModel> ();
    }

    public NewsType1ViewModel NewsType1ViewModel {
        get {
            return ServiceLocator.Current.GetInstance<NewsType1ViewModel> ();
        }
    }

    public NewsDetailsViewModel NewsDetailsViewModel {
        get {
            return ServiceLocator.Current.GetInstance<NewsDetailsViewModel> ();
        }
    }

    public static void Cleanup ()
    {
        // TODO Clear the ViewModels
    }
}

public class AppNavigationPage : NavigationPage
{
    [PreferredConstructor]
    public AppNavigationPage ()
    {

    }
}

Мой App.cs выполняется следующим образом:

public static class App
{
    public static AppNavigationPage Nav;
    public static ViewModelLocator Locator = new ViewModelLocator ();

    public static bool StubMode = true;

    public static Page GetMainPage ()
    {   
        try {
            // Want to return a Nav with NewsType1CP as the starting Page
            NewsType1CP newsType1CP = new NewsType1CP(ServiceLocator.Current.GetInstance<NewsType1ViewModel> ());
            Nav.PushAsync (newsType1CP);
            return Nav;
        } catch (Exception ex) {
            //
            Exception baseexception = ex.GetBaseException ();
            Debug.WriteLine (baseexception.Message);
        }

        return null;
    }
}

Мое последнее исключение -

Невозможно преобразовать тип источника в тип назначения.

Я лаю не по тому дереву, пытаясь предоставить INavigation для каждой из моих ViewModels?

Обновление: после нескольких ответов, показывающих другие способы управления навигацией в Xamarin Forms, я думаю, было бы полезно, если бы кто-то мог объяснить, почему попытка внедрения конструктора для навигации не такая уж хорошая вещь.

Я думаю, что мой пример немного сбивает с толку AppNavigationPage как статический, в идеале я хотел бы, чтобы он также был в IOC, поэтому я мог бы просто вызвать return ServiceLocator.Current.GetInstance‹ AppNavigationPage >(), но я попробовал различные фабричные методы, и я занимаюсь их отладкой, поэтому код явно наполовину испечен...


person WickedW    schedule 27.07.2014    source источник


Ответы (3)


Если вам нужно готовое к использованию решение вместо MVVM Light, вы можете попробовать использовать Xamarin Forms Labs ViewModel base, он внедряет свойство Navigation в вашу ViewModel:

таким образом, вы холодно делаете что-то вроде этого:

 public Command NavigateToViewModel 
{
    get
    {
        return navigateToViewModel ?? (navigateToViewModel = new Command(
                                                                   async () => await Navigation.PushAsync<NewPageViewModel>(),
                                                                   () => true));
    }
}
person Rui Marinho    schedule 28.07.2014
comment
Спасибо, @Rui, я следил за вашим прогрессом в Forms Labs, но немного не искал :) Я посмотрю на настройку ViewFactory / ViewModelNavigation, которую вы там делаете. Я думаю, мне бы помогло, если бы кто-нибудь объяснил, почему навигационная инъекция - это плохо, я соответствующим образом изменю свой вопрос, чтобы помочь этому. - person WickedW; 28.07.2014
comment
Я не думаю, что это плохо, кстати, то, что я обычно делал перед использованием Xamarin.Forms, было аналогичным подходом, когда я вводил INAvigationService в модель представления, мой ответ был просто помочь вам сэкономить время, если вы хотите. - person Rui Marinho; 28.07.2014
comment
Спасибо, Руи, я вполне могу использовать фреймворк Labs по мере продвижения вперед, но, поскольку я новичок в MVVM, в целом я хотел для начала получить представление о голых костях. - person WickedW; 28.07.2014

Я не использую MvvmLight, но могу сказать вам, что да, вы ошибаетесь, пытаясь предоставить INavigation для каждой ViewModel.

Самый простой способ добиться того, что вы пытаетесь сделать, — использовать общедоступную статику в своем приложении.

public static INavigation Navigation
{
    get;
    set;
}
public static Page GetMainPage()
{
    var firstPage = new NavigationPage(new MyRootPage())
    {
        Navigation = firstPage.Navigation;
        return firstPage;
    }
}

Теперь это разваливается, когда вы используете страницу MasterDetail, потому что вам нужна ваша навигация, чтобы обернуть вашу страницу DetailPage, а не MasterDetailPage. Поэтому не устанавливайте Navigation в методе GetMainPage, а вместо этого из страницы MD.

        var master = new MainMenuPage();
        var detail = new NavigationPage(new FirstPage());

        if (App.Navigation == null)
        {
            App.Navigation = detail.Navigation;
        }

        Master = master;
        Detail = detail;
person Chase Florell    schedule 27.07.2014
comment
Спасибо @Чейз. Чтобы еще больше прояснить ситуацию, не могли бы вы объяснить, почему я на неправильном пути? Если бы я хотел протестировать свою ViewModel изолированно, как бы я отделил ее от навигации, если я не могу ее внедрить? - person WickedW; 28.07.2014

О, хорошо, когда я лучше посмотрел ваш код, я, возможно, заметил проблему:

SimpleIoc.Default.Register<INavigation, AppNavigationPage> ();

Должно ли быть:

SimpleIoc.Default.Register<INavigation, AppNavigationPage.Navigation> ();
person Rui Marinho    schedule 28.07.2014
comment
Я вижу, что вы здесь пытаетесь, поскольку сам ContentPage не реализует интерфейс INavigation (отсюда и ошибка приведения), но VisualElement имеет INavigation NavigationProperty. SimpleIOC жалуется на то, что Xamarin.Forms.VisualElement — это поле, но в данном случае используется как тип. - person WickedW; 28.07.2014