在.NET MAUI Shell(MVVM)中,如何不使用QueryProperty或公共setter接收导航数据?
.NET MAUI Shell导航:规避QueryProperty与公共可变属性的MVVM实践
推荐模式1:ViewModel工厂+构造注入
这种模式通过自定义ViewModel工厂,从Shell导航参数中提取数据后直接注入目标ViewModel的构造函数,无需暴露公共可变属性,完全契合构造注入的MVVM设计原则。
步骤示例:
- 定义ViewModel工厂接口:
public interface IViewModelFactory { T Create<T>(IDictionary<string, object> parameters = null); }
- 实现工厂类,根据导航参数创建ViewModel:
public class ViewModelFactory : IViewModelFactory { private readonly IServiceProvider _serviceProvider; public ViewModelFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public T Create<T>(IDictionary<string, object> parameters = null) { var viewModelType = typeof(T); if (parameters != null && viewModelType == typeof(ProductDetailViewModel)) { if (parameters.TryGetValue("product", out var product) && product is Product selectedProduct) { return (T)Activator.CreateInstance(viewModelType, selectedProduct); } } return _serviceProvider.GetRequiredService<T>(); } }
- 目标ViewModel通过构造函数接收数据:
public class ProductDetailViewModel : BaseViewModel { private readonly Product _selectedProduct; public string ProductName => _selectedProduct.Name; public decimal ProductPrice => _selectedProduct.Price; public ProductDetailViewModel(Product selectedProduct) { _selectedProduct = selectedProduct; } }
- 页面中通过工厂绑定ViewModel:
public partial class ProductDetailPage : ContentPage { public ProductDetailPage(IViewModelFactory viewModelFactory) { InitializeComponent(); if (Navigation.CurrentPage?.NavigationParameter is IDictionary<string, object> parameters) { BindingContext = viewModelFactory.Create<ProductDetailViewModel>(parameters); } } }
推荐模式2:导航感知ViewModel(内部参数提取)
让ViewModel实现自定义导航感知接口,在导航完成后直接从参数中提取数据,仅保留业务所需的私有可绑定属性。
步骤示例:
- 定义导航感知接口:
public interface INavigatedAware { void OnNavigatedTo(IDictionary<string, object> parameters); }
- 目标ViewModel实现接口并处理参数:
public class ProductDetailViewModel : BaseViewModel, INavigatedAware { private string _productName; public string ProductName { get => _productName; private set => SetProperty(ref _productName, value); } private decimal _productPrice; public decimal ProductPrice { get => _productPrice; private set => SetProperty(ref _productPrice, value); } public void OnNavigatedTo(IDictionary<string, object> parameters) { if (parameters.TryGetValue("product", out var product) && product is Product selectedProduct) { ProductName = selectedProduct.Name; ProductPrice = selectedProduct.Price; } } }
- 页面中传递导航参数到ViewModel:
public partial class ProductDetailPage : ContentPage { public ProductDetailPage(ProductDetailViewModel viewModel) { InitializeComponent(); BindingContext = viewModel; } protected override void OnNavigatedTo(NavigatedToEventArgs args) { base.OnNavigatedTo(args); if (args.Parameter is IDictionary<string, object> parameters && BindingContext is INavigatedAware navigatedAware) { navigatedAware.OnNavigatedTo(parameters); } } }
推荐模式3:WeakReferenceMessenger消息传递
使用.NET MAUI内置的WeakReferenceMessenger(替代过时的MessagingCenter),通过消息传递导航数据,适合松散耦合的场景,需注意订阅清理避免内存泄漏。
步骤示例:
- 定义导航消息类型:
public class ProductNavigationMessage : ValueChangedMessage<Product> { public ProductNavigationMessage(Product product) : base(product) { } }
- 发起导航时发送消息:
await Shell.Current.GoToAsync(nameof(ProductDetailPage)); WeakReferenceMessenger.Default.Send(new ProductNavigationMessage(selectedProduct));
- 目标ViewModel订阅消息并处理:
public class ProductDetailViewModel : BaseViewModel { private string _productName; public string ProductName { get => _productName; private set => SetProperty(ref _productName, value); } private decimal _productPrice; public decimal ProductPrice { get => _productPrice; private set => SetProperty(ref _productPrice, value); } public ProductDetailViewModel() { WeakReferenceMessenger.Default.Register<ProductNavigationMessage>(this, (r, m) => { var selectedProduct = m.Value; ProductName = selectedProduct.Name; ProductPrice = selectedProduct.Price; }); } public void Cleanup() { WeakReferenceMessenger.Default.Unregister<ProductNavigationMessage>(this); } }
- 页面销毁时清理订阅:
protected override void OnDisappearing() { base.OnDisappearing(); if (BindingContext is ProductDetailViewModel viewModel) { viewModel.Cleanup(); } }
内容的提问来源于stack exchange,提问作者Kriti Raaj
相关产品推荐
相关产品推荐

