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
相关产品推荐
相关产品推荐

