WPF .NET Framework → .NET Migration
- Migration Checklist
1.1 Pre-Migration
-
Verify .NET Framework version (4.6.1+ recommended)
-
Check NuGet package .NET compatibility
-
Verify third-party library .NET versions
-
Check Windows Forms interop usage
-
Check WCF client usage
-
Check ClickOnce deployment usage
1.2 Migration
-
Convert project file to SDK style
-
NuGet packages.config → PackageReference
-
Clean up AssemblyInfo.cs
-
app.config → appsettings.json (optional)
-
Build and fix errors
-
Run tests
1.3 Post-Migration
-
Test self-contained deployment
-
Test single-file deployment
-
Ready to Run (R2R) optimization
-
Performance benchmark
- Project File Conversion
2.1 Old Format (.NET Framework)
<?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" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{GUID}</ProjectGuid> <OutputType>WinExe</OutputType> <RootNamespace>MyApp</RootNamespace> <AssemblyName>MyApp</AssemblyName> <TargetFrameworkVersion>v4.8</TargetFrameworkVersion> <!-- ... hundreds of lines ... --> </PropertyGroup> <!-- ... --> </Project>
2.2 New SDK Format (.NET 10)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net10.0-windows</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <UseWPF>true</UseWPF> </PropertyGroup>
<ItemGroup> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.*" /> </ItemGroup>
</Project>
- Step-by-Step Migration
3.1 Try-Convert Tool (Automated)
Install
dotnet tool install -g try-convert
Run
try-convert -p MyApp.csproj
3.2 Manual Conversion
Step 1: Create new project file
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net10.0-windows</TargetFramework> <UseWPF>true</UseWPF> <RootNamespace>MyApp</RootNamespace> <AssemblyName>MyApp</AssemblyName> </PropertyGroup> </Project>
Step 2: Migrate NuGet references
<!-- packages.config → PackageReference --> <ItemGroup> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" /> </ItemGroup>
Step 3: Clean up AssemblyInfo.cs
SDK style auto-generates most attributes. Keep only custom attributes:
// Properties/AssemblyInfo.cs (keep only necessary items) using System.Runtime.InteropServices;
[assembly: ComVisible(false)] [assembly: Guid("your-guid-here")]
Disable generation in project file (if needed):
<PropertyGroup> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup>
- Breaking Changes
4.1 Removed APIs
API .NET Framework .NET Alternative
BinaryFormatter
✅ ⚠️ Removed for security. Use System.Text.Json
AppDomain.CreateDomain
✅ ❌ Not supported
Remoting
✅ ❌ Use gRPC, SignalR
Code Access Security
✅ ❌ Removed
4.2 WCF Client
<!-- WCF client (server not supported) --> <ItemGroup> <PackageReference Include="System.ServiceModel.Http" Version="8.0." /> <PackageReference Include="System.ServiceModel.Primitives" Version="8.0." /> </ItemGroup>
4.3 Windows Forms Interop
<PropertyGroup> <UseWPF>true</UseWPF> <UseWindowsForms>true</UseWindowsForms> </PropertyGroup>
- Dual Targeting (Gradual Migration)
5.1 Multi-Target
<PropertyGroup> <TargetFrameworks>net48;net10.0-windows</TargetFrameworks> <UseWPF>true</UseWPF> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net48'"> <Reference Include="System.Windows.Forms" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0-windows'"> <PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.*" /> </ItemGroup>
5.2 Conditional Compilation
#if NET48 // .NET Framework specific code System.Configuration.ConfigurationManager.AppSettings["Key"]; #else // .NET specific code Microsoft.Extensions.Configuration.IConfiguration config; #endif
- Modern .NET Features
6.1 Nullable Reference Types
<PropertyGroup> <Nullable>enable</Nullable> </PropertyGroup>
public partial class MainViewModel : ObservableObject { [ObservableProperty] private string? _optionalValue; [ObservableProperty] private string _requiredValue = string.Empty; }
6.2 File-Scoped Namespaces
// Old namespace MyApp.ViewModels { public class MainViewModel { } }
// New namespace MyApp.ViewModels;
public class MainViewModel { }
6.3 Global Usings
// GlobalUsings.cs global using System; global using System.Collections.Generic; global using System.Collections.ObjectModel; global using System.Linq; global using System.Threading.Tasks; global using CommunityToolkit.Mvvm.ComponentModel; global using CommunityToolkit.Mvvm.Input;
- Deployment
7.1 Self-Contained
dotnet publish -c Release -r win-x64 --self-contained true
7.2 Single File
<PropertyGroup> <PublishSingleFile>true</PublishSingleFile> <SelfContained>true</SelfContained> <RuntimeIdentifier>win-x64</RuntimeIdentifier> <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> </PropertyGroup>
7.3 Ready to Run (AOT)
<PropertyGroup> <PublishReadyToRun>true</PublishReadyToRun> </PropertyGroup>
- Common Issues
Issue Solution
System.Drawing error Add System.Drawing.Common NuGet
WCF server code Migrate to CoreWCF or gRPC
app.config reading Use Microsoft.Extensions.Configuration
XAML designer error Requires Visual Studio 2022 17.8+
ClickOnce Replace with MSIX or custom updater
- Resources
-
.NET Upgrade Assistant
-
Porting from .NET Framework
-
WPF Migration Guide
-
Breaking Changes in .NET