Lors du développement de contrôles personnalisés en WPF, il est courant de définir un style par défaut afin que le contrôle présente une apparence cohérente sans nécessiter de configuration explicite par l'utilisateur. Traditionnellement, cela est accompli en plaçant un fichier Generic.xaml dans un dossier Themes à la racine du projet de bibliothèque de contrôles. Ce fichier contient le Style principal dont la propriété TargetType pointe vers votre contrôle personnalisé.
Dans le constructeur statique de votre contrôle personnalisé, vous devez enregistrer le type de votre contrôle auprès du système de propriétés WPF. Ceci est réalisé en utilisant DefaultStyleKeyProperty.OverrideMetadata, en spécifiant le type de votre contrôle et en lui associant un FrameworkPropertyMetadata qui pointe vers le type du contrôle lui-même comme clé de style par défaut.
// Dans le constructeur statique de votre contrôle personnalisé
public MonControlePersonnalise()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MonControlePersonnalise),
new FrameworkPropertyMetadata(typeof(MonControlePersonnalise)));
}
Dans le fichier Generic.xaml, le style serait défini comme suit :
<Style TargetType="{x:Type MonControlePersonnalise}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MonControlePersonnalise}">
<!-- Définition du template du contrôle ici -->
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<TextBlock Text="Mon Contrôle Personnalisé" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Ce mécanisme garantit que lorsque votre contrôle est instancié sans style appliqué explicitement, WPF recherche automatiquement le style par défaut dans le fichier Generic.xaml associé à l'assembly du contrôle.
Problèmes rencontrés avec .NET 8 et les projets de bibliothèque
Lors de la mise à niveau d'un projet existant ou de la création d'un nouveau projet pour cibler .NET 8, les développeurs peuvent rencontrer des difficultés avec l'application automatique des styles par défaut. Si un projet de bibliothèque de classes standard est créé au lieu d'un projet de "Bibliothèque de contrôles utilisateur WPF" (WPF User Control Libray), l'attribut [assembly: ThemeInfo] peut manquer.
Cet attribut est crucial car il indique à WPF où rechercher les ressources de thème. Dans un projet .NET Framework, cet attribut était souvent généré automatiquement dans un fichier AssemblyInfo.cs. Dans les projets .NET plus récents utilisant le style SDK, la génération de ce fichier peut être omise par défaut pour les projets de bibliothèque de classes standards.
Solutions pour rétablir la configuration de ThemeInfo
Pour résoudre ce problème et permettre à WPF de trouver votre Generic.xaml, vous avez deux options principales :
1. Créer un fichier AssemblyInfo.cs
La première approche consiste à recréer manuellement le fichier AssemblyInfo.cs à la racine de votre projet et à y ajouter l'attribut ThemeInfo :
using System.Windows;
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
Ici, ResourceDictionaryLocation.None indique qu'il n'y a pas de dictionnaires de ressources spécifiques au thème dans l'application elle-même, tandis que ResourceDictionaryLocation.SourceAssembly spécifie que les ressources par défaut (comme Generic.xaml) se trouvent dans l'assembly source, typiquement dans le dossier Themes.
2. Configurer via le fichier projet (.csproj) (Recommandé)
Une méthode plus moderne et recommandée dans les projets de style SDK est de spécifier les attributs d'assembly directement dans le fichier .csproj. Cela évite d'avoir un fichier AssemblyInfo.cs séparé.
Ajoutez le bloc ItemGroup suivant à votre fichier .csproj :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Windows.ThemeInfoAttribute">
<_Parameter1>System.Windows.ResourceDictionaryLocation.None</_Parameter1>
<_Parameter2>System.Windows.ResourceDictionaryLocation.SourceAssembly</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
</Project>
Cette configuration indique explicitement à MSBuild d'inclure l'attribut ThemeInfo lors de la compilation de l'assembly, diriegant ainsi WPF vers l'emplacement correct des ressources de style par défaut.