docs/architecture/decisions/ADR-001-multi-target-framework.md
Accepted
The WPF UI library needs to support a wide range of .NET implementations to maximize compatibility with existing projects while leveraging modern .NET features where available.
Target frameworks: net10.0-windows;net9.0-windows;net8.0-windows;net481;net472;net462
Rationale:
-windows TFM suffix for .NET 5+Target frameworks: net10.0;net9.0;net8.0;net462;netstandard2.1;netstandard2.0
Rationale:
Target frameworks: Same as Abstractions
Rationale:
All package versions are managed in Directory.Packages.props:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- Package versions defined here -->
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.242" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
</ItemGroup>
</Project>
Individual projects reference packages without version attributes:
<PackageReference Include="Microsoft.Windows.CsWin32" />
Benefits:
#if NET5_0_OR_GREATER
// Modern .NET APIs (Environment.OSVersion)
#else
// .NET Framework fallback (registry)
#endif
#if NET6_0_OR_GREATER
// DisposeAsync for CancellationTokenRegistration
#endif
#if NET8_0_OR_GREATER
// Latest .NET 8+ features
#endif
<!-- Only for .NET Framework 4.6.2 -->
<PackageReference Include="System.ValueTuple" Condition="'$(TargetFramework)' == 'net462'" />
<!-- Only for .NET Core/5+ (not .NET Framework 4.6.2) -->
<PackageReference Include="System.Drawing.Common" Condition="'$(TargetFramework)' != 'net462'" />
Package: PolySharp (build-time source generator)
Provides polyfills for newer C# features on older target frameworks:
Configuration:
<ItemGroup>
<CompilerVisibleProperty Include="PolySharpExcludeGeneratedTypes" />
</ItemGroup>
<PropertyGroup>
<!-- Exclude specific polyfills if needed -->
<PolySharpExcludeGeneratedTypes>
System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute;
System.Diagnostics.CodeAnalysis.UnscopedRefAttribute
</PolySharpExcludeGeneratedTypes>
</PropertyGroup>
Benefits:
Set to C# 14.0 across all projects:
<LangVersion>14.0</LangVersion>
Note:
Wpf.Ui.csprojoverrides this to<LangVersion>preview</LangVersion>to enable C# preview features.
Combined with PolySharp, this enables:
Directory.Packages.props only — Central Package Management is the single source of truth for NuGet versions-windows TFM suffix for projects with WPF dependencies (e.g., net10.0-windows, not net10.0)netstandard2.0/netstandard2.1 for abstraction-only packages that have no WPF or Windows dependency#if NET{X}_0_OR_GREATER directives — never use runtime version checks for compile-time API differencesIsExternalInit, CallerArgumentExpression)LangVersion in Directory.Build.props — override in individual .csproj only when justified (currently only Wpf.Ui.csproj overrides to preview)Version attribute to PackageReference in .csproj files — all versions must be in Directory.Packages.props#if with specific patch versions (e.g., NET8_0_10) — only use _OR_GREATER suffixed symbols<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> in Directory.Build.props causes build errors if Version is specified in .csprojdotnet build — missing APIs surface as compile errors immediately#if directives for framework-specific codeCentral build properties defined once:
<PropertyGroup>
<Version>4.2.0</Version>
<LangVersion>14.0</LangVersion>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<DefineConstants>$(DefineConstants);NET8_0_OR_GREATER</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<DefineConstants>$(DefineConstants);BELOW_NET8</DefineConstants>
</PropertyGroup>