Dépannage des échecs de chargement du concepteur WPF
Mise à jour : novembre 2007
Le Concepteur Windows Presentation Foundation (WPF) pour Visual Studio intègre un concepteur visuel sophistiqué et extensible qui restitue le code XAML. Si votre fichier XAML ne se charge pas dans le concepteur, vous pouvez effectuer plusieurs actions permettant de déterminer le problème. Cette rubrique fournit quelques conseils et techniques permettant de résoudre les problèmes de chargement du Concepteur WPF.
Remarque : |
---|
De nombreuses techniques présentées dans cette rubrique s'appliquent également à Expression Blend. |
Étapes de dépannage
Les étapes suivantes permettent de résoudre les problèmes de chargement du Concepteur WPF.
Lisez tous les messages d'exception qui s'affichent.
Cette étape peut paraître évidente, mais si vous recevez une exception, lisez clairement le message. Dans certains cas, il permet de diagnostiquer rapidement le problème. Pour plus d'informations, consultez Débogage et interprétation d'erreurs dans le concepteur WPF.
Déterminez si le problème est lié à votre implémentation.
Générez et exécutez votre application pour déterminer si le problème est uniquement le résultat de votre implémentation ou une interaction avec le Concepteur WPF. Si l'application se génère et s'exécute, l'erreur au moment du design est vraisemblablement provoquée par votre implémentation.
Utilisez le débogueur Visual Studio pour effectuer un pas à pas détaillé dans votre code au moment du design. Pour plus d'informations, consultez Procédure pas à pas : débogage des contrôles personnalisés WPF au moment du design.
Déterminez si le problème est une erreur de chargement.
En cas d'échec du chargement du mode Design suite à une exception, le problème est probablement une erreur de chargement. Si vous disposez de code personnalisé chargé au moment du design et que vous rencontrez des exceptions ou des échecs de chargement au moment du design, consultez la section Écriture de code pour le moment de design dans cette rubrique. Si vous utilisez des ressources qui ne se chargent pas, consultez la section Ressources UserControl et de contrôle personnalisé au moment du design dans cette rubrique.
Examinez le code chargé au moment du design.
Il existe deux approches liées à l'écriture de code qui s'exécute également au moment du design. La première approche consiste à écrire du code défensif en vérifiant les paramètres d'entrée pour les classes. La seconde approche consiste à vérifier si le mode Design est actif en appelant la méthode GetIsInDesignMode. Pour plus d'informations, consultez la section Écriture de code pour le moment de design dans cette rubrique.
Examinez les autres zones de votre code.
Consultez la section Conseils de programmation de cette rubrique pour obtenir des conseils de programmation lorsque vous utilisez Concepteur WPF. Consultez la section Meilleures pratiques de programmation de cette rubrique pour connaître les techniques permettant d'écrire du code plus fiable.
Si les problèmes persistent, vous pouvez utiliser le forum de WPF Designer sur MSDN pour communiquer avec d'autres développeurs qui utilisent Concepteur WPF. Pour signaler des problèmes potentiels ou envoyer des suggestions, utilisez le site Commentaires sur Visual Studio et le .NET Framework.
Écriture de code pour le moment de design
Assurez-vous que votre code s'exécute au moment du design et au moment de l'exécution. Si votre code s'exécute au moment du design, ne supposez pas que Application.Current correspond à votre application. Par exemple, lorsque vous utilisez Expression Blend, Current correspond à Expression Blend. Au moment du design, MainWindow n'est pas la fenêtre principale de votre application. Les opérations courantes qui provoquent l'échec d'un contrôle personnalisé au moment du design sont, entre autres, les suivantes.
Casting de Current en sous-classe personnalisée de Application.
Casting de MainWindow en sous-classe personnalisée de Window.
Utilisation de la méthode FindResource ou FindName sur Current ou MainWindow.
Référencement de ressources à partir de Application.Resources. L'instance au moment du design de Application diffère de votre application et ne comporte pas les mêmes ressources.
Absence de vérification du retour de la valeur null par Application.Current ou Application.MainWindow. Si Visual Studio ne crée aucun objet d'application, Application.Current peut retourner la valeur null.
Absence de vérification du retour de la valeur null par Assembly.GetEntryAssembly. Pour Visual Studio, cette méthode retourne la valeur null.
Il existe deux approches liées à l'écriture de code pour le moment du design. La première approche consiste à écrire du code défensif en vérifiant les paramètres d'entrée pour les classes (convertisseurs de valeur, par exemple). La seconde approche consiste à vérifier si le mode Design est actif en appelant la méthode GetIsInDesignMode.
Il est nécessaire de vérifier les paramètres d'entrée de certaines implémentations, car l'environnement de design fournit, pour certaines entrées, d'autres types que ceux fournis par l'environnement d'exécution.
En règle générale, les sélecteurs de style et les convertisseurs de valeur nécessitent l'une de ces approches pour pouvoir s'exécuter correctement au moment du design.
Convertisseurs de valeur
Vos implémentations IValueConverter personnalisées doivent vérifier la présence de la valeur null et du type attendu dans le premier paramètre de la méthode Convert. Le code XAML suivant montre une liaison vers Application.Current qui échoue au moment du design si le convertisseur de valeur n'est pas correctement implémenté.
<ComboBox.IsEnabled>
<MultiBinding Converter="{StaticResource specialFeaturesConverter}">
<Binding Path="CurrentUser.Rating" Source="{x:Static Application.Current}"/>
<Binding Path="CurrentUser.MemberSince" Source="{x:Static Application.Current}"/>
</MultiBinding>
</ComboBox.IsEnabled>
La liaison lève une exception au moment du design, car Application.Current fait référence à l'application de concepteur au lieu de votre application. Pour éviter l'exception, le convertisseur de valeur doit vérifier ses paramètres d'entrée ou vérifier le mode Design.
L'exemple de code suivant indique comment vérifier des paramètres d'entrée dans un convertisseur de valeur qui retourne la valeur true si deux paramètres d'entrée répondent à une logique métier particulière.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Check the values array for correct parameters.
// Designers may send null or unexpected values.
if (values == null || values.Length < 2) return false;
if (!(values[0] is int)) return false;
if (!(values[1] is DateTime)) return false;
int rating = (int)values[0];
DateTime date = (DateTime)values[1];
// If the user has a good rating (10+) and has been a member for
// more than a year, special features are available.
if((rating >= 10) &&
(date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
{
return true;
}
return false;
}
La seconde approche à suivre pour écrire du code pour le moment du design consiste à vérifier si le mode Design est actif. L'exemple de code suivant affiche une vérification du mode Design au lieu de la vérification de paramètres illustrée précédemment.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Check for design mode.
if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue))
{
return false;
}
int rating = (int)values[0];
DateTime date = (DateTime)values[1];
// If the user has a good rating (10+) and has been a member for
// more than a year, special features are available.
if((rating >= 10) &&
(date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
{
return true;
}
return false;
}
Sélecteurs de style
Vos sélecteurs de style personnalisés doivent également être implémentés pour s'exécuter en mode Design. Le code XAML suivant affiche un sélecteur de modèle personnalisé qui utilise Application.MainWindow au moment de l'exécution afin de déterminer la ressource retournée en tant que DataTemplate. Au moment du design, cette ressource risque de ne pas être disponible ; dès lors, la substitution SelectTemplate retourne null au moment du design.
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"
IsSynchronizedWithCurrentItem="True"/>
Le code suivant montre l'implémentation du sélecteur de style.
public class TaskListDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(
object item,
DependencyObject container)
{
if (item != null && item is Task)
{
Task taskitem = item as Task;
Window window = Application.Current.MainWindow;
// To run in design mode, either test for the correct window class
// or test for design mode.
if (window.GetType() == typeof(Window1))
// Or check for design mode:
//if (!DesignerProperties.GetIsInDesignMode(window))
{
if (taskitem.Priority == 1)
return window.FindResource("importantTaskTemplate") as DataTemplate;
else
return window.FindResource("myTaskTemplate") as DataTemplate;
}
}
return null;
}
}
Ressources UserControl et de contrôle personnalisé au moment du design
Par défaut, les ressources UserControl et de contrôle personnalisé qui sont disponibles au moment de l'exécution peuvent ne pas être disponibles au moment du design. Lorsque vous ajoutez un contrôle personnalisé ou un contrôle utilisateur à Page ou à Window sur l'aire de conception, une instance du contrôle est créée. Les ressources de App.xaml ne sont pas disponibles pour UserControl et les instances de contrôle personnalisé chargées sur une page ou une fenêtre.
Pour que vos ressources soient disponibles au moment du design, factorisez-les dans un dictionnaire de ressources distinct et incluez le dictionnaire dans App.xaml et le code XAML du contrôle. Modifiez toutes les références StaticResource en références DynamicResource. L'exemple de code suivant indique comment partager un dictionnaire de ressources afin que ses ressources soient disponibles au moment du design.
UserControl1.xaml
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Syntaxe URI à en-tête pack
Il n'est pas recommandé d'utiliser des références de ressource relatives à l'application. L'exemple de code suivant affiche la syntaxe basée sur l'application, qui peut échouer au moment du design.
<Image Name="image1" Source="pack://application:,,,/Image1.bmp" />
Ces références sont relatives à l'application, plutôt qu'à la DLL. L'utilisation d'une référence de ressource relative à l'application dans une DLL rend cette dernière dépendante des ressources de l'application parente. Cette approche est peu fiable et son fonctionnement n'est pas garanti au moment du design.
Au lieu d'utiliser des références de ressource relatives à l'application, ajoutez des ressources à la DLL et utilisez des références de ressource à base de composants. Pour plus d'informations, consultez URI à en-tête pack dans Windows Presentation Foundation.
Les exemples de code suivants indiquent la syntaxe à base de composants, qui constitue l'approche recommandée.
<Image Name="image1" Source="/TestHostApp;component/Image1.bmp" />
<Image Name="image1" Source="pack://application:,,,/TestHostApp;component/Image1
Conseils de programmation
Vous trouverez ci-dessous quelques conseils de programmation à suivre lorsque vous utilisez le Concepteur WPF.
Si vous souhaitez charger votre contrôle personnalisé dans le Concepteur WPF, vous devez fournir les méthodes CLR Get et Set pour toutes les propriétés de dépendance définies. Pour plus d'informations, consultez Propriétés de dépendance personnalisées.
Les ornements de type ComboBox ne sont pas pris en charge.
Pour utiliser un contrôle Windows Forms tiers, créez un type UserControl dont la collection Controls contient une instance du contrôle de fournisseur. Pour plus d'informations, consultez Procédure pas à pas : hébergement d'un contrôle Windows Forms tiers dans une application WPF.
Le moment du design n'est pas directement pris en charge pour FlowDocument. Si vous souhaitez utiliser le Concepteur WPF dans un FlowDocument incorporé, placez d'abord FlowDocument dans un contrôle Frame, que vous pouvez ensuite utiliser dans le Concepteur WPF.
Meilleures pratiques de programmation
Vous trouverez ci-dessous quelques-unes des meilleures pratiques de programmation permettant d'écrire du code plus fiable pour le Concepteur WPF.
Encapsulez toujours les portées d'édition dans des instructions using ou dans des blocs try/finally. Si une exception est levée, la modification est abandonnée dans l'appel Dispose. Pour plus d'informations, consultez ModelEditingScope.
Utilisez ModelEditingScope pour déplacer un contrôle d'un conteneur à un autre. Une exception est levée en cas d'échec du déplacement.
Dans WPF et le Concepteur WPF, n'utilisez pas de valeur de propriété par défaut si vous avez l'intention de l'effacer. Pour les valeurs NaN, comme Height, appelez la méthode ClearValue au lieu d'assigner NaN.
Lorsque vous récupérez des valeurs d'une propriété, utilisez la valeur calculée de la propriété. Vous devez dès lors utiliser la propriété ComputedValue au lieu de la méthode GetCurrentValue de ModelItem. La méthode GetCurrentValue retourne des liaisons et d'autres expressions si elles sont stockées dans le code XAML. Dans certains cas, vous pouvez dès lors obtenir des exceptions de cast.
Voir aussi
Autres ressources
Débogage et interprétation d'erreurs dans le concepteur WPF
Procédures pas à pas pour XAML et le code