【问题标题】:Set Up INavigation with Xamarin.Forms and MVVMLight IOC使用 Xamarin.Forms 和 MVVMLight IOC 设置 INavigation
【发布时间】:2023-04-08 01:03:02
【问题描述】:

我开始将 MVVMLight 与 Xamarin.Forms 一起使用,需要一些帮助来设置 IOC 以在 App.GetMainPage 中创建我的第一个 ContentPage。

我的 ViewModel 有这样的构造函数 -

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

我最近的例外是 -

无法从源类型转换为目标类型。

我是否像这样为我的每个 ViewModel 提供 INavigation 时找错了树?

更新:在几个答案显示了在 Xamarin Forms 中控制导航的其他方法之后,我认为如果有人能澄清为什么尝试为导航注入构造函数不是一件好事,这将有所帮助。

我认为我的示例与作为静态的 AppNavigationPage 有点混淆,理想情况下我希望它也在 IOC 中,所以我可以调用 return ServiceLocator.Current.GetInstance(),但我有一个使用各种工厂方法,并且正在调试它,所以代码显然是半生不熟的......

【问题讨论】:

    标签: mvvm-light xamarin.forms


    【解决方案1】:

    我没有使用 MvvmLight,但我可以告诉你,是的,你在尝试为每个 ViewModel 提供 INavigation 时会发现错误的树。

    实现您想要做的最简单的方法是在您的应用上使用公共静态。

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

    现在,当您使用 MasterDetail 页面时,这会分崩离析,因为您需要 Navigation 来包装 DetailPage,而不是 MasterDetailPage。因此,不要在 GetMainPage 方法中设置Navigation,而是在 MD Page 中设置。

            var master = new MainMenuPage();
            var detail = new NavigationPage(new FirstPage());
    
            if (App.Navigation == null)
            {
                App.Navigation = detail.Navigation;
            }
    
            Master = master;
            Detail = detail;
    

    【讨论】:

    • 谢谢@Chase。为了进一步澄清,您能否详细说明为什么我走错了路?如果我想单独对我的 ViewModel 进行 UnitTest,如果我无法注入它,我将如何将它与 Navigation 分离?
    【解决方案2】:

    如果您想要一个即用型解决方案,而不是 MVVM Light,您可以尝试使用 Xamarin Forms Labs ViewModel base,它会在您的 ViewModel 中注入 Navigation 属性:

    那样你冷做这样的事情:

     public Command NavigateToViewModel 
    {
        get
        {
            return navigateToViewModel ?? (navigateToViewModel = new Command(
                                                                       async () => await Navigation.PushAsync<NewPageViewModel>(),
                                                                       () => true));
        }
    }
    

    【讨论】:

    • 感谢@Rui,我一直在关注您在 Forms Labs 的进展,但没有找到 :) 我将看看您在那里进行的 ViewFactory / ViewModelNavigation 设置。我认为如果有人可以解释为什么导航注入是一件坏事,这会对我有所帮助,我会相应地改变我的问题以帮助解决这个问题。
    • 我不认为这是一件坏事,顺便说一句,我在使用 Xamarin.Forms 之前通常做的事情是一种类似的方法,我将 INAvigationService 注入到视图模型中,我的回应只是为了帮助你如果您愿意,可以节省时间。
    • 感谢 Rui,我可能会在推进过程中使用 Labs 框架,但作为 MVVM 的新手,我想从一开始就了解基本原理。
    【解决方案3】:

    哦,好的,当我更好地查看您的代码时,我可能发现了问题:

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

    应该是:

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

    【讨论】:

    • 我知道你在这里尝试了什么,因为 ContentPage 本身没有实现 INavigation 接口(因此出现转换错误),但 VisualElement 具有 INavigation NavigationProperty。在这种情况下,SimpleIOC 抱怨“Xamarin.Forms.VisualElement 是一个字段,但用作类型”。
    猜你喜欢
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    • 2018-08-03
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-22
    • 1970-01-01
    相关资源
    最近更新 更多