You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms无MVVM框架:导航与DI标准实现求助

我完全理解你的需求——不用第三方MVVM框架,自己搭建一套Xamarin.Forms里ViewModel导航+可控DI的体系,既要解耦视图和ViewModel,又要避免直接new页面或者硬依赖DependencyService的局限。下面我给你一套完整的实现方案,一步步拆解:

第一步:实现轻量级DI容器(替代DependencyService)

原生DependencyService是静态且功能有限的,我们自己写一个灵活的DI容器,支持单例/瞬时注册,后续还能扩展构造函数注入:

public interface IContainer
{
    void RegisterSingleton<TInterface, TImplementation>() where TImplementation : class, TInterface;
    void RegisterTransient<TInterface, TImplementation>() where TImplementation : class, TInterface;
    void RegisterSingleton<TInterface>(TInterface instance) where TInterface : class;
    TInterface Resolve<TInterface>();
}

public class SimpleContainer : IContainer
{
    private readonly Dictionary<Type, (Type ImplType, bool IsSingleton, object Instance)> _registrations = new();

    public void RegisterSingleton<TInterface, TImplementation>() where TImplementation : class, TInterface
    {
        var interfaceType = typeof(TInterface);
        if (_registrations.ContainsKey(interfaceType))
            throw new InvalidOperationException($"{interfaceType.Name}已被注册");
        
        _registrations[interfaceType] = (typeof(TImplementation), true, null);
    }

    public void RegisterTransient<TInterface, TImplementation>() where TImplementation : class, TInterface
    {
        var interfaceType = typeof(TInterface);
        if (_registrations.ContainsKey(interfaceType))
            throw new InvalidOperationException($"{interfaceType.Name}已被注册");
        
        _registrations[interfaceType] = (typeof(TImplementation), false, null);
    }

    public void RegisterSingleton<TInterface>(TInterface instance) where TInterface : class
    {
        var interfaceType = typeof(TInterface);
        _registrations[interfaceType] = (instance.GetType(), true, instance);
    }

    public TInterface Resolve<TInterface>()
    {
        var interfaceType = typeof(TInterface);
        if (!_registrations.TryGetValue(interfaceType, out var reg))
            throw new InvalidOperationException($"{interfaceType.Name}未注册");

        if (reg.IsSingleton)
        {
            reg.Instance ??= Activator.CreateInstance(reg.ImplType);
            return (TInterface)reg.Instance;
        }
        else
        {
            return (TInterface)Activator.CreateInstance(reg.ImplType);
        }
    }
}

第二步:建立View与ViewModel的映射机制

我们需要一个注册表来关联ViewModel和对应的View,同时给页面写一个基类,自动绑定ViewModel:

// 页面基类,自动接收ViewModel并设置BindingContext
public class BasePage<TViewModel> : ContentPage where TViewModel : class
{
    public TViewModel ViewModel
    {
        get => BindingContext as TViewModel;
        set => BindingContext = value;
    }

    public BasePage(TViewModel viewModel)
    {
        ViewModel = viewModel;
    }
}

// 扩展IContainer,添加View-ViewModel映射注册
public interface IContainer
{
    // 原有方法...
    void RegisterView<TView, TViewModel>() where TView : BasePage<TViewModel> where TViewModel : class;
    Type GetViewTypeForViewModel(Type viewModelType);
}

public class SimpleContainer : IContainer
{
    // 原有私有字段...
    private readonly Dictionary<Type, Type> _viewVmMappings = new();

    public void RegisterView<TView, TViewModel>() where TView : BasePage<TViewModel> where TViewModel : class
    {
        var vmType = typeof(TViewModel);
        if (_viewVmMappings.ContainsKey(vmType))
            throw new InvalidOperationException($"已为{vmType.Name}注册过View");
        
        _viewVmMappings[vmType] = typeof(TView);
    }

    public Type GetViewTypeForViewModel(Type viewModelType)
    {
        if (_viewVmMappings.TryGetValue(viewModelType, out var viewType))
            return viewType;
        
        throw new InvalidOperationException($"未为{viewModelType.Name}注册对应的View");
    }

    // 原有Resolve方法...
}

第三步:实现ViewModel驱动的导航服务

导航服务依赖DI容器,通过ViewModel类型找到对应View,自动注入ViewModel后完成导航:

public interface INavigationService
{
    Task PushViewModelAsync<TViewModel>() where TViewModel : class;
    Task PopViewModelAsync();
    Task PopToRootViewModelAsync();
}

public class NavigationService : INavigationService
{
    private readonly IContainer _container;
    private readonly INavigation _navigation;

    public NavigationService(IContainer container, INavigation navigation)
    {
        _container = container;
        _navigation = navigation;
    }

    public async Task PushViewModelAsync<TViewModel>() where TViewModel : class
    {
        var vmType = typeof(TViewModel);
        var viewType = _container.GetViewTypeForViewModel(vmType);
        
        // 解析ViewModel实例
        var viewModel = _container.Resolve<TViewModel>();
        // 创建View并传入ViewModel
        var view = Activator.CreateInstance(viewType, viewModel) as Page;
        
        await _navigation.PushAsync(view);
    }

    public async Task PopViewModelAsync()
    {
        await _navigation.PopAsync();
    }

    public async Task PopToRootViewModelAsync()
    {
        await _navigation.PopToRootAsync();
    }
}

第四步:完整使用示例

1. 定义业务服务(比如你的ITestService)

public interface ITestService
{
    string GetSystemWelcome();
}

public class TestService : ITestService
{
    public string GetSystemWelcome()
    {
        return "欢迎使用Xamarin.Forms自定义DI+导航体系!";
    }
}

2. 编写ViewModel和View

主页面ViewModel:

public class MainViewModel
{
    private readonly INavigationService _navService;
    private readonly ITestService _testService;

    public string WelcomeMessage { get; }
    public ICommand GoToSecondPageCmd { get; }

    public MainViewModel(INavigationService navService, ITestService testService)
    {
        _navService = navService;
        _testService = testService;
        
        WelcomeMessage = _testService.GetSystemWelcome();
        GoToSecondPageCmd = new Command(async () => await _navService.PushViewModelAsync<SecondViewModel>());
    }
}

主页面View:

public class MainPage : BasePage<MainViewModel>
{
    public MainPage(MainViewModel viewModel) : base(viewModel)
    {
        Content = new StackLayout
        {
            Padding = new Thickness(20),
            Children =
            {
                new Label { Text = ViewModel.WelcomeMessage, FontSize = NamedSize.Large, Margin = new Thickness(0,0,0,20) },
                new Button { Text = "跳转到第二页", Command = ViewModel.GoToSecondPageCmd }
            }
        };
    }
}

第二页ViewModel:

public class SecondViewModel
{
    private readonly INavigationService _navService;

    public ICommand GoBackCmd { get; }

    public SecondViewModel(INavigationService navService)
    {
        _navService = navService;
        GoBackCmd = new Command(async () => await _navService.PopViewModelAsync());
    }
}

第二页View:

public class SecondPage : BasePage<SecondViewModel>
{
    public SecondPage(SecondViewModel viewModel) : base(viewModel)
    {
        Content = new StackLayout
        {
            Padding = new Thickness(20),
            Children =
            {
                new Label { Text = "这是第二页!", FontSize = NamedSize.Large, Margin = new Thickness(0,0,0,20) },
                new Button { Text = "返回首页", Command = ViewModel.GoBackCmd }
            }
        };
    }
}

3. 在App.xaml.cs中完成全局注册

public partial class App : Application
{
    private readonly IContainer _container;

    public App()
    {
        InitializeComponent();

        // 初始化DI容器
        _container = new SimpleContainer();

        // 注册业务服务
        _container.RegisterSingleton<ITestService, TestService>();

        // 初始化导航服务(传入主页面的Navigation实例)
        var tempMainPage = new MainPage(null);
        var navService = new NavigationService(_container, tempMainPage.Navigation);
        _container.RegisterSingleton<INavigationService>(navService);

        // 注册View-ViewModel映射
        _container.RegisterView<MainPage, MainViewModel>();
        _container.RegisterView<SecondPage, SecondViewModel>();

        // 解析主ViewModel并创建主页面
        var mainVm = _container.Resolve<MainViewModel>();
        MainPage = new MainPage(mainVm);
    }
}

核心优势与扩展方向

  • 完全解耦:View和ViewModel通过映射关联,彼此无硬引用,符合MVVM关注点分离
  • 可控DI:自定义容器可灵活扩展(比如添加构造函数注入、生命周期管理),比原生DependencyService更灵活
  • ViewModel驱动:导航只需要指定ViewModel,无需关心具体页面,后续页面变更无需修改ViewModel代码

可优化点:

  • 给DI容器添加构造函数注入支持(通过反射自动解析构造函数参数)
  • 导航服务添加参数传递功能(比如给ViewModel传初始化参数)
  • 扩展ViewModel生命周期方法(如OnNavigatedTo、OnNavigatedFrom)
  • 支持模态导航、TabbedPage等复杂导航场景

内容的提问来源于stack exchange,提问作者flaurens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:40:50