如何将 Windows 窗体桌面应用程序移植到 .NET CoreHow to port a Windows Forms desktop app to .NET Core

本文介绍如何将基于 Windows 窗体的桌面应用从 .NET Framework 移植到 .NET Core 3.0 或更高版本。This article describes how to port your Windows Forms-based desktop app from .NET Framework to .NET Core 3.0 or later. .NET Core 3.0 SDK 支持 Windows 窗体应用程序。The .NET Core 3.0 SDK includes support for Windows Forms applications. Windows 窗体仍是仅适用于 Windows 的框架,并且只能在 Windows 上运行。Windows Forms is still a Windows-only framework and only runs on Windows. 示例使用 .NET Core SDK CLI 创建和管理项目。This example uses the .NET Core SDK CLI to create and manage your project.

本文中的各种名称用于标识迁移所用的文件类型。In this article, various names are used to identify types of files used for migration. 迁移项目时,你的文件将以不同的名称命名,因此,请自行在心里将它们与下面列出的文件进行匹配:When migrating your project, your files will be named differently, so mentally match them to the ones listed below:

文件File 描述Description
MyApps.slnMyApps.sln 解决方案文件的名称。The name of the solution file.
MyForms.csprojMyForms.csproj 要移植的 .NET Framework Windows 窗体项目的名称。The name of the .NET Framework Windows Forms project to port.
MyFormsCore.csprojMyFormsCore.csproj 创建的新 .NET Core 项目的名称。The name of the new .NET Core project you create.
MyAppCore.exeMyAppCore.exe .NET Core Windows 窗体应用程序的可执行文件。The .NET Core Windows Forms app executable.

先决条件Prerequisites

备注

Visual Studio 2019 及更高版本支持 .NET Core Windows 窗体项目。.NET Core Windows Forms projects are supported in Visual Studio 2019 and later versions. 从 Visual Studio 2019 版本 16.5 开始,还支持 .NET Core Windows 窗体设计器。The .NET Core Windows Forms designer is supported starting in Visual Studio 2019 version 16.5.

若要启用该设计器,请转到“工具” > “选项” > “环境” > “预览功能”,然后选择“将预览版 Windows 窗体设计器用于 .NET Core 应用”选项。To enable the designer, go to Tools > Options > Environment > Preview Features and select the Use the preview Windows Forms designer for .NET Core apps option.

考虑Consider

移植 .NET Framework Windows 窗体应用程序时,必须考虑以下几个事项。When porting a .NET Framework Windows Forms application, there are a few things you must consider.

  1. 检查应用程序是否适合迁移。Check that your application is a good candidate for migration.

    使用 .NET 可移植性分析器确定项目将会迁移到 .NET Core 3.0。Use the .NET Portability Analyzer to determine if your project will migrate to .NET Core 3.0. 如果项目存在 .NET Core 3.0 相关问题,分析器可帮助你识别这些问题。If your project has issues with .NET Core 3.0, the analyzer helps you identify those problems.

  2. 使用的是不同版本的 Windows 窗体。You're using a different version of Windows Forms.

    发布 .NET Core 3.0 预览版 1 时,Windows 窗体在 GitHub 上公布了开放源代码。When .NET Core 3.0 Preview 1 was released, Windows Forms went open source on GitHub. .NET Core Windows 窗体的代码是 .NET Framework Windows 窗体基本代码的一个分支。The code for .NET Core Windows Forms is a fork of the .NET Framework Windows Forms codebase. 可能存在一些差异导致应用程序无法移植。It's possible some differences exist and your app won't port.

  3. 使用 Windows 兼容包可有助于进行迁移。The Windows Compatibility Pack may help you migrate.

    某些 API 可在 .NET Framework 中使用,但不可用于 .NET Core 3.0。Some APIs that are available in .NET Framework aren't available in .NET Core 3.0. Windows 兼容包增加了很多这些 API,有助于使 Windows 窗体应用与 .NET Core 兼容。The Windows Compatibility Pack adds many of these APIs and may help your Windows Forms app become compatible with .NET Core.

  4. 更新项目使用的 NuGet 包。Update the NuGet packages used by your project.

    在执行任何迁移之前使用最新版 NuGet 包始终是一个不错的做法。It's always a good practice to use the latest versions of NuGet packages before any migration. 如果应用程序引用任何 NuGet 包,请将它们更新到最新版本。If your application is referencing any NuGet packages, update them to the latest version. 确保成功生成应用程序。Ensure your application builds successfully. 升级后,如果存在任何包错误,请将包降级到不会破坏代码的最新版本。After upgrading, if there are any package errors, downgrade the package to the latest version that doesn't break your code.

创建新的 SDK 项目Create a new SDK project

创建的新 .NET Core 3.0 项目必须位于不同于 .NET Framework 项目的目录中。The new .NET Core 3.0 project you create must be in a different directory from your .NET Framework project. 如果它们位于同一目录中,可能会与 obj 目录中生成的文件发生冲突。If they're both in the same directory, you may run into conflicts with the files that are generated in the obj directory. 本例中,我们将在 SolutionFolder 目录中创建一个名为 MyFormsAppCore 的目录 :In this example, we'll create a directory named MyFormsAppCore in the SolutionFolder directory:

SolutionFolder
├───MyApps.sln
├───MyFormsApp
│   └───MyForms.csproj
└───MyFormsAppCore      <--- New folder for core project

接下来,需要在 MyFormsAppCore 目录中创建 MyFormsCore.csproj 项目 。Next, you need to create the MyFormsCore.csproj project in the MyFormsAppCore directory. 可以使用所选的文本编辑器手动创建此文件。You can create this file manually by using the text editor of choice. 粘贴以下 XML:Paste in the following XML:

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

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

</Project>

如果不想手动创建项目文件,可以使用 Visual Studio 或 .NET Core SDK 生成项目。If you don't want to create the project file manually, you can use Visual Studio or the .NET Core SDK to generate the project. 但是,必须删除项目模板生成的所有其他文件(项目文件除外)。However, you must delete all other files generated by the project template except for the project file. 若要使用 SDK,请从 SolutionFolder 目录运行以下命令:To use the SDK, run the following command from the SolutionFolder directory:

dotnet new winforms -o MyFormsAppCore -n MyFormsCore

创建 MyFormsCore.csproj 后,目录结构应如下所示:After you create the MyFormsCore.csproj, your directory structure should look like the following:

SolutionFolder
├───MyApps.sln
├───MyFormsApp
│   └───MyForms.csproj
└───MyFormsAppCore
    └───MyFormsCore.csproj

使用 Visual Studio 或 SolutionFolder 目录中的 .NET Core CLI 将 MyFormsCore.csproj 项目添加到 MyApps.sln :Add the MyFormsCore.csproj project to MyApps.sln with either Visual Studio or the .NET Core CLI from the SolutionFolder directory:

dotnet sln add .\MyFormsAppCore\MyFormsCore.csproj

修复程序集信息生成Fix assembly info generation

使用 .NET Framework 创建的 Windows 窗体项目包含一个 AssemblyInfo.cs 文件,该文件包含诸如要生成的程序集的版本等程序集特性。Windows Forms projects that were created with .NET Framework include an AssemblyInfo.cs file, which contains assembly attributes such as the version of the assembly to be generated. SDK 样式的项目会根据 SDK 项目文件自动生成此信息。SDK-style projects automatically generate this information for you based on the SDK project file. 同时具有两种类型的“程序集信息”时,会产生冲突。Having both types of "assembly info" creates a conflict. 通过禁用自动生成可以解决此问题,这会强制项目使用现有的 AssemblyInfo.cs 文件。Resolve this problem by disabling automatic generation, which forces the project to use your existing AssemblyInfo.cs file.

若要添加到主 <PropertyGroup> 节点,有三个设置项。There are three settings to add to the main <PropertyGroup> node.

  • GenerateAssemblyInfoGenerateAssemblyInfo
    将此属性设置为 false 时,它不会生成程序集特性。When you set this property to false, it won't generate the assembly attributes. 这可以避免与 .NET Framework 项目中的现有 AssemblyInfo.cs 文件冲突。This avoids the conflict with the existing AssemblyInfo.cs file from the .NET Framework project.

  • AssemblyNameAssemblyName
    此属性的值是编译时创建的输出二进制。The value of this property is the output binary created when you compile. 该名称不需要添加扩展名。The name doesn't need an extension added to it. 例如,使用 MyCoreApp 生成 MyCoreApp.exeFor example, using MyCoreApp produces MyCoreApp.exe.

  • RootNamespaceRootNamespace
    项目使用的默认命名空间。The default namespace used by your project. 它应该与 .NET Framework 项目的默认命名空间匹配。This should match the default namespace of the .NET Framework project.

将这三个元素添加到 MyFormsCore.csproj 文件中的 <PropertyGroup> 节点:Add these three elements to the <PropertyGroup> node in the MyFormsCore.csproj file:

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

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>

    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
    <AssemblyName>MyCoreApp</AssemblyName>
    <RootNamespace>WindowsFormsApp1</RootNamespace>
  </PropertyGroup>

</Project>

添加源代码Add source code

现在,MyFormsCore.csproj 项目不编译任何代码。Right now, the MyFormsCore.csproj project doesn't compile any code. 默认情况下,.NET Core 项目会自动包含当前目录和所有子目录中的所有源代码。By default, .NET Core projects automatically include all source code in the current directory and any child directories. 必须使用相对路径配置项目以包含 .NET Framework 项目中的代码。You must configure the project to include code from the .NET Framework project using a relative path. 如果 .NET Framework 项目使用了 .resx 文件作为窗体的图标和资源,则还需要包含这些文件。If your .NET Framework project used .resx files for icons and resources for your forms, you'll need to include those too.

将以下 <ItemGroup> 节点添加到项目中。Add the following <ItemGroup> node to your project. 每个语句都包含一个文件 glob 模式,其中包含子目录。Each statement includes a file glob pattern that includes child directories.

  <ItemGroup>
    <Compile Include="..\MyFormsApp\**\*.cs" />
    <EmbeddedResource Include="..\MyFormsApp\**\*.resx" />
  </ItemGroup>

或者,可以为 .NET Framework 项目中的每个文件创建一个 <Compile><EmbeddedResource> 条目。Alternatively, you can create a <Compile> or <EmbeddedResource> entry for each file in your .NET Framework project.

添加 NuGet 包Add NuGet packages

将 .NET Framework 项目引用的每个 NuGet 包添加到 .NET Core 项目。Add each NuGet package referenced by the .NET Framework project to the .NET Core project.

很可能你的 .NET Framework Windows 窗体应用程序有一个 packages.config 文件,其中包含项目引用的所有 NuGet 包的列表。Most likely your .NET Framework Windows Forms app has a packages.config file that contains a list of all of the NuGet packages that are referenced by your project. 可以查看此列表以确定要添加到 .NET Core 项目的 NuGet 包。You can look at this list to determine which NuGet packages to add to the .NET Core project. 例如,如果 .NET Framework 项目引用了 MetroFrameworkMetroFramework.DesignMetroFramework.Fonts NuGet 包,则使用 Visual Studio 或 SolutionFolder 目录中的 .NET Core CLI 将每个包添加到项目中:For example, if the .NET Framework project referenced the MetroFramework, MetroFramework.Design, and MetroFramework.Fonts NuGet packages, add each to the project with either Visual Studio or the .NET Core CLI from the SolutionFolder directory:

dotnet add .\MyFormsAppCore\MyFormsCore.csproj package MetroFramework
dotnet add .\MyFormsAppCore\MyFormsCore.csproj package MetroFramework.Design
dotnet add .\MyFormsAppCore\MyFormsCore.csproj package MetroFramework.Fonts

之前的命令会将以下 NuGet 引用添加到 MyFormsCore.csproj 项目:The previous commands would add the following NuGet references to the MyFormsCore.csproj project:

  <ItemGroup>
    <PackageReference Include="MetroFramework" Version="1.2.0.3" />
    <PackageReference Include="MetroFramework.Design" Version="1.2.0.3" />
    <PackageReference Include="MetroFramework.Fonts" Version="1.2.0.3" />
  </ItemGroup>

移植控件库Port control libraries

如果要移植 Windows 窗体控件库项目,操作说明与移植 .NET Framework Windows 窗体应用程序项目相同,只不过在一些设置上存在差异。If you have a Windows Forms Controls library project to port, the directions are the same as porting a .NET Framework Windows Forms app project, except for a few settings. 并且此操作中不编译到可执行文件,而是编译到库。And instead of compiling to an executable, you compile to a library. 除了包含源代码的文件 glob 的路径之外,可执行项目和库项目之间的区别也很小。The difference between the executable project and the library project, besides paths for the file globs that include your source code, is minimal.

借助上一步的示例,详细介绍正在处理的项目和文件。Using the previous step's example, lets expand what projects and files we're working with.

文件File 描述Description
MyApps.slnMyApps.sln 解决方案文件的名称。The name of the solution file.
MyControls.csprojMyControls.csproj 要移植的 .NET Framework Windows 窗体控件项目的名称。The name of the .NET Framework Windows Forms Controls project to port.
MyControlsCore.csprojMyControlsCore.csproj 创建的新 .NET Core 库项目的名称。The name of the new .NET Core library project you create.
MyCoreControls.dllMyCoreControls.dll .NET Core Windows 窗体控件库。The .NET Core Windows Forms Controls library.
SolutionFolder
├───MyApps.sln
├───MyFormsApp
│   └───MyForms.csproj
├───MyFormsAppCore
│   └───MyFormsCore.csproj
│
├───MyFormsControls
│   └───MyControls.csproj
└───MyFormsControlsCore
    └───MyControlsCore.csproj   <--- New project for core controls

请考虑 MyControlsCore.csproj 项目与先前创建的 MyFormsCore.csproj 项目之间的差异。Consider the differences between the MyControlsCore.csproj project and the previously created MyFormsCore.csproj project.

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

   <PropertyGroup>
-    <OutputType>WinExe</OutputType>
     <TargetFramework>netcoreapp3.0</TargetFramework>
     <UseWindowsForms>true</UseWindowsForms>

     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-    <AssemblyName>MyCoreApp</AssemblyName>
-    <RootNamespace>WindowsFormsApp1</RootNamespace>
+    <AssemblyName>MyControlsCore</AssemblyName>
+    <RootNamespace>WindowsFormsControlLibrary1</RootNamespace>
   </PropertyGroup>

   <ItemGroup>
-    <Compile Include="..\MyFormsApp\**\*.cs" />
-    <EmbeddedResource Include="..\MyFormsApp\**\*.resx" />
+    <Compile Include="..\MyFormsControls\**\*.cs" />
+    <EmbeddedResource Include="..\MyFormsControls\**\*.resx" />
   </ItemGroup>

 </Project>

以下是 .NET Core Windows 窗体控件库项目文件的示例:Here is an example of what the .NET Core Windows Forms Controls library project file would look like:

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

  <PropertyGroup>

    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>

    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
    <AssemblyName>MyCoreControls</AssemblyName>
    <RootNamespace>WindowsFormsControlLibrary1</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="..\MyFormsControls\**\*.cs" />
    <EmbeddedResource Include="..\MyFormsControls\**\*.resx" />
  </ItemGroup>

</Project>

如你所见,<OutputType> 节点已被删除,默认使编译器生成库而不是可执行文件。As you can see, the <OutputType> node was removed, which defaults the compiler to produce a library instead of an executable. <AssemblyName><RootNamespace> 已更改。The <AssemblyName> and <RootNamespace> were changed. 具体来说,<RootNamespace> 应匹配正在移植的 Windows 窗体控件库的命名空间。Specifically the <RootNamespace> should match the namespace of the Windows Forms Controls library you are porting. 最后,<Compile><EmbeddedResource> 节点已调整为指向要移植的 Windows 窗体控件库的文件夹。And finally, the <Compile> and <EmbeddedResource> nodes were adjusted to point to the folder of the Windows Forms Controls library you are porting.

接下来,在主要的 .NET Core MyFormsCore.csproj 项目中,添加对新 .NET Core Windows 窗体控件库的引用。Next, in the main .NET Core MyFormsCore.csproj project, add a reference to the new .NET Core Windows Forms Control library. 使用 Visual Studio 或 .NET Core CLI 从 SolutionFolder 目录添加引用:Add a reference with either Visual Studio or the .NET Core CLI from the SolutionFolder directory:

dotnet add .\MyFormsAppCore\MyFormsCore.csproj reference .\MyFormsControlsCore\MyControlsCore.csproj

上一个命令将以下内容添加到 MyFormsCore.csproj 项目中:The previous command adds the following to the MyFormsCore.csproj project:

  <ItemGroup>
    <ProjectReference Include="..\MyFormsControlsCore\MyControlsCore.csproj" />
  </ItemGroup>

编译问题Compilation problems

如果在编译项目时遇到问题,可能是由于正在使用的一些仅适用于 Windows 的 API 在 .NET Framework 中可用,但在 .NET Core 中不可用。If you have problems compiling your projects, you may be using some Windows-only APIs that are available in .NET Framework but not available in .NET Core. 可以尝试将 Windows 兼容包 NuGet 包添加到项目中。You can try adding the Windows Compatibility Pack NuGet package to your project. 此包仅在 Windows 上运行,为 .NET Core 和 .NET Standard 项目添加了大约 20,000 个 Windows API。This package only runs on Windows and adds about 20,000 Windows APIs to .NET Core and .NET Standard projects.

dotnet add .\MyFormsAppCore\MyFormsCore.csproj package Microsoft.Windows.Compatibility

上一个命令将以下内容添加到 MyFormsCore.csproj 项目中:The previous command adds the following to the MyFormsCore.csproj project:

  <ItemGroup>
    <PackageReference Include="Microsoft.Windows.Compatibility" Version="3.1.0" />
  </ItemGroup>

Windows Forms Designer — Windows 窗体设计器Windows Forms Designer

如本文所述,Visual Studio 2019 仅支持 .NET Framework 项目中的窗体设计器。As detailed in this article, Visual Studio 2019 only supports the Forms Designer in .NET Framework projects. 通过创建并行 .NET Core 项目,可以在使用 .NET Framework 项目设计窗体时通过 .NET Core 测试项目。By creating a side-by-side .NET Core project, you can test your project with .NET Core while you use the .NET Framework project to design forms. 解决方案文件包括 .NET Framework 和 .NET Core 项目。Your solution file includes both the .NET Framework and .NET Core projects. 在 .NET Framework 项目中添加和设计窗体和控件,并且根据添加到 .NET Core 项目的文件 glob 模式,任何新的或更改的文件将自动包含在 .NET Core 项目中。Add and design your forms and controls in the .NET Framework project, and based on the file glob patterns we added to the .NET Core projects, any new or changed files will automatically be included in the .NET Core projects.

一旦 Visual Studio 2019 支持 Windows 窗体设计器,就可以将 .NET Core 项目文件的内容复制/粘贴到 .NET Framework 项目文件中。Once Visual Studio 2019 supports the Windows Forms Designer, you can copy/paste the content of your .NET Core project file into the .NET Framework project file. 然后删除使用 <Source><EmbeddedResource> 项添加的文件 glob 模式。Then delete the file glob patterns added with the <Source> and <EmbeddedResource> items. 修复由应用程序使用的任何项目引用的路径。Fix the paths to any project reference used by your app. 这可以有效地将 .NET Framework 项目升级到 .NET Core 项目。This effectively upgrades the .NET Framework project to a .NET Core project.

后续步骤Next steps