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

MAUI嵌套视图场景下,如何在XAML中使用依赖注入?

问题描述

我正在尝试用MVVM模式嵌套视图,视图的ViewModel通过依赖注入注入。视图和ViewModel已注册为服务,依赖注入本身可以正常工作,OuterView的代码后台和InnerView的XAML已省略。

InnerView的构造函数如下:

public partial class InnerView
{
   public InnerView(InnerViewModel viewModel)
   {
       BindingContext = viewModel;
       InitializeComponent();
   }
}

在OuterView的XAML中直接引用InnerView:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:pages="clr-namespace:Example.Views"
             x:Class="Example.Views.OuterView"
             x:DataType="viewModels:OuterViewModel">
    <VerticalStackLayout x:Name="InnerViewWrapper">
        <pages:InnerView/>
    </VerticalStackLayout>
</ContentPage>

运行时报错:Type 'InnerView' is not usable as an object element because it [...] does not define a parameterless constructor [...],这符合预期。我现在是通过代码后台添加视图:

StockHistoryGraph? view = Handler?.MauiContext?.Services.GetServices<StockHistoryGraph>().FirstOrDefault(defaultValue: default);
if (view is not null) 
    InnerViewWrapper.Add(view);

但这种方式太繁琐,想问:是否可以在XAML文件中直接对视图使用依赖注入?


解决方案

当然可以在XAML中实现依赖注入创建视图,MAUI提供了几种简便的方式:

1. 使用{x:FactoryMethod}调用DI容器获取实例

你可以通过{x:FactoryMethod}调用MauiContext.Services.GetRequiredService<T>()来获取视图实例,需要先给ContentPage指定x:Name以便访问Handler.MauiContext:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:pages="clr-namespace:Example.Views"
             xmlns:di="clr-namespace:Microsoft.Extensions.DependencyInjection;assembly=Microsoft.Extensions.DependencyInjection.Abstractions"
             x:Class="Example.Views.OuterView"
             x:DataType="viewModels:OuterViewModel"
             x:Name="RootPage">
    <VerticalStackLayout>
        <pages:InnerView x:FactoryMethod="{x:Static di:ServiceProviderServiceExtensions.GetRequiredService}"
                         x:Arguments="{x:Type pages:InnerView}"
                         x:FactoryArguments="{Binding Source={x:Reference RootPage}, Path=Handler.MauiContext.Services}"/>
    </VerticalStackLayout>
</ContentPage>

这种方式直接利用DI容器的GetRequiredService方法获取已注册的InnerView实例,无需手动写后台代码。

2. 自定义标记扩展(更简洁)

如果觉得上面的写法太冗长,可以自定义一个标记扩展来简化调用:

首先创建标记扩展类:

using Microsoft.Maui.Controls.Xaml;
using Microsoft.Extensions.DependencyInjection;

[ContentProperty(nameof(Type))]
public class ResolveExtension : IMarkupExtension
{
    public Type Type { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        if (Type == null)
            throw new InvalidOperationException("必须指定Type属性");
        
        return serviceProvider.GetRequiredService(Type);
    }
}

然后在XAML中引用这个扩展:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:pages="clr-namespace:Example.Views"
             xmlns:extensions="clr-namespace:Example.Extensions"
             x:Class="Example.Views.OuterView"
             x:DataType="viewModels:OuterViewModel">
    <VerticalStackLayout>
        <ContentView Content="{extensions:Resolve Type={x:Type pages:InnerView}}"/>
    </VerticalStackLayout>
</ContentPage>

或者直接把标记扩展返回的视图作为子元素(需要实现IMarkupExtension<View>):

public class ResolveViewExtension : IMarkupExtension<View>
{
    public Type ViewType { get; set; }

    public View ProvideValue(IServiceProvider serviceProvider)
    {
        if (ViewType == null)
            throw new InvalidOperationException("必须指定ViewType属性");
        
        return (View)serviceProvider.GetRequiredService(ViewType);
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) => ProvideValue(serviceProvider);
}

XAML中使用:

<VerticalStackLayout>
    <extensions:ResolveViewExtension ViewType="{x:Type pages:InnerView}"/>
</VerticalStackLayout>

注意事项

  • 确保InnerView和InnerViewModel已正确注册到DI容器,比如:
builder.Services.AddTransient<InnerViewModel>();
builder.Services.AddTransient<InnerView>();
  • 自定义标记扩展的方式更适合频繁使用的场景,代码更简洁易读。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:52:17