在 WPF 中集成 ASP.NET Core 和 WebView2 用于集成 SPA 应用

wpf,asp,net,core,webview2,spa · 浏览次数 : 0

小编点评

**修改项目文件** 1. **添加 WebView2 包** - 在 `project.json` 文件中添加以下代码: ```json "Microsoft.Web.WebView2.Wpf": "1.0.2478.35" ``` 2. **添加 ASP.NET Core 框架引用** - 在 `project.json` 文件中添加以下代码: ```json "Microsoft.AspNetCore.App": "5.0.0" ``` **修改 App.xaml 和 App.xaml.cs** 1. **在 `App.xaml` 中添加 WebView2 控件** - 在 `Window` 元素中添加以下代码: ```xml ``` 2. **在 `App.xaml.cs` 中添加以下代码** - 在 `MainWindow` 类中添加以下代码: ```csharp public string SourceUrl { get; set; } public MainWindow(IServer server) { InitializeComponent(); // 获取服务器地址 var addresses = server.Features.Get()?.Addresses; SourceUrl = addresses is not null ? (addresses.FirstOrDefault() ?? "http://localhost:5000") : "http://localhost:5000"; } ``` **修改 ApplicationStartup 事件** 1. **删除默认启动窗口** - 在 `ApplicationStartup` 方法中删除以下代码: ```csharp var builder = WebApplication.CreateBuilder(Environment.GetCommandLineArgs()); ``` 2. **注册 Startup 事件** - 在 `ApplicationStartup` 方法中注册 `ApplicationStartup` 事件: ```csharp builder.Events.AddApplicationEvent(this, "ApplicationStartup"); ``` **使用 WebView2 控件** 1. 在 `MainWindow.xaml` 中添加 WebView2 控件。 2. 设置 `Source` 属性以指向您 SPA 页面文件的路径。 3. 设置 `AllowDrop` 和 `SnapsToDevicePixels` 属性为 `True`。 **注意** 1. 请确保您的 SPA 页面已构建并部署到 npm 项目中。 2. 您需要在服务器上运行 ASP.NET Core 应用。 3. 您可以根据需要调整 `SourceUrl` 属性以指向其他源。

正文

背景

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

修改项目文件

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

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
	<OutputType>WinExe</OutputType>
	<TargetFramework>net8.0-windows</TargetFramework>
	<Nullable>enable</Nullable>
	<ImplicitUsings>enable</ImplicitUsings>
	<UseWPF>true</UseWPF>
  </PropertyGroup>

  <ItemGroup>
    <!-- 这里插入 WebView2 的包,用于显示网页 -->
	<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2478.35" />
    <!-- 这里插入 ASP.NET Core 的框架引用,用于代理资源文件 -->
	<FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>

  <ItemGroup>
    <!-- 这里模仿 ASP.NET Core,将 SPA 资源文件存于 wwwroot 文件夹下 -->
	<None Update="wwwroot\**">
	  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
	</None>
  </ItemGroup>

</Project>

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

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

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

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

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using System.Windows;

namespace WpfAircraftViewer
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application, IAsyncDisposable
    {
        public WebApplication? WebApplication { get; private set; }

        public async ValueTask DisposeAsync()
        {
            if (WebApplication is not null)
            {
                await WebApplication.DisposeAsync();
            }
            GC.SuppressFinalize(this);
        }
        
        private async void ApplicationStartup(object sender, StartupEventArgs e)
        {
            // 这里是创建 ASP.NET 版通用主机的代码
            var builder = WebApplication.CreateBuilder(Environment.GetCommandLineArgs());
            // 注册主窗口和其他服务
            builder.Services.AddSingleton<MainWindow>();
            builder.Services.AddSingleton(this);
            var app = builder.Build();
            // 这里是文件类型映射,如果你的静态文件在浏览器中加载报 404,那么需要在这里注册,这里我加载一个 3D 场景文件的类型
            var contentTypeProvider = new FileExtensionContentTypeProvider();
            contentTypeProvider.Mappings[".glb"] = "model/gltf-binary";
            app.UseStaticFiles(new StaticFileOptions
            {
                ContentTypeProvider = contentTypeProvider,
            });
            // 你如果使用了 Vue Router 或者其他前端路由了,需要在这里添加这句话让路由返回前端,而不是 ASP.NET Core 处理
            app.MapFallbackToFile("/index.html");
            WebApplication = app;
            // 处理退出事件,退出 App 时关闭 ASP.NET Core
            Exit += async (s, e) => await WebApplication.StopAsync();
            // 显示主窗口
            MainWindow = app.Services.GetRequiredService<MainWindow>();
            MainWindow.Show();
            await app.RunAsync().ConfigureAwait(false);
        }
    }
}

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

使用 WebView2 控件

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

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

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

using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using System.Windows;

namespace WpfAircraftViewer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public string SourceUrl { get; set; }
        public MainWindow(IServer server)
        {
            InitializeComponent();
            // 这里通过注入的 IServer 对象来获取监听的 Url
            var addresses = server.Features.Get<IServerAddressesFeature>()?.Addresses;
            SourceUrl = addresses is not null ? (addresses.FirstOrDefault() ?? "http://localhost:5000") : "http://localhost:5000";
            // 无 VM,用自身当 VM
            DataContext = this;
        }
    }
}

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

与在 WPF 中集成 ASP.NET Core 和 WebView2 用于集成 SPA 应用相似的内容:

在 WPF 中集成 ASP.NET Core 和 WebView2 用于集成 SPA 应用

背景 我们有些工具在 Web 版中已经有了很好的实践,而在 WPF 中重新开发也是一种费时费力的操作,那么直接集成则是最省事省力的方法了。 修改项目文件 我们首先修改项目文件,让 WPF 项目可以包含 ASP.NET Core 的库,以及引用 WebView2 控件。

.NET C# 程序自动更新组件

引言 本来博主想偷懒使用AutoUpdater.NET组件,但由于博主项目有些特殊性和它的功能过于多,于是博主自己实现一个轻量级独立自动更新组件,可稍作修改集成到大家自己项目中,比如:WPF/Winform/Windows服务。大致思路:发现更新后,从网络上下载更新包并进行解压,同时在 WinFor

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(1)

在我们的SqlSugar的开发框架中,整合了Winform端、Vue3+ElementPlus的前端、以及基于UniApp+Vue+ThorUI的移动前端几个前端处理,基本上覆盖了我们日常的应用模式了,本篇随笔进一步介绍前端应用的领域,研究集成WPF的应用端,循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发。

循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(7) -- 图标列表展示和选择处理

我们在WPF应用端的界面中,使用lepoco/wpfui 来做主要的入口框架,这个项目它的菜单内置了不少图标,我们需要在动态菜单的配置中,使用它作为图标的展示处理,本篇随笔介绍如何基于图标枚举集合进行图标的展示和选择处理。并扩展到Font-Awesome-WPF的处理进行展示和选择。

在WPF中使用着色器

概念类比 范畴 CPU GPU 二进制文件 .exe .cso / .ps 二进制指令 机器码 CSO(shader指令) 助记符 汇编 SL 高级语言 C# HLSL 高级语言文件 .cs .hlsl / .fx 高级语言编译器 csc.exe fxc.exe API .NET API Direc

在WPF中使用WriteableBitmap对接工业相机及常用操作

写作背景 写这篇文章主要是因为工业相机(海康、大恒等)提供的.NET开发文档和示例程序都是用WinForm项目来说明举例的,而在WPF项目中对图像的使用和处理与在WinForm项目中有很大不同。在WinForm中用System.Drawing.Bitmap来处理图像,而在WPF中是用System.W

Avalonia中的线性渐变画刷LinearGradientBrush

在WPF中使用Shape实现复杂线条动画后,尝试在Avalonia中也实现同样效果。尽管官方提供了从WPF到Avalonia的快速入门文档,但由于第一次使用Avalonia,体验过程中并不是很顺利,主要是卡在线性渐变画刷LinearGradientBrush的使用上。Avalonia中的线性渐变画刷

使用.NET查询日出日落时间

在WPF中,通过资源文件实现主题切换是个常见的功能,有不少文章介绍了如何实现手动切换主题。那如何实现自动切换主题呢?通常有两种机制:一是跟随系统明暗主题切换,二是像手机操作系统那样根据日出日落时间自动切换。本文将以终为始,采用倒推法一步步介绍如何使用.NET免费获取日出日落时间。 获取日出日落时间

[WPF]浅析依赖属性(DependencyProperty)

在WPF中,引入了依赖属性这个概念,提到依赖属性时通常都会说依赖属性能节省实例对内存的开销。此外依赖属性还有两大优势。 支持多属性值,依赖属性系统可以储存多个值,配合Expression、Style、Animation等可以给我们带来很强的开发体验。 加入了属性变化通知,限制、验证等功能。方便我们使

WPF/C#:实现导航功能

前言 在WPF中使用导航功能可以使用Frame控件,这是比较基础的一种方法。前几天分享了wpfui中NavigationView的基本用法,但是如果真正在项目中使用起来,基础的用法是无法满足的。今天通过wpfui中的mvvm例子来说明在wpfui中如何通过依赖注入与MVVM模式使用导航功能。实践起来