.NET MAUI集成Xamarin.Forms代码时遇CS0311错误求助
将Xamarin.Forms聊天应用代码适配到.NET 7的.NET MAUI项目时,所有页面触发CS0311编译错误,错误信息如下:
error CS0311: The type 'MauiApp1.Views.CallPage' cannot be used as type parameter 'TView' in the generic type or method 'IContainerRegistryExtensions.RegisterForNavigation<TView, TViewModel>(IContainerRegistry, string)'. There is no implicit reference conversion from 'MauiApp1.Views.CallPage' to 'Xamarin.Forms.Page'.
相关代码片段:
App类代码
using MauiApp1.Services.Core; using MauiApp1.Services.Interfaces; using MauiApp1.ViewModels; using MauiApp1.Views; using Prism; using Prism.Ioc; using Prism.Unity; namespace MauiApp1 { public partial class App : PrismApplication { public App(IPlatformInitializer initializer = null) : base(initializer) { } protected override async void OnInitialized() { InitializeComponent(); await NavigationService.NavigateAsync("NavigationPage/MainPage"); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton<IAppInfo, AppInfoImplementation>(); containerRegistry.RegisterForNavigation<NavigationPage>(); containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>(); containerRegistry.RegisterForNavigation<ChatRoomPage, ChatRoomPageViewModel>(); containerRegistry.RegisterForNavigation<FriendsPage, FriendsPageViewModel>(); containerRegistry.Register<IChatService, ChatService>(); containerRegistry.Register<IAuthenticationService, AuthenticationService>(); containerRegistry.Register<ISessionService, SessionService>(); containerRegistry.Register<IUsersService, UsersService>(); containerRegistry.RegisterForNavigation<PrivateChatPage, PrivateChatPageViewModel>(); containerRegistry.RegisterForNavigation<IncomeCallPage, IncomeCallPageViewModel>(); containerRegistry.RegisterForNavigation<CallPage, CallPageViewModel>(); } } }
CallPage的XAML代码
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiApp1.Views.CallPage" xmlns:prism="http://prismlibrary.com" xmlns:views="clr-namespace:MauiApp1.Views" Title="CallPage"> <Grid RowDefinitions="50,*,60,30"> <views:GenericWebView x:Name="CallWebView" Grid.RowSpan="4"/> <!--prism:ViewModelLocator.AutowireViewModel="True"--> <!--<RelativeLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#CCC" Grid.RowSpan="4" IsVisible="false"/> <Frame Margin="0" Padding="0" IsVisible="false"> <Grid ColumnDefinitions="*, 50,50"> <Label VerticalOptions="Center" Text="Someone is calling..." Padding="0" Margin="10,0,0,0"/> <Image Grid.Column="1" HeightRequest="25" VerticalOptions="Center" Source="outline_call.png"/> <Image Grid.Column="2" HeightRequest="25" VerticalOptions="Center" Source="end_call.png"/> </Grid> </Frame> <StackLayout Grid.Row="2" Margin="0,0,10,0" Orientation="Horizontal" HorizontalOptions="Center" BackgroundColor="Transparent"> <Frame HasShadow="True" CornerRadius="50" HeightRequest="60" WidthRequest="60" Padding="1" BackgroundColor="{Binding IsAudioActive, Converter={StaticResource BooleanToColorConverter}, ConverterParameter=#00FFFFFF|#80CCCCCC}" BorderColor="LightGray" HorizontalOptions="Start" VerticalOptions="Center"> <ImageButton Source="mic_off.png" Margin="10" CornerRadius="50" HeightRequest="50" WidthRequest="50" HorizontalOptions="Center" BackgroundColor="Transparent" Clicked="ToggleMicClick"/> </Frame> <Frame HasShadow="True" CornerRadius="50" HeightRequest="60" WidthRequest="60" Padding="1" BackgroundColor="#CC0000" BorderColor="LightGray" HorizontalOptions="Center" VerticalOptions="Center"> <ImageButton Source="end_call.png" Margin="10" CornerRadius="50" HeightRequest="50" WidthRequest="50" HorizontalOptions="Center" BackgroundColor="Transparent" Clicked="EndCallClick" Command="{Binding EndCallClick}"/> </Frame> <Frame HasShadow="True" CornerRadius="50" HeightRequest="60" WidthRequest="60" Padding="1" BackgroundColor="{Binding IsVideoActive, Converter={StaticResource BooleanToColorConverter}, ConverterParameter=#007E33|#80CCCCCC}" BorderColor="LightGray" HorizontalOptions="EndAndExpand" VerticalOptions="Center"> <ImageButton Source="video_call.png" Margin="10" CornerRadius="50" HeightRequest="50" WidthRequest="50" HorizontalOptions="Center" BackgroundColor="Transparent" Clicked="ToggelCameraClick"/> </Frame> </StackLayout> </Grid> </ContentPage>
CallPage后台代码
using MauiApp1.Services.Interfaces; using MauiApp1.ViewModels; using System; using System.Threading.Tasks; using Xamarin.Forms; namespace MauiApp1.Views; public partial class CallPage { private CallPageViewModel callPageViewModel; public CallPage() { InitializeComponent(); var urlSource = new Microsoft.Maui.Controls.UrlWebViewSource(); string baseUrl = Microsoft.Maui.Controls.DependencyService.Get<IWebViewService>().GetContent(); urlSource.Url = baseUrl; CallWebView.Source = urlSource; callPageViewModel = BindingContext as CallPageViewModel; } protected override async void OnAppearing() { base.OnAppearing(); await Task.Delay(1000); var status = await Permissions.CheckStatusAsync<Permissions.Camera>(); if (status != PermissionStatus.Granted) { var response = await Permissions.RequestAsync<Permissions.Camera>(); } var statusMic = await Permissions.CheckStatusAsync<Permissions.Microphone>(); if (statusMic != PermissionStatus.Granted) { var response = await Permissions.RequestAsync<Permissions.Microphone>(); } await Connect(); } private async Task Connect() { try { await CallWebView.EvaluateJavaScriptAsync($"init('{callPageViewModel.CurrentUser.ID}');"); await Task.Delay(2000); await CallWebView.EvaluateJavaScriptAsync($"startCall('{callPageViewModel.Friend.ID}');"); } catch (Exception exp) {} } private async void ToggleMicClick(object sender, EventArgs e) { callPageViewModel.IsAudioActive = !callPageViewModel.IsAudioActive; await CallWebView.EvaluateJavaScriptAsync($"toggleAudio('{callPageViewModel.IsAudioActive.ToString().ToLower()}');"); } private void EndCallClick(object sender, EventArgs e) { EndCall(); } private async void ToggelCameraClick(object sender, EventArgs e) { callPageViewModel.IsVideoActive = !callPageViewModel.IsVideoActive; await CallWebView.EvaluateJavaScriptAsync($"toggleVideo('{callPageViewModel.IsVideoActive.ToString().ToLower()}');"); } private void EndCall() { CallWebView.Source = "about:blank"; } }
错误核心原因是:MAUI页面继承自Microsoft.Maui.Controls.ContentPage,但代码中混用了Xamarin.Forms的命名空间与Prism包,导致Prism的导航注册无法识别MAUI页面类型。
1. 替换Prism依赖包
移除项目中针对Xamarin.Forms的Prism包(如Prism.Unity、Prism.Forms),安装适配MAUI的Prism.Maui NuGet包。
2. 修正页面后台代码
将页面代码中的Xamarin.Forms命名空间替换为Microsoft.Maui.Controls,并确保页面类显式继承ContentPage:
using MauiApp1.Services.Interfaces; using MauiApp1.ViewModels; using System; using System.Threading.Tasks; using Microsoft.Maui.Controls; using Microsoft.Maui.ApplicationModel; namespace MauiApp1.Views; public partial class CallPage : ContentPage { private CallPageViewModel callPageViewModel; public CallPage() { InitializeComponent(); var urlSource = new UrlWebViewSource(); string baseUrl = DependencyService.Get<IWebViewService>().GetContent(); urlSource.Url = baseUrl; CallWebView.Source = urlSource; callPageViewModel = BindingContext as CallPageViewModel; } protected override async void OnAppearing() { base.OnAppearing(); await Task.Delay(1000); var status = await Permissions.CheckStatusAsync<Permissions.Camera>(); if (status != PermissionStatus.Granted) { var response = await Permissions.RequestAsync<Permissions.Camera>(); } var statusMic = await Permissions.CheckStatusAsync<Permissions.Microphone>(); if (statusMic != PermissionStatus.Granted) { var response = await Permissions.RequestAsync<Permissions.Microphone>(); } await Connect(); } private async Task Connect() { try { await CallWebView.EvaluateJavaScriptAsync($"init('{callPageViewModel.CurrentUser.ID}');"); await Task.Delay(2000); await CallWebView.EvaluateJavaScriptAsync($"startCall('{callPageViewModel.Friend.ID}');"); } catch (Exception exp) {} } private async void ToggleMicClick(object sender, EventArgs e) { callPageViewModel.IsAudioActive = !callPageViewModel.IsAudioActive; await CallWebView.EvaluateJavaScriptAsync($"toggleAudio('{callPageViewModel.IsAudioActive.ToString().ToLower()}');"); } private void EndCallClick(object sender, EventArgs e) { EndCall(); } private async void ToggelCameraClick(object sender, EventArgs e) { callPageViewModel.IsVideoActive = !callPageViewModel.IsVideoActive; await CallWebView.EvaluateJavaScriptAsync($"toggleVideo('{callPageViewModel.IsVideoActive.ToString().ToLower()}');"); } private void EndCall() { CallWebView.Source = "about:blank"; } }
3. 修正App类的Prism引用
替换App类中的Prism命名空间,使用MAUI版本的Prism:
using MauiApp1.Services.Core; using MauiApp1.Services.Interfaces; using MauiApp1.ViewModels; using MauiApp1.Views; using Prism; using Prism.Ioc; using Prism.Maui; namespace MauiApp1 { public partial class App : PrismApplication { public App(IPlatformInitializer initializer = null) : base(initializer) { } protected override async void OnInitialized() { InitializeComponent(); await NavigationService.NavigateAsync("NavigationPage/MainPage"); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton<IAppInfo, AppInfoImplementation>(); containerRegistry.RegisterForNavigation<NavigationPage>(); containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>(); containerRegistry.RegisterForNavigation<ChatRoomPage, ChatRoomPageViewModel>(); containerRegistry.RegisterForNavigation<FriendsPage, FriendsPageViewModel>(); containerRegistry.Register<IChatService, ChatService>(); containerRegistry.Register<IAuthenticationService, AuthenticationService>(); containerRegistry.Register<ISessionService, SessionService>(); containerRegistry.Register<IUsersService, UsersService>(); containerRegistry.RegisterForNavigation<PrivateChatPage, PrivateChatPageViewModel>(); containerRegistry.RegisterForNavigation<IncomeCallPage, IncomeCallPageViewModel>(); containerRegistry.RegisterForNavigation<CallPage, CallPageViewModel>(); } } }
4. 全局检查命名空间
遍历所有ViewModel、服务类,移除Xamarin.Forms相关引用,替换为MAUI对应的Microsoft.Maui.*命名空间。
内容的提问来源于stack exchange,提问作者Official Essama

