In this post, Olia Gavrysh (Program Manager, Microsoft) guides you through the process of porting a desktop application from .NET Framework to .NET Core using an existing WinForms application as an example.
This article was originally posted on the Microsoft .NET Blog and is republished with permission.
In this post, I will describe how to port a desktop application from .NET Framework to .NET Core. I picked a WinForms application as an example. Steps for WPF application are similar and I’ll describe what needs to be done different for WPF as we go. I will also show how you can keep using the WinForms designer in Visual Studio even though it is under development and is not yet available for .NET Core projects.
For this post, I’ll be using a Memory-style board game application. It contains a WinForms UI (MatchingGame.exe
) and a class library with the game logic (MatchingGame.Logic.dll
), both targeting .NET Framework 4.5. You can download the sample here. I’ll be porting the application project to .NET Core 3.0 and the class library to .NET Standard 2.0. Using .NET Standard instead of .NET Core allows me to reuse the game logic to provide the application for other platforms, such as iOS, Android or web.
You can either watch Scott Hunter and me doing the conversion in the following video, or you can follow the step-by-step instructions below. Of course, I won’t be holding it against you, if you were to do both.
I suggest doing the migration in a separate branch or, if you’re not using version control, creating a copy of your project so you have a clean state to go back to if necessary.
Before porting the application to .NET Core 3.0, I need to do some preparation first.
packages.config
with PackageReference
. If your project uses NuGet packages, you need to add the same NuGet packages to the new .NET Core project. .NET Core projects support only PackageReference
for adding NuGet packages. To move your NuGet references from packages.config
to your project file, in the solution explorer right-click on packages.config
-> Migrate packages.config to PackageReference….You can learn more about this migration in the Migrate from packages.config to PackageReference article.dotnet new winforms -o <path-to-your-solution>\MatchingGame.Core\</path-to-your-solution>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
Contact
form on the NuGet gallery.First, let’s try the fast way to port. Make sure you have a copy of your current .csproj
file, you might need to use it in future. Replace your current .csproj
file with the .csproj
file from the project you created on the step above and add in the top <PropertyGroup>
:
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
Build your app. If you got no errors – congrats, you’ve successfully migrated your project to .NET Core 3. For porting a dependent (UI) project see Porting UI section, for using the Designer, check out Using WinForms Designer for .NET Core projects section.
If you got errors (like I did with my app), it means there are more adjustments you need to make. Instead of the fast way described above, here I’ll do one change at a time and give possible fixes for each issue. Steps bellow would also help to better understand the process of the migration so if the fast way worked for you but you’re curious to learn all “whys”, keep on reading.
.csproj
file. Replace the content of your .csproj
file with the following. For WinForms application:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net472</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>
For WPF application:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net472</TargetFramework>
<UseWPF>true</UseWPF>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>
Note that I set
<GenerateAssemblyInfo>
tofalse
. In the new-style projectsAssemblyInfo.cs
is generated automatically by default. So if you already haveAssemblyInfo.cs
file in your project (spoiler alert: you do), you need to disable auto-generation or remove the file.
Now copy & paste all references from the old version of .csproj
file into the new one. For example:
NuGet package reference
<PackageReference Include="Microsoft.Windows.Compatibility" Version="2.0.1" />
Project reference
<ProjectReference Include="..\MatchingGame.Core\MatchingGame.Core.csproj" />
The project should build successfully since it is just a new way of writing the same thing. If you got any errors, double check your steps.
There is also a third-party tool CsprojToVs2017 that can perform the conversion for you. But after using it, you still might need to delete some reference by hand, such as:
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
<TargetFramework>net472</TargetFramework>
with
<TargetFramework>netstandard2.0</TargetFramework>
Build your application. You might get some errors if you are using APIs that are not included in .NET Standard. If you did not get any errors with your application, you can skip the next two steps.
MatchingGame.exe
, so now I will perform similar steps to migrate that to .NET Core.dotnet
CLI.
dotnet new winforms -o <path-to-your-solution>\MatchingGame.Core\
For WPF projects you’d use this:
dotnet new wpf -o <path-to-your-solution>\MatchingGame.Core\
After my new WinForms .NET Core project was created, I added it to my solution.
.csproj
file.
<ItemGroup>
<Compile Include="..\<Your .NET Framework Project Name>\**\*.cs" />
<EmbeddedResource Include="..\<Your .NET Framework Project Name>\**\*.resx" />
</ItemGroup>
If you have a WPF application you also need to include .xaml
files:
<ItemGroup>
<ApplicationDefinition Include="..\WpfApp1\App.xaml" Link="App.xaml">
<Generator>MSBuild:Compile</Generator>
</ApplicationDefinition>
<Compile Include="..\WpfApp1\App.xaml.cs" Link="App.xaml.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="..\WpfApp1\MainWindow.xaml" Link="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Compile Include="..\WpfApp1\MainWindow.xaml.cs" Link="MainWindow.xaml.cs" />
</ItemGroup>
Resources.Designer.cs
), you generally want to make sure that the .NET Core version of your application uses the same namespace and the same assembly name. Copy the following settings from your .NET Framework project:
<PropertyGroup>
<RootNamespace><!-- (Your default namespace) --></RootNamespace>
<AssemblyName><!-- (Your assembly name) --></AssemblyName>
</PropertyGroup>
AssemblyInfo.cs
generation. As I mentioned earlier, in the new-style projects, AssemblyInfo.cs
is generated automatically by default. At the same time the AssemblyInfo.cs
file from the old WinForms project will be copied to the new project too, because I linked all files **\*.cs
in the previous step. That will result in duplication of AssemblyInfo.cs
. To avoid it in MatchingGame.Core
project file I set GenerateAssemblyInfo
to false
.
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
As I mentioned above, the WinForms designer for .NET Core projects is not yet available in Visual Studio. However there are two ways to work around it:
.csproj
file from the existing .NET Framework project and the new SDK-style .csproj
file of the new .NET Core WinForms project. You’ll just have to unload and reload the project with corresponding project file depending on whether you want to use the designer or not.In this blog post, I showed you how to port a desktop application containing multiple projects from .NET Framework to .NET Core. In typical cases, just retargeting your projects to .NET Core isn’t enough. I described potential issues you might encounter and ways of addressing them. Also, I demonstrated how you can still use the WinForms designer for your ported apps while it’s not yet available for .NET Core projects.
We hope you enjoyed reading Olia's post. We on the Progress Telerik team are excited about .NET Core 3.0, and for those of you out there using Telerik UI for WinForms or Telerik UI for WPF, we'd like to remind you that the controls in both products are .NET Core 3.0 compatible. If you want to test it out on your applications, check out this blog post for more info. Happy coding!
Olia is a Program Manager for Microsoft, working on .NET Core and WinForms. Before she was doing Machine Learning and AI at ML.NET team.