经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
在 WPF 中集成 ASP.NET Core 和 WebView2 用于集成 SPA 应用
来源:cnblogs  作者:Aoba_xu  时间:2024/5/13 8:55:33  对本文有异议

背景

我们有些工具在 Web 版中已经有了很好的实践,而在 WPF 中重新开发也是一种费时费力的操作,那么直接集成则是最省事省力的方法了。

思路解释

  1. 为什么要使用 WPF?莫问为什么,老 C# 开发的坚持,另外因为 Windows 上已经装了 Webview2/edge 整体打包比 electron 小很多,release 后的体积主要是 ASP.NET Core 的文件。
  2. 为什么要使用 ASP.NET Core 进行代理呢?很简单,因为很多操作要求使用 HTTP Context,在类似 file:/// 的链接下是不能使用的,如果做成联网的有些资源进行跨域请求也是不能的。举个很简单的例子,vite 打包后的 SPA 如果直接点开那么里面打包的 ES Module 的文件全部不允许请求。
  3. 那你这个项目不联网能用吗?看你的需求了,不联网当然能用,这里集成的 SPA 不一定全部都得是完整的 SPA,整套集成如果客户在有网的环境下可以直接引用网页的 URL 就好了。比如我们要用 monaco-editor 或者其他的文字编辑器又或者是 3D 编辑器,在 C# 上找不到或不好找到类似的库,那么集成 npm 上现成的库就是最佳选择。

修改项目文件

我们首先修改项目文件,让 WPF 项目可以包含 ASP.NET Core 的库,以及引用 WebView2 控件。

  1. <Project Sdk="Microsoft.NET.Sdk">
  2. <PropertyGroup>
  3. <OutputType>WinExe</OutputType>
  4. <TargetFramework>net8.0-windows</TargetFramework>
  5. <Nullable>enable</Nullable>
  6. <ImplicitUsings>enable</ImplicitUsings>
  7. <UseWPF>true</UseWPF>
  8. </PropertyGroup>
  9. <ItemGroup>
  10. <!-- 这里插入 WebView2 的包,用于显示网页 -->
  11. <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2478.35" />
  12. <!-- 这里插入 ASP.NET Core 的框架引用,用于代理资源文件 -->
  13. <FrameworkReference Include="Microsoft.AspNetCore.App" />
  14. </ItemGroup>
  15. <ItemGroup>
  16. <!-- 这里模仿 ASP.NET Core,将 SPA 资源文件存于 wwwroot 文件夹下 -->
  17. <None Update="wwwroot\**">
  18. <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  19. </None>
  20. </ItemGroup>
  21. </Project>

修改 App.xamlApp.xaml.cs 以使用 ASP.NET Core 的 WebApplication.CreateBuilder()

这里为了全局使用依赖注入,我们将 WebApplication.CreateBuilder() 放在 App.xaml.cs 中全局使用。为了使用依赖注入应注释掉默认启动窗口,并接管 Startup 事件。

  1. <Application x:Class="WpfAircraftViewer.App"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:local="clr-namespace:WpfAircraftViewer"
  5. Startup="ApplicationStartup">
  6. <!-- 这里将 StartupUri 属性删除,然后注册 Startup 事件 -->
  7. <Application.Resources>
  8. </Application.Resources>
  9. </Application>

然后通过修改 Startup 事件的代码来实现相应的加载动作。

  1. using Microsoft.AspNetCore.Builder;
  2. using Microsoft.AspNetCore.StaticFiles;
  3. using Microsoft.Extensions.DependencyInjection;
  4. using System.Windows;
  5. namespace WpfAircraftViewer
  6. {
  7. /// <summary>
  8. /// Interaction logic for App.xaml
  9. /// </summary>
  10. public partial class App : Application, IAsyncDisposable
  11. {
  12. public WebApplication? WebApplication { get; private set; }
  13. public async ValueTask DisposeAsync()
  14. {
  15. if (WebApplication is not null)
  16. {
  17. await WebApplication.DisposeAsync();
  18. }
  19. GC.SuppressFinalize(this);
  20. }
  21. private async void ApplicationStartup(object sender, StartupEventArgs e)
  22. {
  23. // 这里是创建 ASP.NET 版通用主机的代码
  24. var builder = WebApplication.CreateBuilder(Environment.GetCommandLineArgs());
  25. // 注册主窗口和其他服务
  26. builder.Services.AddSingleton<MainWindow>();
  27. builder.Services.AddSingleton(this);
  28. var app = builder.Build();
  29. // 这里是文件类型映射,如果你的静态文件在浏览器中加载报 404,那么需要在这里注册,这里我加载一个 3D 场景文件的类型
  30. var contentTypeProvider = new FileExtensionContentTypeProvider();
  31. contentTypeProvider.Mappings[".glb"] = "model/gltf-binary";
  32. app.UseStaticFiles(new StaticFileOptions
  33. {
  34. ContentTypeProvider = contentTypeProvider,
  35. });
  36. // 你如果使用了 Vue Router 或者其他前端路由了,需要在这里添加这句话让路由返回前端,而不是 ASP.NET Core 处理
  37. app.MapFallbackToFile("/index.html");
  38. WebApplication = app;
  39. // 处理退出事件,退出 App 时关闭 ASP.NET Core
  40. Exit += async (s, e) => await WebApplication.StopAsync();
  41. // 显示主窗口
  42. MainWindow = app.Services.GetRequiredService<MainWindow>();
  43. MainWindow.Show();
  44. await app.RunAsync().ConfigureAwait(false);
  45. }
  46. }
  47. }

此时,我们已经可以正常开启一个默认界面的 MainWindow 了。

使用 WebView2 控件

这时我们就可以先将 SPA 文件从 npm 项目的 dist 复制到 wwwroot 了,在编辑 MainWindow 加入 WebView2 控件后就可以查看了。

  1. <Window x:Class="WpfAircraftViewer.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:local="clr-namespace:WpfAircraftViewer"
  7. xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
  8. mc:Ignorable="d" MinHeight="450" MinWidth="800" SnapsToDevicePixels="True">
  9. <!-- 在上面加入 xmlns:wv2 属性用于引用 WebView2 控件 -->
  10. <Grid>
  11. <!-- 这里插入 WebView2 控件,我们默认可以让 Source 是 http://localhost:5000,这是 ASP.NET Core 的默认监听地址 -->
  12. <wv2:WebView2 Name="webView"
  13. Source="{Binding SourceUrl, FallbackValue='http://localhost:5000'}" AllowDrop="True" SnapsToDevicePixels="True"/>
  14. </Grid>
  15. </Window>

我们可以继续编辑窗口的信息,让他可以关联 ASP.NET Core 的监听地址。

  1. using Microsoft.AspNetCore.Hosting.Server;
  2. using Microsoft.AspNetCore.Hosting.Server.Features;
  3. using System.Windows;
  4. namespace WpfAircraftViewer
  5. {
  6. /// <summary>
  7. /// Interaction logic for MainWindow.xaml
  8. /// </summary>
  9. public partial class MainWindow : Window
  10. {
  11. public string SourceUrl { get; set; }
  12. public MainWindow(IServer server)
  13. {
  14. InitializeComponent();
  15. // 这里通过注入的 IServer 对象来获取监听的 Url
  16. var addresses = server.Features.Get<IServerAddressesFeature>()?.Addresses;
  17. SourceUrl = addresses is not null ? (addresses.FirstOrDefault() ?? "http://localhost:5000") : "http://localhost:5000";
  18. // 无 VM,用自身当 VM
  19. DataContext = this;
  20. }
  21. }
  22. }

这时我们就可以看到窗口打开了我们的 SPA 页面了。

示例代码

原文链接:https://www.cnblogs.com/aobaxu/p/18186785

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号