Migrating from .NET Framework
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
Most desktop applications that exist today were written for .NET Framework. They still run, and they will keep running on Windows for a long time, so migration is rarely an emergency. It is a decision about where the next five years of investment go: the runtime and the language keep moving, the libraries keep moving, and the people who know the older stack keep moving on. This page covers the full path for Bookshelf Desktop, the running scenario of this section, imagined here as an older .NET Framework 4.8 application that is being brought forward: the reasons, the order of work, the technologies that do not come along, the serialization change that surprises clipboard and drag-and-drop code, and the choice between staying on WPF or Windows Forms and moving to WinUI 3.
The page was written against these versions, current in October 2026:
-
.NET 10 LTS, latest patch 10.0.13, with C# 14, supported until 14 November 2028. .NET 11 is at release candidate 2 (general availability is expected 10 to 13 November 2026); nothing on this page depends on it. .NET 8 and .NET 9 both end support on 10 November 2026, so they are poor migration targets today.
-
Visual Studio 2026 18.11.1, which includes the GitHub Copilot modernization agent used below.
-
Windows App SDK 2.5.1 for the WinUI 3 option.
-
CommunityToolkit.Mvvm 8.4.2 for the view models.
-
Windows 11 and Windows 10; Windows 10 reached end of support on 14 October 2025.
Publishing the migrated application (single-file, MSIX, installers) is covered by the publishing pages of this section (see .NET Publishing Options and MSIX Packaging & Signing). The server-side counterpart of this story is on Migrating to Modern ASP.NET, which deals with Web Forms and has no mechanical upgrade path; desktop apps are in a better position, because WPF and Windows Forms themselves run on modern .NET.
Why Migrate
The official Windows Forms guide states the case plainly: .NET Framework is a Windows-only, closed-source runtime that no longer receives feature updates. It continues to receive security patches for supported versions, but it does not benefit from the performance work, language improvements or active investment that .NET does. WPF and Windows Forms both run on .NET and both keep receiving features there.
| Reason | What you gain on .NET | Caveat |
|---|---|---|
Language and libraries |
Current C# versions, nullable reference types, |
Some language features need no runtime change; others do, so check each one. |
Performance |
Runtime startup, throughput and memory improvements across releases |
Measure Bookshelf Desktop before and after; do not assume. |
Desktop investment |
Newer controls, high-DPI and accessibility improvements; dark mode on Windows 11 is available only on .NET for Windows Forms |
Desktop apps on .NET remain Windows-only. |
Ecosystem |
NuGet packages increasingly target .NET only |
A package that targets only .NET Framework may block the port. |
Tooling |
Visual Studio 2026 and the Copilot modernization agent |
The agent does not fully support Visual Basic projects. |
Side-by-side runtimes |
An app can carry its own runtime (self-contained) and upgrade independently of the machine |
The deployment size grows; see the publishing page. |
Reasons not to migrate yet are equally real: a stable line-of-business tool that is rarely changed, a hard dependency on a technology listed in the unavailable-technologies section below, or a team with no capacity to test the result. The migration decision guide at the end of this page treats "stay on .NET Framework and modernize in place" as a legitimate path.
The .NET Framework Support Story
which ties their support to the Windows version that ships them, and the lifecycle listing shows no end date for either. The decision guide describes the current state as supported but receiving only security fixes. Older versions have already ended or have dates.
| Version | Support end (lifecycle listing) | Meaning for a migration plan |
|---|---|---|
4.8.1 |
None listed |
The newest; no further feature versions are planned. |
4.8 |
None listed |
The recommended retarget if you cannot move yet. |
4.7.2 |
None listed |
The minimum recommended baseline before porting to .NET. |
4.6.2 |
13 January 2027 |
Retarget soon. |
4.5.2, 4.6, 4.6.1 |
26 April 2022 (ended) |
Already out of support. |
3.5 SP1 |
10 January 2029 |
Standalone product under the Fixed Policy since Windows 10 1809. |
Two consequences follow. First, an application on 4.8 is not in immediate danger, so the migration can be staged rather than rushed. Second, "supported" does not mean "evolving": security fixes only, no new APIs, no new language-runtime features, and a shrinking set of third-party packages that still test against it.
The Migration Flow
A desktop migration is a sequence of decisions, not a single conversion. The flow below is the shape this page follows; each box is a section below.
The first useful fact about the flow is that the two long boxes, preparation and the unavailable-technologies check, happen before any tool rewrites a project. They are also the steps that determine whether the later automated steps succeed.
Prepare While Still on .NET Framework
Every step in this section can be done and tested on .NET Framework, which is why it is safe to do first. The official Windows Forms guide lists them as the preparation that reduces the scope of the real port.
| Step | Detail |
|---|---|
Upgrade the tooling |
Use a Visual Studio version that supports the target .NET version; Visual Studio 2026 for .NET 10. |
Retarget to 4.7.2 or later |
The broadest API overlap with .NET Standard 2.0. Recompile and fix issues before continuing. |
Convert |
In Visual Studio, right-click |
Convert to the SDK-style project format |
Possible while still targeting .NET Framework; shrinks the change set of the actual port. |
Update NuGet dependencies |
Prefer packages that target .NET Standard 2.0 or .NET; read release notes for breaking changes. |
Audit third-party dependencies |
Controls and libraries that target only .NET Framework might not work on .NET. |
Commit to source control |
The Copilot agent commits as it goes, so the solution must be in a Git repository. |
SDK-Style Projects
SDK-style project infers them and states only what differs. The Bookshelf Desktop project after conversion, still targeting .NET Framework 4.8, and then after retargeting to .NET 10:
<!-- Step 1: SDK-style, still on .NET Framework 4.8 -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<OutputType>WinExe</OutputType>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
<!-- Step 2: retargeted to .NET 10 -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0-windows</TargetFramework>
<OutputType>WinExe</OutputType>
<UseWPF>true</UseWPF>
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup>
</Project>
The nullable and implicit-using properties are shown disabled on purpose: enabling them is a separate effort after
the port, and the Copilot agent’s sample plan treats it the same way. The other Windows UI properties are
UseWindowsForms for Windows Forms (and both can be set in a project that hosts one inside the other). A WPF
project on .NET targets net10.0-windows, which is the same moniker the project-structure page of this section
uses.
Three things change besides the file format. NuGet references move to PackageReference, so packages live in a
global cache instead of a per-solution packages folder. Assembly information that used to live in a hand-written
AssemblyInfo.cs is normally generated from project properties, a point covered in the versioning part of the
project-structure page. And the runtime configuration file (app.config with System.Configuration) still works
through the System.Configuration.ConfigurationManager package, so settings can be moved to appsettings.json and
the Options pattern incrementally rather than in the same change.
Upgrading with GitHub Copilot App Modernization
The .NET Upgrade Assistant is officially deprecated. The current recommendation is the GitHub Copilot modernization agent, included with Visual Studio 2026 and with Visual Studio 2022 17.14.16 or later. The agent analyzes projects and dependencies, produces a step-by-step plan with recommendations and automated fixes, and commits each change so you can validate or roll back. It handles WPF, Windows Forms, WinUI, class libraries, console apps and test projects, and upgrades from .NET Framework (any version) to .NET 8 or later, or to .NET Framework 4.8.1.
| Stage | What the agent does | File it writes under .github/upgrades |
|---|---|---|
Assessment |
Examines structure, dependencies and code patterns; lists breaking changes, API incompatibilities and scope; proposes a strategy (bottom-up, top-down or all-at-once) |
|
Planning |
Turns assessment and your confirmed options into a specification of order, target frameworks, package paths and risks |
|
Execution |
Breaks the plan into tasks with validation criteria, applies fixes, commits per task or group |
|
You start it from Visual Studio by right-clicking the solution and choosing Modernize, or by typing @Modernize in
Copilot Chat. In Visual Studio Code it is @upgrade, and the same service is available from the GitHub Copilot CLI
and the Copilot app. The flow mode defaults to automatic; for a solution like Bookshelf Desktop with a data layer,
a view-model library and a WPF shell, switch to guided mode so each stage can be reviewed. The state lives in the
repository, so the work can pause and resume across editors.
Upgrade everything to .NET 10.
Source: GitHub Copilot upgrade overview
The agent is a helper, not a guarantee. The official walk-through of a WPF sample ends with the instruction to build,
run, run the unit tests and test the application thoroughly, and it warns that after migrating project files the
WPF designer may need a Visual Studio restart. Visual Basic is the main gap: the agent does not fully support VB
projects, which are upgraded by hand (create a new SDK-style project, copy sources, re-add packages) or with the
standard Copilot agent. Mixed solutions can use the agent for the C# projects and handle VB separately. The
My.Computer and My.User extensions were removed in .NET and are flagged during assessment.
Technologies That Do Not Come Along
Some .NET Framework technologies have no equivalent in .NET 6 and later. They are the most disruptive part of a migration, so find them before starting. The official list, with the replacement it recommends:
| Technology | Status on .NET | Replacement |
|---|---|---|
Application domains ( |
Creating more app domains is not supported; some members work, some do nothing, some throw |
Separate processes or containers for isolation; |
.NET Remoting |
Not supported; |
|
Code access security (CAS) |
Not supported and no longer a security boundary; WPF treats CAS-related calls as full trust |
Operating-system boundaries: virtualization, containers, user accounts, AppContainer. |
Security transparency |
Not supported as a security boundary |
The same OS-level isolation. |
WCF server |
Not available; the WCF client works through the |
CoreWCF, gRPC or ASP.NET Core Web API. |
ASMX web services |
Not available |
ASP.NET Core Web API or minimal API endpoints. |
Windows Workflow Foundation |
Not supported |
CoreWF, an open-source port. |
|
Not supported |
Redesign; |
Multi-module assemblies |
|
Merge into a single assembly. |
XSLT script blocks |
Not supported |
Move the logic into code. |
For Bookshelf Desktop the realistic impact is small and typical: a plug-in loader built on AppDomain becomes an
AssemblyLoadContext, and a WCF service client keeps working through the System.ServiceModel packages. Code that
only uses Remoting-style delegates asynchronously (BeginInvoke) must be rewritten with async and await.
The security implications of dropping CAS are discussed on the Security & App Isolation page.
Missing Windows-specific APIs are a separate and milder problem. The Microsoft.Windows.Compatibility NuGet package
provides about 20,000 APIs (Registry, Event Log, WMI, performance counters, directory services, ACLs, Windows
Services, cryptography, ports, ODBC, CodeDom and more) on top of .NET Standard 2.0, so the application builds and
runs first and deeper refactoring comes later.
dotnet add package Microsoft.Windows.Compatibility
dotnet add package System.Configuration.ConfigurationManager
From WebBrowser to WebView2
The WPF WebBrowser control is based on Internet Explorer, which is no longer supported. On modern .NET the
replacement is the WebView2 control, from the Microsoft.Web.WebView2 NuGet package, which uses the Microsoft
Edge engine. Depending on the Windows version, users might need the WebView2 runtime installed separately, so the
installer or the packaging step has to account for it. The XAML change is small; the work is the asynchronous
initialization:
<Window x:Class="Bookshelf.Desktop.HelpWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
Title="Help" Width="800" Height="600">
<DockPanel>
<wv2:WebView2 x:Name="webView" Source="https://example.com/bookshelf/help" />
</DockPanel>
</Window>
namespace Bookshelf.Desktop;
public partial class HelpWindow : Window
{
public HelpWindow()
{
InitializeComponent();
Loaded += async (_, _) => await webView.EnsureCoreWebView2Async(null);
}
}
The Interop: Win32, WinForms & Web Content page of this section covers the airspace restriction and the rest of the WebView2 API; this page only needs the fact that the swap is part of the port.
BinaryFormatter, the Clipboard and Drag-and-Drop
BinaryFormatter was deprecated for security reasons and removed from the runtime in .NET 9: the APIs remain, but
their implementation always throws PlatformNotSupportedException, so the old backward-compatibility flag no longer
helps. Applications that pass custom objects through the clipboard or a drag-and-drop operation are the usual
victims, because older code relied on BinaryFormatter to serialize them implicitly.
| Situation on .NET 9 and later | Behaviour |
|---|---|
|
Completes without throwing, but serialization fails and no data is stored. |
|
Obsolete in .NET 10; when |
Primitives, |
Still work with |
Custom types |
Use |
Drag-and-drop |
|
For Bookshelf, copying a book between two running instances is exactly this case. A migration-safe version in the
Windows Forms project (net10.0-windows with UseWindowsForms) uses JSON and the typed read:
using System.Windows.Forms;
using Bookshelf.Core;
namespace Bookshelf.WinForms;
public static class BookClipboard
{
private const string Format = "Bookshelf.Book";
public static void Copy(Book book) =>
Clipboard.SetDataAsJson(Format, book);
public static Book? Paste() =>
Clipboard.TryGetData(Format, out Book? book) ? book : null;
}
The example lives in Bookshelf.WinForms because it uses the Windows Forms clipboard API from the official page;
the WPF clipboard class (System.Windows.Clipboard) is a separate type, so check its own documentation for the
equivalent overloads before porting WPF code. Prefer fixing the code
over re-enabling the formatter: the unsupported System.Runtime.Serialization.Formatters compatibility package
brings the working implementation back, together with its vulnerabilities, and the official page calls it a
temporary bridge only. Data that other systems wrote with BinaryFormatter can still be read safely with the NRBF
payload reader (System.Formats.Nrbf) without general-purpose deserialization. Resources (.resx) holding custom
types also need attention; strings and icons are fine.
Further source: BinaryFormatter migration guide
Third-Party Control Vendors
A UI migration is often decided by one question: do the controls we pay for support the target? The official decision guide gives the pattern: most vendors support modern .NET for WPF and WinForms, but WinUI 3 support must be checked per vendor, and Telerik, DevExpress, Syncfusion and Infragistics all publish compatibility matrices.
| Check | Why it matters | How |
|---|---|---|
Does the vendor ship a |
The package must load on .NET, not only on .NET Framework |
Read the vendor’s supported-platforms table; try restoring the package into a test project. |
Does the vendor support your Visual Studio and .NET versions? |
Designers and licenses are version-bound |
Check the release notes for the VS 2026 and .NET 10 lines. |
Is there a WinUI 3 equivalent for each control you use? |
A missing grid or chart blocks a UI move |
List the controls per screen; compare with the vendor’s WinUI matrix. |
Is the license transferable? |
Some licenses are per-platform |
Ask the vendor before starting. |
Are visual and behavioural differences documented? |
Themes and templating differ |
Run both versions side by side on a sample screen. |
This page deliberately names no vendor feature beyond what the official guide states; verify each product yourself, since matrices change release by release.
Choosing the Destination: Upgrade, Modernize or Move
After the runtime port, the next question is how far to go. The official migration decision guide is explicit: you do not have to migrate to WinUI 3. WPF and Windows Forms are fully supported on modern .NET and keep receiving feature updates, and many applications are best served by upgrading and then adding Windows App SDK features incrementally.
| Path | What changes | Best for | Risk |
|---|---|---|---|
Upgrade in place |
Runtime (.NET Framework to modern .NET), project format, some packages |
Apps that work today and need long-term support, performance or modern APIs |
Low to medium |
Modernize in place |
Nothing in the UI; you add Windows App SDK, WinRT APIs or MSIX packaging |
Apps needing notifications, widgets, AI features or clean install without a UI change |
Low |
Move to WinUI 3 |
UI framework, namespaces, controls and the app lifecycle |
Apps whose UI layer needs a refresh, ported view by view or in a new project |
Medium to high |
The paths are not exclusive. The common order is upgrade, then modernize, and later migrate individual views. A C# WinUI 3 app requires modern .NET and cannot target .NET Framework, so an app on .NET Framework must be upgraded first. The guide’s criteria, condensed:
| Criterion | Upgrade | Modernize | Move to WinUI 3 |
|---|---|---|---|
Solution with 10 or more projects |
Automated tooling handles it |
No restructuring |
Needs per-project planning |
WCF or ASMX services |
CoreWCF migration |
No impact |
Same WCF migration |
COM interop |
Test thoroughly |
No change |
Mostly works; some WinRT APIs need window-handle initialization |
Third-party UI controls |
Most vendors support .NET |
No UI change |
Vendors must offer WinUI 3 versions |
Team capacity |
Low to medium |
Low |
High |
Visual Studio XAML designer |
Supported for WPF on .NET |
Supported |
Not yet available for WinUI 3 |
Long-term .NET support |
Gained |
.NET Framework 4.8 gets security fixes only |
WinUI 3 runs on modern .NET |
For Bookshelf Desktop the recommended sequence is: upgrade WPF to .NET 10, replace the WebBrowser with WebView2,
fix the clipboard, then decide. If a feature such as app notifications is the only reason for change, modernize in
place; the WinUI 3 & the Windows App SDK page shows what the
Windows App SDK adds, and Interop: Win32, WinForms &
Web Content shows how to call it from a WPF or WinForms process. If the whole UI is due a refresh,
XAML Dialects: WPF, WinUI & MAUI lists what changes in
markup, binding and styling, and Windows App Platforms compares the
stacks. Because Bookshelf Desktop keeps its logic in Bookshelf.Core view models, a view-by-view move is feasible:
the view models do not change, only the views and the services behind IDialogService.
Windows Forms Notes
The Windows Forms path is the same flow with the same preparation. Two points deserve repeating. Windows Forms on .NET is Windows-only, like WPF. And the clipboard and drag-and-drop change above is a Windows Forms .NET 10 feature set, so it applies directly to existing WinForms code. The designer on .NET runs out of process, which is why some older third-party design-time components need updated versions; see Windows Forms on .NET for the details.
A Migration Checklist
-
Record the baseline: target framework, packages, third-party controls, technologies from the unavailable list.
-
Retarget to .NET Framework 4.7.2 or later; migrate to
PackageReference; convert to SDK-style; update packages. -
Create a Git branch and commit; keep unit tests green at every step.
-
Run the Copilot modernization agent in guided mode, review
assessment.md,plan.mdandtasks.md. -
Replace unavailable technologies:
AppDomain, Remoting, CAS, WCF server, Workflow,System.EnterpriseServices. -
Add
Microsoft.Windows.CompatibilityandSystem.Configuration.ConfigurationManagerwhere needed. -
Replace
WebBrowserwith WebView2; plan for the WebView2 runtime in the installer. -
Replace
BinaryFormatteruse, including clipboard and drag-and-drop, with JSON and typed reads. -
Exercise high-DPI, printing, clipboard and every third-party control by hand.
-
Decide upgrade, modernize or move to WinUI 3; update the publishing pipeline to produce a .NET 10 build.
Pitfalls
-
Treating the agent as the end of the work. It produces a building project; behaviour still needs testing.
-
Migrating while also changing the design. Port first, then enable nullable, dependency injection and new settings; mixing them hides the cause of regressions.
-
Targeting .NET 8 or .NET 9 today. Both end support on 10 November 2026; target .NET 10 LTS.
-
Ignoring the silent clipboard failure.
SetDatawith a custom type does not throw; the data is just missing. -
Forgetting the WebView2 runtime. A
WebBrowserreplacement that works on a developer machine can fail on a clean one. -
Assuming a package works because it restores. A .NET Framework-only package may restore with a warning and fail at run time.
-
Skipping the Visual Studio restart. After project-format changes the WPF designer can misbehave until it is restarted.
-
Moving to WinUI 3 from .NET Framework in one step. It cannot target .NET Framework; upgrade first.
Summary
Migrating from .NET Framework is a staged flow: decide whether to move, prepare the project while it is still on
.NET Framework (4.7.2 or later, PackageReference, SDK-style), look for unavailable technologies before running
any tool, let the GitHub Copilot modernization agent drive the port and review each stage, then fix the known
behaviour changes (WebBrowser to WebView2, BinaryFormatter to JSON in the clipboard and drag-and-drop). After
that, the decision guide offers three honest destinations: stay on WPF or Windows Forms on .NET 10, add Windows App
SDK features in place, or move views to WinUI 3.