migrating-wpf-to-dotnet

SKILL.md

WPF .NET Framework → .NET Migration

1. 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

2. 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>

3. 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>

4. 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>

5. 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

6. 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;

7. 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>

8. 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

9. Resources

Weekly Installs
4
GitHub Stars
16
First Seen
Feb 28, 2026
Installed on
cline4
github-copilot4
codex4
kimi-cli4
gemini-cli4
cursor4