Target Framework Management Guide
This document provides comprehensive guidance on managing target framework selection across the Krypton Standard Toolkit solution. The target framework configuration is centralized using MSBuild's Directory.Build.props mechanism, ensuring consistency and maintainability across all projects.
Table of Contents
- Overview
- Architecture
- How It Works
- Configuration Scenarios
- Managing Target Frameworks
- Adding New Projects
- Overriding Behavior
- Troubleshooting
- Best Practices
Overview
The Krypton Standard Toolkit supports multiple .NET target frameworks to ensure broad compatibility:
- .NET Framework:
net472,net48,net481 - .NET (Modern):
net8.0-windows,net9.0-windows,net10.0-windows
The target framework selection logic is centralized in Source/Krypton Components/Directory.Build.props, eliminating the need to duplicate configuration across individual project files. This approach provides:
- Single Source of Truth: All target framework logic in one location
- Consistency: All projects automatically use the same framework selection rules
- Maintainability: Changes propagate automatically to all projects
- Flexibility: Support for different build configurations and scenarios
Architecture
File Structure
Source/Krypton Components/
├── Directory.Build.props ← Centralized target framework logic
├── Krypton.Toolkit/
│ └── Krypton.Toolkit 2022.csproj
├── Krypton.Ribbon/
│ └── Krypton.Ribbon 2022.csproj
├── Krypton.Navigator/
│ └── Krypton.Navigator 2022.csproj
├── Krypton.Workspace/
│ └── Krypton.Workspace 2022.csproj
├── Krypton.Docking/
│ └── Krypton.Docking 2022.csproj
├── Krypton.Utilities/
│ └── Krypton.Utilities.csproj
├── Krypton.Standard.Toolkit/
│ └── Krypton.Standard.Toolkit.csproj
└── TestForm/
└── TestForm.csproj
MSBuild Import Chain
The Directory.Build.props file is automatically imported by MSBuild for all projects in the directory and its subdirectories. The import hierarchy is:
- Root
Directory.Build.props(if exists) Source/Krypton Components/Directory.Build.props← Target framework logic here- Individual project
.csprojfiles
Properties defined in Directory.Build.props are available to all projects, but individual projects can override them if needed.
How It Works
The Selection Logic
The target framework selection uses MSBuild's <Choose> element with conditional evaluation:
<Choose>
<!-- Condition 1: Debug/Nightly/Canary builds -->
<When Condition="'$(Configuration)' == 'Nightly' Or '$(Configuration)' == 'Canary' Or '$(Configuration)' == 'Debug'">
<PropertyGroup>
<TargetFrameworks>net472;net48;net481;net8.0-windows;net9.0-windows;net10.0-windows</TargetFrameworks>
</PropertyGroup>
</When>
<!-- Condition 2: Skip .NET Framework builds -->
<When Condition="'$(SkipNetFramework)' == 'true'">
<PropertyGroup>
<TargetFrameworks>net8.0-windows;net9.0-windows;net10.0-windows</TargetFrameworks>
</PropertyGroup>
</When>
<!-- Condition 3: Default (Release/Installer) -->
<Otherwise>
<PropertyGroup>
<TargetFrameworks>net48;net481;net8.0-windows;net9.0-windows;net10.0-windows</TargetFrameworks>
<!-- Override when TFMs=all -->
<TargetFrameworks Condition="'$(TFMs)' == 'all'">net472;net48;net481;net8.0-windows;net9.0-windows;net10.0-windows</TargetFrameworks>
</PropertyGroup>
</Otherwise>
</Choose>
Evaluation Order
MSBuild evaluates conditions in order:
- First: Checks if Configuration is
Debug,Nightly, orCanary - Second: Checks if
SkipNetFrameworkproperty istrue - Third: Falls back to the default (
Otherwise) case
Important: Only the first matching condition applies. If multiple conditions could match, the first one wins.
Configuration Scenarios
Scenario 1: Debug Development Builds
Configuration: Debug, Nightly, or Canary
Target Frameworks: All frameworks
net472;net48;net481;net8.0-windows;net9.0-windows;net10.0-windows;net11.0-windows
Use Case:
- Comprehensive testing across all supported frameworks
- Ensuring compatibility during development
- CI/CD pipelines that need full coverage
Example Build Command:
dotnet build "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" -c Debug
Scenario 2: Skip .NET Framework (Modern Only)
Property: SkipNetFramework=true
Target Frameworks: Modern .NET only
net8.0-windows;net9.0-windows;net10.0-windows;net11.0-windows
Use Case:
- Faster builds during development/testing
- CI scenarios where .NET Framework support isn't needed
- Testing modern .NET features only
Example Build Command:
dotnet build "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" -p:SkipNetFramework=true
Scenario 3: Release/Stable Builds (Default)
Configuration: Release, Installer, or any other configuration
Target Frameworks: .NET Framework 4.8+ and modern .NET
net48;net481;net8.0-windows;net9.0-windows;net10.0-windows;net11.0-windows
Use Case:
- Production builds
- NuGet package creation
- Balanced build time vs. compatibility
Example Build Command:
dotnet build "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" -c Release
Scenario 4: All Frameworks (Including net472)
Property: TFMs=all
Target Frameworks: All frameworks including .NET Framework 4.7.2
net472;net48;net481;net8.0-windows;net9.0-windows;net10.0-windows;net11.0-windows
Use Case:
- Full compatibility testing
- Legacy support validation
- Complete framework coverage
Example Build Command:
dotnet build "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" -c Release -p:TFMs=all
Note: This only works when the default (Otherwise) branch is active. It won't work with Debug/Nightly/Canary configurations since they already include all frameworks.
Managing Target Frameworks
Modifying the Centralized Logic
To change target framework selection for all projects, edit Source/Krypton Components/Directory.Build.props:
Adding a New Target Framework
- Locate the relevant
<TargetFrameworks>property group - Add the new TFM to the semicolon-separated list:
<TargetFrameworks>net48;net481;net8.0-windows;net9.0-windows;net10.0-windows;net11.0-windows</TargetFrameworks>
- Update all relevant conditions (Debug, SkipNetFramework, default)
Removing a Target Framework
- Remove the TFM from all
<TargetFrameworks>properties - Ensure you update all three condition branches
Changing Framework Selection Rules
- Modify the condition expressions in the
<Choose>element - Adjust the
<TargetFrameworks>values in the corresponding<PropertyGroup>
Example: To exclude net472 from Debug builds:
<When Condition="'$(Configuration)' == 'Nightly' Or '$(Configuration)' == 'Canary' Or '$(Configuration)' == 'Debug'">
<PropertyGroup>
<TargetFrameworks>net48;net481;net8.0-windows;net9.0-windows;net10.0-windows</TargetFrameworks>
</PropertyGroup>
</When>
Adding a New Configuration
To add a new build configuration that uses specific target frameworks:
- Add a new
<When>condition before the<Otherwise>block:
<When Condition="'$(Configuration)' == 'YourNewConfig'">
<PropertyGroup>
<TargetFrameworks>net8.0-windows;net9.0-windows</TargetFrameworks>
</PropertyGroup>
</When>
- Ensure the new configuration is defined in project files (if needed):
<PropertyGroup>
<Configurations>Debug;Release;Installer;Nightly;Canary;YourNewConfig</Configurations>
</PropertyGroup>
Modifying Framework Lists
When updating framework lists, maintain consistency across all branches:
| Branch | Typical Frameworks | Notes |
|---|---|---|
| Debug/Nightly/Canary | All frameworks | Maximum compatibility testing |
| SkipNetFramework | Modern .NET only | Fast builds, modern features |
| Default (Release) | net48+ and modern | Production balance |
Adding New Projects
Automatic Inheritance
New projects added to Source/Krypton Components/ or any subdirectory automatically inherit the target framework configuration from Directory.Build.props. No action is required.
Project File Structure
Your new project file should look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<!-- Other project-specific properties -->
</PropertyGroup>
<!-- Target frameworks are automatically set by Directory.Build.props -->
</Project>
Verification
To verify a project is using the centralized configuration:
- Build the project and check the output
- Inspect the build log for target framework information
- Use MSBuild property evaluation:
dotnet msbuild "YourProject.csproj" -t:GetTargetFramework -p:Configuration=Debug
Overriding Behavior
Project-Level Override
If a specific project needs different target frameworks, you can override the centralized setting by defining <TargetFrameworks> directly in the project file:
<Project Sdk="Microsoft.NET.Sdk">
<!-- Override centralized configuration -->
<PropertyGroup>
<TargetFrameworks>net8.0-windows;net9.0-windows</TargetFrameworks>
</PropertyGroup>
<!-- Rest of project file -->
</Project>
Warning: Overriding should be rare and well-documented. It breaks the "single source of truth" principle.
Conditional Override
You can also conditionally override based on project-specific needs:
<PropertyGroup Condition="'$(MSBuildProjectName)' == 'SpecialProject'">
<TargetFrameworks>net8.0-windows</TargetFrameworks>
</PropertyGroup>
Build-Time Override
Override target frameworks at build time using MSBuild properties:
dotnet build YourProject.csproj -p:TargetFrameworks=net8.0-windows
Note: This overrides the centralized configuration for that specific build only.
Troubleshooting
Problem: Project Not Using Centralized Configuration
Symptoms:
- Project builds with different frameworks than expected
- Build output shows unexpected target frameworks
Solutions:
Verify Import Chain:
- Ensure
Directory.Build.propsexists inSource/Krypton Components/ - Check that the project is in a subdirectory of that location
- Ensure
Check for Overrides:
- Search the project file for
<TargetFrameworks>property - Remove any project-level definitions if they shouldn't be there
- Search the project file for
Verify MSBuild Evaluation:
dotnet msbuild YourProject.csproj -t:GetTargetFramework -v:detailed
Problem: Configuration Not Matching Expected Behavior
Symptoms:
- Debug builds not including all frameworks
- Release builds including net472 when they shouldn't
Solutions:
Check Configuration Name:
- Ensure configuration names match exactly (case-sensitive)
- Verify:
Debug,Nightly,Canary(notdebug,DEBUG)
Verify Condition Logic:
- Review the
<Choose>conditions inDirectory.Build.props - Ensure conditions are evaluated in the correct order
- Review the
Check Property Values:
dotnet build YourProject.csproj -c Debug -v:detailedLook for property evaluation in the build log
Problem: TFMs=all Not Working
Symptoms:
- Setting
TFMs=alldoesn't include net472
Solutions:
Check Configuration:
TFMs=allonly works with Release/Installer configurations- Debug/Nightly/Canary already include all frameworks
Verify Property Syntax:
dotnet build YourProject.csproj -c Release -p:TFMs=all
Problem: SkipNetFramework Not Working
Symptoms:
- Setting
SkipNetFramework=truestill builds .NET Framework targets
Solutions:
Check Condition Order:
- Debug/Nightly/Canary configurations take precedence
- Use Release configuration with SkipNetFramework
Verify Property Value:
dotnet build YourProject.csproj -c Release -p:SkipNetFramework=true -v:detailed
Problem: Build Performance Issues
Symptoms:
- Builds taking too long
- Building unnecessary frameworks
Solutions:
Use SkipNetFramework for Development:
dotnet build -p:SkipNetFramework=trueBuild Specific Framework:
dotnet build -f net8.0-windowsUse Release Configuration:
- Release builds exclude net472 by default (faster)
Best Practices
1. Centralize All Framework Logic
✅ Do: Keep all target framework selection in Directory.Build.props
❌ Don't: Duplicate framework logic in individual project files
2. Document Special Cases
If a project must override the centralized configuration:
✅ Do: Add a comment explaining why:
<!-- This project only supports .NET 8+ due to API dependencies -->
<PropertyGroup>
<TargetFrameworks>net8.0-windows;net9.0-windows;net10.0-windows</TargetFrameworks>
</PropertyGroup>
❌ Don't: Override without documentation
3. Test Configuration Changes
Before committing changes to Directory.Build.props:
- Build a representative project in each configuration
- Verify expected frameworks are built
- Check build output for correctness
4. Use Appropriate Configurations
- Development: Use
Debugfor comprehensive testing - CI/CD: Use
NightlyorCanaryfor full framework coverage - Local Testing: Use
SkipNetFramework=truefor faster iteration - Production: Use
Releasefor optimized builds
5. Maintain Framework Lists Consistently
When adding/removing frameworks:
- Update all three condition branches
- Update documentation
- Test across all configurations
- Update CI/CD pipelines if needed
6. Version Control Considerations
- Commit
Directory.Build.propschanges with clear messages - Document breaking changes if framework support is removed
- Tag releases when framework support changes
7. Performance Optimization
- Use
SkipNetFramework=trueduring active development - Use Release configuration for production builds
- Build specific frameworks when testing:
-f net8.0-windows
Advanced Topics
MSBuild Property Evaluation
Understanding MSBuild property evaluation order helps debug issues:
- Imports:
Directory.Build.propsis imported early - Properties: Properties are evaluated in order of appearance
- Conditions: Conditions are evaluated at property evaluation time
- Overrides: Later definitions override earlier ones
Property Precedence
When multiple sources define <TargetFrameworks>:
- Command-line (
-p:TargetFrameworks=...) - Highest precedence - Project file - Medium precedence
- Directory.Build.props - Lower precedence (but applies to all projects)
Conditional Framework Selection
You can create more complex conditions if needed:
<When Condition="'$(Configuration)' == 'Debug' And '$(SkipNetFramework)' != 'true'">
<PropertyGroup>
<TargetFrameworks>net472;net48;net481;net8.0-windows</TargetFrameworks>
</PropertyGroup>
</When>
Framework-Specific Code
When writing framework-specific code, use conditional compilation:
#if NET472 || NET48 || NET481
// .NET Framework specific code
#else
// Modern .NET specific code
#endif
Or use MSBuild conditions in project files:
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<Reference Include="LegacyAssembly" />
</ItemGroup>
Related Documentation
Summary
The centralized target framework management system provides:
- ✅ Consistent framework selection across all projects
- ✅ Easy maintenance through single-file configuration
- ✅ Flexible build scenarios via MSBuild properties
- ✅ Automatic inheritance for new projects
By following this guide, you can effectively manage target frameworks across the entire Krypton Standard Toolkit solution while maintaining consistency and reducing maintenance overhead.