Home .NET Porting WPF applications to netcore 3.0

Porting WPF applications to netcore 3.0

by admin

The expected release of netcore 3.0 allows you to run wpf on netcore.The translation procedure for one uncomplicated project takes one or two days.Each subsequent one is much faster.

Porting WPF applications to netcore 3.0

Project preparation and conversion

The first stage of preparation is to install and run the Portability Analyzer.You’ll get an Excel spreadsheet showing you how our code meets the new requirements.

Porting WPF applications to netcore 3.0

The procedure of converting old projects was done in several steps.

  1. Microsoft recommends raising the framework version to .Net Framework 4.7.3 for older projects.
  2. Convert the structure of old projects to the new format. Replace packages.config with PackageReference.
  3. Third, adjust the structure of the csproj file to netcore format.

I want to thank Yangirov Emil with his report on netcore migration, which was very helpful. Link to his report.

It turned out that we decided to skip the first stage. The second stage implied the need to convert more than 100 projects. How this process is carried out you can read in detail here

We realized that we couldn’t do without automation. We took advantage of a solution which was already available: CsprojToVs2017 Don’t let the name of the project confuse you : the utility also converts for Visual Studio 2019.

What will happen?

The size of the csproj files will decrease. Why? From csproj will leave all the attached files with source code, remove unnecessary lines, etc.

- <Compile Include="Models\ViewModels\HistoryViewModel.cs" />- <Compile Include="Properties\Settings.Designer.cs">- <AutoGen> True</AutoGen>- <DependentUpon> Settings.settings</DependentUpon>- <DesignTimeSharedInput> True</DesignTimeSharedInput>- </Compile>

Records of connected libraries and subprojects will be reduced.

- <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">- <HintPath> ..\packages\NLog.4.3.3\lib\net45\NLog.dll</HintPath>- <Private> False</Private>- </Reference>- <ProjectReference Include="..\WpfCommon\WpfCommon.csproj">- <Project> {7ce118f6-2978-42a7-9e6a-ee5cd3057e29}</Project>- <Name> WpfCommon</Name>- </ProjectReference>+ <PackageReference Include="NLog" Version="4.6.7" />+ <ProjectReference Include="..\WpfCommon\WpfCommon.csproj" />

Common settings for multiple projects can be taken to Directory.BuildProps. This is such a special file that MsBuild looks into.
Similar to .gitignore and .editorconfig, we have a global file with general settings.
We add private PropertyGroup settings for subdirectories/projects into specific csproj files. You can read about it in details. Here.

Dependencies

The old dependencies will be for netframework. You will have to find an alternative or similar packages for nuget. For many projects, there is already a nuget package that supports netcore or netstandard.

For example, this project used an old version of DI Unity. When you switched to the new version, you had to update using and correct the code in two or three places.

using Microsoft.Practices.Unity -> using Unity;

Or maybe it will be enough to upgrade all versions of packages. And restart the studio just in case.

Change csproj to use netcore

In projects that use WPF controls, change the format to Microsoft.NET.Sdk.WindowsDesktop:

-<?xml version="1.0" encoding="utf-8"?>-<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">- <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />- <PropertyGroup/>

+<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">+ <PropertyGroup>+ <TargetFramework> netcoreapp3.0</TargetFramework>+ <AssemblyTitle> MyEnterpriseLibrary</AssemblyTitle>+ <Product> MyEnterpriseLibrary</Product>+ <OutputPath> ..\bin\$(Configuration)\</OutputPath>+ <UseWPF> true</UseWPF>+ <!--If there is already an assemblyinfo file and it suits you, then you should add -->+ <GenerateAssemblyInfo> false</GenerateAssemblyInfo></Project>

For ClassLibrary, it is sufficient to leave the type Microsoft.NET.Sdk:

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework> netcoreapp3.0</TargetFramework><AssemblyTitle> MyEnterpriseLibrary</AssemblyTitle><Product> MyEnterpriseLibrary</Product><OutputPath> ..\bin\$(Configuration)\</OutputPath></PropertyGroup><!-- ... --></Project>

In some projects that use Windows Forms controllers, you might also need to plug in the UseWindowsForms call:

<UseWindowsForms> true</UseWindowsForms>

In csproj, the approach to resource compilation flow has changed. Previously, the format allowed you to connect a file to both resources and Content,
and anywhere.

Now, if a file is in a collection, you have to pull it out of the collection and then put it into the right group.
Here’s the code that pulls file.json from the None collection and connects it to the Resource collection.

<ItemGroup><None Exclude="file.json" /><Resource Include="file.json" /></ItemGroup>

Accordingly, all files that are not sources, you need to pull from the None collection and connect to resources. For example, like this :

<ItemGroup Condition="'$(UseWPF)' == 'true' And $(UseWindowsForms) != 'true'"><None Exclude="**\*.xml;**\*.xsl;**\*.xslt;**\*.txt;**\*.bmp;**\*.ico;**\*.cur;**\*.gif;**\*.jpeg;**\*.jpe;**\*.jpg;**\*.png;**\*.dib;**\*.tiff;**\*.tif;**\*.inf;**\*.compositefont;**\*.otf;**\*.ttf;**\*.ttc;**\*.tte" /><Resource Include="**\*.xml;**\*.xsl;**\*.xslt;**\*.txt;**\*.bmp;**\*.ico;**\*.cur;**\*.gif;**\*.jpeg;**\*.jpe;**\*.jpg;**\*.png;**\*.dib;**\*.tiff;**\*.tif;**\*.inf;**\*.compositefont;**\*.otf;**\*.ttf;**\*.ttc;**\*.tte" /></ItemGroup>

Some lines will have to be deleted, because they throw off the version of the framework on .net framework 4.0.

Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets"

Somewhere after the conversion there will be some strange entries that do not let the project compile. Here are examples of such constructs :

- <ItemGroup>- <EmbeddedResource Include="**\*.resx" />- </ItemGroup>- <Compile Remove="something.cs">

WCF clients

If you used WCF, you have to regenerate bindings. You can read how to do this correctly here : docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework#updating-wcf-client-usage

What won’t take off?

Stylecop and code analysis.

Some of our projects used static code analyzers. When switching to modern editions of MsBuild, the builder explicitly suggests using the new Roslyn analyzers instead of the old static code analyzers.

I had to translate old rules to use Nuget packages Stylecop.Analyzers and FxCop.Analyzers following This Microsoft guide.
If you have several projects in different folders (a mono repository), it is much more convenient to put the connection of analyzers into Build.props and configure a single ruleset.

This is what I got :

- <RunCodeAnalysis> true</RunCodeAnalysis>+ <PackageReference Include="FxCop.Analyzers" Version="2.9.4" />

Orphan Files

The old csproj format implied an explicit connection of .cs files. Sometimes during renaming or refactoring the old files were removed from csproj, but not explicitly removed from the file system. The new format of csproj will automatically pick up all the files that are in the folder with the project, just the ones that were not deleted earlier. Most likely, they will contain errors, reference to classes, methods and resources which don’t exist anymore. This will translate into trivial build errors.

Resources

One project used a SplashScreen, one of which was randomly selected at startup. The SplashScreen instance was fed the path to the resource during initialization. For some reason I could not defeat it on netcore 3 : it swore that there was no resource.

Code that seems to work

Code that worked in the .Net Framework is likely to work in netcore. But there may be some parts of the code that the compiler has turned a blind eye to. In this case, if the code gets to the instructions that are not implemented in netcore, we will get a PlatformException.

There is a special analyzer to look for such places : github.com/dotnet/platform-compat

What’s all this for if the project works?

There are not many advantages, but they are there nonetheless.

  • Your code will get all the optimizations added to netcore.
  • The application’s launch speed will be increased.
  • Targeting future versions of C#.
  • Reduced project build time thanks to new versions of csproj.
  • Package into single exe.

Microsoft is not pushing to move applications to the new rails. However, if your application is a plug-in for another bigger one, it makes sense to target future releases, which may be on netcore as well.

Useful Links

You may also like