如何将 WPF 桌面应用升级到 .NET 8

本文介绍如何将 Windows Presentation Foundation (WPF) 桌面应用升级到 .NET 8。 尽管 WPF 在 .NET 这一跨平台技术上运行,但它仍然是一个仅限 Windows 的框架。 可以使用 .NET 升级助手升级以下与 WPF 相关的项目类型:

  • WPF 项目
  • 控件库
  • .NET 库

如果要从 .NET Framework 升级到 .NET,不妨查看与 WPF .NET 的差异文章和从 .NET Framework 移植到 .NET指南。

先决条件

演示应用

本文是在升级 Web 收藏夹示例项目的背景下编写的,可以从 .NET 示例 GitHub 存储库下载该项目。

启动升级

如果要升级多个项目,请从没有依赖项的项目开始。 在 Web 收藏夹示例中,WebSiteRatings 项目依赖于 StarVoteControl 库,因此应首先升级 StarVoteControl

提示

请务必对代码进行备份,例如在源代码管理中或副本中。

使用以下步骤在 Visual Studio 中升级项目:

  1. 右键单击“解决方案资源管理器”窗口中的 StarVoteControl 项目,然后选择“升级”:

    The .NET Upgrade Assistant's Upgrade menu item in Visual Studio.

    将打开一个新选项卡,提示你选择执行升级的方式。

  2. 选择“就地项目升级”。

  3. 接下来,选择目标框架。 根据要升级的项目类型,会显示不同的选项。 如果该库不依赖于 WPF 等桌面技术并且可以由 .NET Framework 项目和 .NET 项目使用,那么 .NET Standard 2.0 是一个不错的选择。 但是,最新的 .NET 版本相对于 .NET Standard 提供了许多语言和编译器改进。

    选择“.NET 8.0”,然后选择“下一步”。

  4. 此时将显示一个树状结构,其中包含与项目相关的所有工件,例如代码文件和库。 可以升级单个生成生成工件或整个项目(这是默认设置)。 选择“升级选择”以开始升级。

    升级完成后,将显示结果:

    The .NET Upgrade Assistant's upgrade results tab, showing 7 out of the 21 items were skipped.

    具有实心绿色圆圈的生成工件已升级,而具有空绿色圆圈的生成工件则被跳过。 跳过的生成工件意味着升级助手没有找到任何可以升级的内容。

现在应用程序的支持库已升级,请升级主应用程序。

升级应用

升级所有支持库后,即可升级主应用项目。 执行以下步骤:

  1. 右键单击“解决方案资源管理器”窗口中的 WebSiteRatings 项目,然后选择“升级”:
  2. 选择“就地项目升级”作为升级模式。
  3. 选择“.NET 8.0”作为目标框架,然后选择“下一步”
  4. 保留选中所有项目,然后选择“升级选择”。

升级完成后,将显示结果。 如果某个项目有警告符号,则意味着有一条注释可供阅读,可以通过展开该项目来阅读。

生成干净的内部版本

项目升级后,清理并编译它。

  1. 右键单击“解决方案资源管理器”窗口中的 WebSiteRatings 项目,然后选择“清理”。
  2. 右键单击“解决方案资源管理器”窗口中的 WebSiteRatings 项目,然后选择“生成”。

如果应用程序遇到任何错误,可以在“错误列表”窗口中找到这些错误,并提供修复这些错误的建议。

升级后的步骤

如果你的项目正在从 .NET Framework 升级到 .NET,请查看从 .NET Framework 升级到 .NET 后的现代化一文中的信息。

升级后,需要:

  • 检查 NuGet 包。

    .NET 升级助手将一些包升级到了新版本。 借助本文中提供的示例应用程序,Microsoft.Data.Sqlite NuGet 包已从 1.0.0 升级到 8.0.x。 不过,1.0.0 依赖于 SQLite NuGet 包,但 8.0.x 移除了该依赖关系。 项目仍会引用 SQLite NuGet 包,尽管已经不再需要它。 可以从项目中移除 SQLiteSQLite.Native NuGet 包。

  • 清理旧的 NuGet 包。

    不再需要 packages.config 文件,可以将其从项目中删除,因为 NuGet 包引用现已在项目文件中声明。 此外,名为 Packages 的本地 NuGet 包缓存文件夹位于项目的文件夹或父文件夹中。 可以删除此本地缓存文件夹。 新的 NuGet 包引用使用包的全局缓存文件夹,该文件夹位于用户的配置文件目录中,名为 .nuget\packages

  • 移除 System.Configuration 库。

    大多数 .NET Framework 应用都会引用 System.Configuration 库。 升级后,有可能还是直接引用这个库。

    System.Configuration 库使用 app.config 文件为应用程序提供运行时配置选项。 对于 .NET,此库已替换为 System.Configuration.ConfigurationManager NuGet 包。 移除对库的引用,并将 NuGet 包添加到项目。

  • 检查对应用进行现代化改造的位置。

    自 .NET 发布以来,API 和库发生了很大变化。 在大多数情况下,.NET Framework 无法访问这些改进。 通过升级到 .NET,现在可以访问更现代的库。

    后续部分将介绍对本文使用的示例应用程序进行现代化改造的方面。

现代化:Web 浏览器控件

WPF 示例应用所引用的 WebBrowser 控件基于已过时的 Internet Explorer。 WPF for .NET 可以使用基于 Microsoft Edge 的 WebView2 控件。 完成以下步骤以升级到新的 WebView2 Web 浏览器控件:

  1. 添加 Microsoft.Web.WebView2 NuGet 包。

  2. 在 MainWindow.xaml 文件中:

    1. 将控件导入根元素中的 wpfControls 命名空间:

      <mah:MetroWindow x:Class="WebSiteRatings.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:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
              xmlns:local="clr-namespace:WebSiteRatings"
              xmlns:vm="clr-namespace:WebSiteRatings.ViewModels"
              xmlns:VoteControl="clr-namespace:StarVoteControl;assembly=StarVoteControl"
              xmlns:wpfControls="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
              Loaded="MetroWindow_Loaded"
              mc:Ignorable="d"
              Title="My Sites" Height="650" Width="1000">
      
    2. 在声明 <Border> 元素的位置下,删除 WebBrowser 控件并将其替换为 wpfControls:WebView2 控件:

      <Border Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" BorderThickness="1" BorderBrush="Black" Margin="5">
          <wpfControls:WebView2 x:Name="browser" ScrollViewer.CanContentScroll="True" />
      </Border>
      
  3. 编辑 MainWindow.xaml.cs 代码隐藏文件。 更新 ListBox_SelectionChanged 方法以将 browser.Source 属性设置为有效的 Uri。 此代码以前作为字符串传入网站 URL,但 WebView2 控件需要使用 Uri

    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var siteCollection = (ViewModels.SiteCollection)DataContext;
    
        if (siteCollection.SelectedSite != null)
            browser.Source = new Uri(siteCollection.SelectedSite.Url);
        else
            browser.NavigateToString("<body></body>");
    }
    

根据你的应用的用户运行的 Windows 版本,他们可能需要安装 WebView2 运行时。 有关详细信息,请参阅 WPF 应用中的 WebView2 入门

现代化:appsettings.json

.NET Framework 使用 App.config 文件为你的应用加载设置,例如连接字符串和日志记录提供程序。 .NET 现在使用 appsettings.json 文件进行应用设置。 通过 System.Configuration.ConfigurationManager NuGet 包在 .NET 中支持 App.config 文件,并且通过 Microsoft.Extensions.Configuration NuGet 包提供对 appsettings.json 的支持。

随着其他库升级到 .NET,它们将通过支持 appsettings.json 而不是 App.config 实现新式化。例如,已针对 .NET 6+ 升级的 .NET Framework 中的日志记录提供程序将不再使用 App.config 进行设置。 最好遵循它们的指示,并尽可能不要使用 App.config

将 appsettings.json 与 WPF 示例应用配合使用

例如,升级 WPF 示例应用程序后,使用 appsettings.json 作为本地数据库的连接字符串。

  1. 删除 System.Configuration.ConfigurationManager NuGet 包。

  2. 添加 Microsoft.Extensions.Configuration.Json NuGet 包。

  3. 将文件添加到名为 appsettings.json 的项目。

  4. 将 appsettings.json 文件设置为复制到输出目录。

    解决方案资源管理器中选择文件后,使用“属性”窗口通过 Visual Studio 设置“复制到输出”设置。 或者,可以直接编辑项目并添加以下 ItemGroup

      <ItemGroup>
        <Content Include="appsettings.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </Content>
      </ItemGroup>
    
  5. App.config 文件中的设置迁移到新的 appsettings.json 文件。

    在 WPF 示例应用程序中,app.config 仅包含一个连接字符串。 编辑 appsettings.json 文件以定义连接字符串:

    {
      "ConnectionStrings": {
        "database": "DataSource=sqlite.db;"
      }
    }
    
  6. 编辑 App.xaml.cs 文件,实例化加载 appsettings.json 文件的配置对象,添加的行会突出显示:

    using System.Windows;
    using Microsoft.Extensions.Configuration;
    
    namespace WebSiteRatings
    {
        /// <summary>
        /// Interaction logic for App.xaml
        /// </summary>
        public partial class App : Application
        {
            public static IConfiguration Config { get; private set; }
    
            public App()
            {
                Config = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json")
                    .Build();
            }
        }
    }
    
  7. 在 .\Models\Database.cs 文件中,更改 OpenConnection 方法以使用新的 App.Config 属性。 这需要导入 Microsoft.Extensions.Configuration 命名空间:

    using Microsoft.Data.Sqlite;
    using System.Collections.Generic;
    using Microsoft.Extensions.Configuration;
    
    namespace WebSiteRatings.Models
    {
        internal class Database
        {
            public static SqliteConnection OpenConnection() =>
                new SqliteConnection(App.Config.GetConnectionString("database"));
    
            public static IEnumerable<Site> ReadSites()
    

    GetConnectionStringMicrosoft.Extensions.Configuration 命名空间提供的扩展方法。