Vue d’ensemble de la création de contrôles

L’extensibilité du modèle de contrôle Windows Presentation Foundation (WPF) réduit considérablement la nécessité de créer un contrôle. Vous pouvez toutefois être amené, dans certains cas, à créer un contrôle personnalisé. Cette rubrique décrit les fonctionnalités qui réduisent votre besoin de créer un contrôle personnalisé et les différents modèles de création de contrôles dans Windows Presentation Foundation (WPF). Cette rubrique montre également comment créer un contrôle.

Alternatives à l’écriture d’un nouveau contrôle

Par le passé, quand vous souhaitiez obtenir une expérience personnalisée à partir d’un contrôle existant, vous étiez limité à la modification des propriétés standard du contrôle (couleur d’arrière-plan, largeur de bordure, la taille de police, etc.). Si vous souhaitiez étendre l’apparence ou le comportement d’un contrôle au-delà de ces paramètres prédéfinis, vous deviez créer un contrôle, en procédant généralement par héritage d’un contrôle existant et par substitution de la méthode responsable du dessin du contrôle. Bien qu’il s’agit toujours d’une option, WPF vous permet de personnaliser des contrôles existants à l’aide de ses con mode tente l, styles, modèles et déclencheurs enrichis. La liste suivante donne des exemples d’utilisation de ces fonctionnalités pour créer des expériences personnalisées et cohérentes sans devoir créer un contrôle.

  • Contenu riche. La plupart des contrôles WPF standard prennent en charge du contenu enrichi. Par exemple, la propriété de contenu d’un Button type est de type Object, donc théoriquement tout peut être affiché sur un Button. Pour qu’un bouton affiche une image et un texte, vous pouvez ajouter une image et un TextBlock à une StackPanel et affecter la ContentStackPanel propriété. Étant donné que les contrôles peuvent afficher des éléments visuels WPF et des données arbitraires, il est moins nécessaire de créer un contrôle ou de modifier un contrôle existant pour prendre en charge une visualisation complexe. Pour plus d’informations sur le con mode tente l pour Button et d’autres con mode tente ls dans WPF, consultez le modèle de contenu WPF.

  • Styles. Il Style s’agit d’une collection de valeurs qui représentent les propriétés d’un contrôle. Grâce aux styles, vous pouvez créer une représentation réutilisable de l’apparence et du comportement souhaités d’un contrôle sans écrire un nouveau contrôle. Par exemple, supposons que vous souhaitez que tous vos TextBlock contrôles aient une police rouge, une police Arial avec une taille de police de 14. Vous pouvez créer un style comme ressource et définir les propriétés appropriées en conséquence. Ensuite, chaque TextBlock ajout à votre application aura la même apparence.

  • Modèles de données. A DataTemplate vous permet de personnaliser la façon dont les données sont affichées sur un contrôle. Par exemple, un DataTemplate peut être utilisé pour spécifier la façon dont les données sont affichées dans un ListBox. Pour obtenir un exemple, consultez Vue d’ensemble des modèles de données. Outre la personnalisation de l’apparence des données, vous DataTemplate pouvez inclure des éléments d’interface utilisateur, ce qui vous offre beaucoup de flexibilité dans les INTERFACES utilisateur personnalisées. Par exemple, à l’aide d’un DataTemplate, vous pouvez créer un ComboBox élément dans lequel chaque élément contient une zone de case activée.

  • Modèles de contrôle. De nombreux contrôles dans WPF utilisent un ControlTemplate pour définir la structure et l’apparence du contrôle, ce qui sépare l’apparence d’un contrôle des fonctionnalités du contrôle. Vous pouvez changer considérablement l’apparence d’un contrôle en redéfinissant son ControlTemplate. Imaginons par exemple que vous souhaitiez créer un contrôle ressemblant à un feu rouge. Ce contrôle a une interface utilisateur et des fonctionnalités simples. Il se compose de trois cercles, dont un seul peut être allumé à la fois. Après une réflexion, vous pouvez vous rendre compte qu’une RadioButton offre la fonctionnalité d’un seul étant sélectionné à la fois, mais l’apparence par défaut de l’apparence RadioButton ne ressemble pas aux lumières sur un feu d’arrêt. Étant donné que le RadioButton modèle de contrôle utilise un modèle de contrôle pour définir son apparence, il est facile de redéfinir les ControlTemplate exigences du contrôle et d’utiliser des cases d’option pour rendre votre lampe d’arrêt.

    Remarque

    Bien qu’un RadioButton peut utiliser un DataTemplate, il DataTemplate n’est pas suffisant dans cet exemple. Définit DataTemplate l’apparence du contenu d’un contrôle. Dans le cas d’un RadioButton, le contenu apparaît à droite du cercle qui indique si celui-ci RadioButton est sélectionné. Dans l’exemple du feu d’arrêt, la case d’option doit simplement être un cercle qui peut « allumer ». Étant donné que l’exigence d’apparence pour le feu d’arrêt est tellement différente de l’apparence par défaut du , il est nécessaire de RadioButtonredéfinir le ControlTemplate. En général, il DataTemplate est utilisé pour définir le contenu (ou les données) d’un contrôle, et il ControlTemplate est utilisé pour définir la structure d’un contrôle.

  • Déclencheurs. A Trigger vous permet de modifier dynamiquement l’apparence et le comportement d’un contrôle sans créer de contrôle. Par exemple, supposons que vous disposez de plusieurs ListBox contrôles dans votre application et que les éléments de chacun d’eux ListBox soient en gras et en rouge lorsqu’ils sont sélectionnés. Votre premier instinct peut être de créer une classe qui hérite et remplacer ListBox la OnSelectionChanged méthode pour modifier l’apparence de l’élément sélectionné, mais une meilleure approche consiste à ajouter un déclencheur à un style d’un ListBoxItem élément qui modifie l’apparence de l’élément sélectionné. Un déclencheur vous permet de changer les valeurs des propriétés ou de prendre des mesures sur la base de la valeur d’une propriété. Une EventTrigger option vous permet d’effectuer des actions lorsqu’un événement se produit.

Pour plus d’informations sur les styles, modèles et déclencheurs, consultez Application d’un style et création de modèles.

En général, si votre contrôle reflète les fonctionnalités d’un contrôle existant, mais que vous souhaitez qu’il ait un aspect différent, vous devez d’abord vous demander s’il est possible d’utiliser l’une des méthodes décrites dans cette section pour changer l’apparence du contrôle existant.

Modèles de création de contrôles

Le modèle de contenu riche, les styles, les modèles et les déclencheurs réduisent la nécessité de créer des contrôles. Toutefois, si vous devez créer un contrôle, il est important de comprendre les différents modèles de création de contrôle dans WPF. WPF fournit trois modèles généraux pour la création d’un contrôle, chacun d’eux fournissant un ensemble différent de fonctionnalités et de niveau de flexibilité. Les classes de base pour les trois modèles sont UserControl, Controlet FrameworkElement.

Dérivation de UserControl

La façon la plus simple de créer un contrôle dans WPF consiste à dériver de UserControl. Lorsque vous générez un contrôle qui hérite de UserControl, vous ajoutez des composants existants au UserControl, nommez les composants et référencez les gestionnaires d’événements en XAML. Vous pouvez ensuite référencer les éléments nommés et définir les gestionnaires d’événements dans le code. Ce modèle de développement est très similaire au modèle utilisé pour le développement d’applications dans WPF.

S’il est correctement généré, un UserControl peut tirer parti des avantages du contenu enrichi, des styles et des déclencheurs. Toutefois, si votre contrôle hérite, UserControlles personnes qui utilisent votre contrôle ne pourront pas utiliser une DataTemplate ou ControlTemplate personnaliser son apparence. Il est nécessaire de dériver de la Control classe ou de l’une de ses classes dérivées (autres que UserControl) pour créer un contrôle personnalisé qui prend en charge les modèles.

Avantages de la dérivation de UserControl

Envisagez de dériver si UserControl tous les éléments suivants s’appliquent :

  • Vous souhaitez générer votre contrôle de la même façon que vous créez une application.

  • Votre contrôle contient uniquement des composants existants.

  • Vous n’avez pas besoin de prendre en charge une personnalisation complexe.

Dérivation de Control

La dérivation de la Control classe est le modèle utilisé par la plupart des contrôles WPF existants. Lorsque vous créez un contrôle qui hérite de la Control classe, vous définissez son apparence à l’aide de modèles. En procédant ainsi, vous séparez la logique opérationnelle de la représentation visuelle. Vous pouvez également garantir le découplage de l’interface utilisateur et de la logique à l’aide de commandes et de liaisons au lieu d’événements et d’éviter de référencer des éléments dans la mesure du ControlTemplate possible. Si l’interface ControlTemplate utilisateur et la logique de votre contrôle sont correctement découplées, un utilisateur de votre contrôle peut redéfinir les contrôles pour personnaliser son apparence. Bien que la création d’un personnalisé Control ne soit pas aussi simple que la création d’un UserControl, une personnalisée Control offre la plus grande flexibilité.

Avantages de la dérivation de Control

Envisagez de dériver au Control lieu d’utiliser la UserControl classe si l’un des éléments suivants s’applique :

  • Vous souhaitez que l’apparence de votre contrôle soit personnalisable via le ControlTemplate.

  • Vous souhaitez que votre contrôle prenne en charge des thèmes différents.

Dérivation de FrameworkElement

Contrôles qui dérivent ou UserControlControl s’appuient sur la composition d’éléments existants. Pour de nombreux scénarios, il s’agit d’une solution acceptable, car tout objet qui hérite FrameworkElement peut se trouver dans un ControlTemplate. Dans certains cas, l’apparence d’un contrôle nécessite toutefois des fonctionnalités autres que celles de la composition d’éléments simples. Pour ces scénarios, la base d’un composant FrameworkElement est le bon choix.

Il existe deux méthodes standard pour la création de composants basés sur la création FrameworkElement: le rendu direct et la composition d’éléments personnalisés. Le rendu direct implique de remplacer la OnRender méthode et de FrameworkElement fournir des DrawingContext opérations qui définissent explicitement les visuels de composant. Il s’agit de la méthode utilisée par Image et Border. La composition d’élément personnalisé implique l’utilisation d’objets de type Visual pour composer l’apparence de votre composant. Pour obtenir un exemple, consultez Utilisation d’objets DrawingVisual. Track est un exemple de contrôle dans WPF qui utilise une composition d’élément personnalisée. Il est également possible de combiner le rendu direct et la composition d’éléments personnalisés au sein du même contrôle.

Avantages de la dérivation de FrameworkElement

Envisagez de dériver si FrameworkElement l’un des éléments suivants s’applique :

  • Vous souhaitez avoir un contrôle précis sur l’apparence de votre contrôle au-delà des fonctionnalités proposées par la composition d’éléments simples.

  • Vous souhaitez définir l’apparence de votre contrôle en définissant votre propre logique de rendu.

  • Vous voulez composer des éléments existants de nouvelles façons qui vont au-delà de ce qui est possible avec UserControl et Control.

Notions de base de la création de contrôles

Comme indiqué précédemment, l’une des fonctionnalités les plus puissantes de WPF est la possibilité d’aller au-delà de la définition des propriétés de base d’un contrôle pour modifier son apparence et son comportement, mais n’a pas encore besoin de créer un contrôle personnalisé. Les fonctionnalités de style, de liaison de données et de déclencheur sont rendues possibles par le système de propriétés WPF et le système d’événements WPF. Les sections suivantes décrivent certaines pratiques que vous devez suivre, quel que soit le modèle que vous utilisez pour créer le contrôle personnalisé, afin que les utilisateurs de votre contrôle personnalisé puissent utiliser ces fonctionnalités comme ils le feraient pour un contrôle inclus dans WPF.

Utiliser des propriétés de dépendance

Dans le cas d’une propriété de dépendance, il est possible d’effectuer les opérations suivantes :

  • Définir la propriété dans un style

  • Lier la propriété à une source de données

  • Utiliser une ressource dynamique comme valeur de la propriété

  • Animer la propriété

Si vous souhaitez qu’une propriété de votre contrôle prenne en charge l’une de ces fonctionnalités, vous devez l’implémenter comme propriété de dépendance. L’exemple suivant montre comment définir une propriété de dépendance appelée Value :

  • Définissez un DependencyProperty identificateur nommé ValueProperty en tant que publicstaticreadonly champ.

  • Inscrivez le nom de la propriété auprès du système de propriétés, en appelant DependencyProperty.Register, pour spécifier les éléments suivants :

    • Le nom de la propriété.

    • Type de la propriété.

    • Type qui possède la propriété.

    • Métadonnées de la propriété. Les métadonnées contiennent la valeur par défaut de la propriété, a CoerceValueCallback et a PropertyChangedCallback.

  • Définissez une propriété wrapper CLR nommée Value, qui est le même nom que celui utilisé pour inscrire la propriété de dépendance, en implémentant les accesseurs et set les accesseurs de get la propriété. Notez que les accesseurs et set les get accesseurs appellent GetValue uniquement et SetValue respectivement. Il est recommandé que les accesseurs des propriétés de dépendance ne contiennent pas de logique supplémentaire, car les clients et WPF peuvent contourner les accesseurs et appeler et SetValue appeler GetValue directement. Par exemple, quand une propriété est liée à une source de données, l’accesseur set de la propriété n’est pas appelé. Au lieu d’ajouter une logique supplémentaire aux accesseurs get et set, utilisez le ValidateValueCallback, CoerceValueCallbacket PropertyChangedCallback les délégués pour répondre ou case activée la valeur lorsqu’il change. Pour plus d’informations sur ces rappels, consultez Validation et rappels de propriétés de dépendance.

  • Définissez une méthode pour le CoerceValueCallback nom CoerceValue. CoerceValue garantit que Value est supérieur ou égal à MinValue et inférieur ou égal à MaxValue.

  • Définissez une méthode pour le PropertyChangedCallback, nommé OnValueChanged. OnValueChanged crée un RoutedPropertyChangedEventArgs<T> objet et se prépare à déclencher l’événement ValueChanged routé. Les événements routés sont traités dans la section suivante.

/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register(
        "Value", typeof(decimal), typeof(NumericUpDown),
        new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
                                      new CoerceValueCallback(CoerceValue)));

/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{
    get { return (decimal)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

private static object CoerceValue(DependencyObject element, object value)
{
    decimal newValue = (decimal)value;
    NumericUpDown control = (NumericUpDown)element;

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));

    return newValue;
}

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    NumericUpDown control = (NumericUpDown)obj;			

    RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>(
        (decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
    control.OnValueChanged(e);
}
''' <summary>
''' Identifies the Value dependency property.
''' </summary>
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Decimal), GetType(NumericUpDown), New FrameworkPropertyMetadata(MinValue, New PropertyChangedCallback(AddressOf OnValueChanged), New CoerceValueCallback(AddressOf CoerceValue)))

''' <summary>
''' Gets or sets the value assigned to the control.
''' </summary>
Public Property Value() As Decimal
    Get
        Return CDec(GetValue(ValueProperty))
    End Get
    Set(ByVal value As Decimal)
        SetValue(ValueProperty, value)
    End Set
End Property

Private Shared Overloads Function CoerceValue(ByVal element As DependencyObject, ByVal value As Object) As Object
    Dim newValue As Decimal = CDec(value)
    Dim control As NumericUpDown = CType(element, NumericUpDown)

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue))

    Return newValue
End Function

Private Shared Sub OnValueChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
    Dim control As NumericUpDown = CType(obj, NumericUpDown)

    Dim e As New RoutedPropertyChangedEventArgs(Of Decimal)(CDec(args.OldValue), CDec(args.NewValue), ValueChangedEvent)
    control.OnValueChanged(e)
End Sub

Pour plus d’informations, consultez Propriétés de dépendance personnalisées.

Utiliser des événements routés

Tout comme les propriétés de dépendance étendent la notion de propriétés CLR avec des fonctionnalités supplémentaires, les événements routés étendent la notion d’événements CLR standard. Lorsque vous créez un contrôle WPF, il est également recommandé d’implémenter votre événement en tant qu’événement routé, car un événement routé prend en charge le comportement suivant :

  • Vous pouvez gérer les événements sur le parent de plusieurs contrôles. Si un événement est un événement de propagation, un parent unique de l’arborescence d’éléments peut s’abonner à l’événement. Les auteurs d’applications peuvent ensuite utiliser un gestionnaire unique pour répondre à l’événement de plusieurs contrôles. Par exemple, si votre contrôle fait partie de chaque élément d’un ListBox (car il est inclus dans un DataTemplate), le développeur d’applications peut définir le gestionnaire d’événements pour l’événement de votre contrôle sur le ListBox. Chaque fois que l’événement se produit sur un des contrôles, le gestionnaire d’événements est appelé.

  • Les événements routés peuvent être utilisés dans un , ce qui permet aux développeurs d’applications EventSetterde spécifier le gestionnaire d’un événement dans un style.

  • Les événements routés peuvent être utilisés dans un EventTrigger, ce qui est utile pour animer des propriétés à l’aide de XAML. Pour plus d’informations, consultez Vue d’ensemble de l’animation.

L’exemple suivant montre comment définir un événement routé :

  • Définissez un RoutedEvent identificateur nommé ValueChangedEvent en tant que publicstaticreadonly champ.

  • Inscrivez l’événement routé en appelant la EventManager.RegisterRoutedEvent méthode. L’exemple spécifie les informations suivantes quand il appelle RegisterRoutedEvent:

    • Le nom de l’événement est ValueChanged.

    • La stratégie de routage est Bubble, ce qui signifie qu’un gestionnaire d’événements sur la source (l’objet qui déclenche l’événement) est appelé en premier, puis les gestionnaires d’événements sur les éléments parents de la source sont appelés en succession, en commençant par le gestionnaire d’événements sur l’élément parent le plus proche.

    • Le type du gestionnaire d’événements est RoutedPropertyChangedEventHandler<T>, construit avec un Decimal type.

    • Le type propriétaire de l’événement est NumericUpDown.

  • Déclarez un événement public nommé ValueChanged et incluez des déclarations d’accesseurs d’événement. L’exemple appelle AddHandler dans la déclaration d’accesseur add et RemoveHandler dans la déclaration d’accesseur remove pour utiliser les services d’événements WPF.

  • Créez une méthode virtuelle protégée nommée OnValueChanged, qui déclenche l’événement ValueChanged.

/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
    "ValueChanged", RoutingStrategy.Bubble,
    typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown));

/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
{
    add { AddHandler(ValueChangedEvent, value); }
    remove { RemoveHandler(ValueChangedEvent, value); }
}

/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
{
    RaiseEvent(args);
}
''' <summary>
''' Identifies the ValueChanged routed event.
''' </summary>
Public Shared ReadOnly ValueChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, GetType(RoutedPropertyChangedEventHandler(Of Decimal)), GetType(NumericUpDown))

''' <summary>
''' Occurs when the Value property changes.
''' </summary>
Public Custom Event ValueChanged As RoutedPropertyChangedEventHandler(Of Decimal)
    AddHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.AddHandler(ValueChangedEvent, value)
    End AddHandler
    RemoveHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.RemoveHandler(ValueChangedEvent, value)
    End RemoveHandler
    RaiseEvent(ByVal sender As System.Object, ByVal e As RoutedPropertyChangedEventArgs(Of Decimal))
    End RaiseEvent
End Event

''' <summary>
''' Raises the ValueChanged event.
''' </summary>
''' <param name="args">Arguments associated with the ValueChanged event.</param>
Protected Overridable Sub OnValueChanged(ByVal args As RoutedPropertyChangedEventArgs(Of Decimal))
    MyBase.RaiseEvent(args)
End Sub

Pour plus d’informations, consultez Vue d’ensemble des événements routés et Créer un événement routé personnalisé.

Utiliser la liaison

Pour découpler l’interface utilisateur de votre contrôle de sa logique, songez à utiliser la liaison de données. Cela est particulièrement important si vous définissez l’apparence de votre contrôle à l’aide d’un ControlTemplate. L’utilisation de la liaison de données permet d’éviter de référencer des parties spécifiques de l’interface utilisateur à partir du code. Il est judicieux d’éviter de référencer des éléments qui se trouvent dans le ControlTemplate code, car lorsque le code fait référence à des éléments qui se trouvent dans le ControlTemplate code et que celui-ci ControlTemplate est modifié, l’élément référencé doit être inclus dans le nouveau ControlTemplate.

L’exemple suivant met à jour le TextBlockNumericUpDown contrôle, lui affectant un nom et référençant la zone de texte par nom dans le code.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
  <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
private void UpdateTextBlock()
{
    valueText.Text = Value.ToString();
}
Private Sub UpdateTextBlock()
    valueText.Text = Value.ToString()
End Sub

L’exemple suivant utilise la liaison pour accomplir la même chose.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">

    <!--Bind the TextBlock to the Value property-->
    <TextBlock 
        Width="60" TextAlignment="Right" Padding="5"
        Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type local:NumericUpDown}}, 
                       Path=Value}"/>

</Border>

Pour plus d’informations sur la liaison de données, consultez Vue d’ensemble de la liaison de données.

Conception pour les concepteurs

Pour recevoir la prise en charge des contrôles WPF personnalisés dans le Concepteur WPF pour Visual Studio (par exemple, la modification de propriété avec le Fenêtre Propriétés), suivez ces instructions. Pour plus d’informations sur le développement pour le concepteur WPF, consultez Design XAML dans Visual Studio.

Propriétés de dépendance

Veillez à implémenter clR get et set accesseurs comme décrit précédemment, dans « Utiliser les propriétés de dépendance ». Les concepteurs peuvent utiliser le wrapper pour détecter la présence d’une propriété de dépendance, mais ils, comme WPF et les clients du contrôle, ne sont pas nécessaires pour appeler les accesseurs lors de l’obtention ou de la définition de la propriété.

Propriétés attachées

Pour implémenter des propriétés jointes sur des contrôles personnalisés, tenez compte des indications suivantes :

  • Avoir un publicreadonlyDependencyPropertystaticformulaire PropertyNameProperty qui a été créé à l’aide de la RegisterAttached méthode. Le nom de propriété passé doit correspondre à RegisterAttached PropertyName.

  • Implémentez une paire de méthodes CLR publicstatic nommées SetNomPropriété et GetNomPropriété. Les deux méthodes doivent accepter une classe dérivée de DependencyProperty leur premier argument. La méthode SetNomPropriété accepte également un argument dont le type correspond au type de données inscrit pour la propriété. La méthode GetNomPropriété doit retourner une valeur du même type. Si la méthode SetNomPropriété est manquante, la propriété est marquée en lecture seule.

  • SetPropertyName et GetPropertyName doivent acheminer directement vers les GetValue méthodes de SetValue l’objet de dépendance cible, respectivement. Les concepteurs peuvent accéder à la propriété jointe en l’appelant par le biais du wrapper de méthode ou en effectuant un appel direct à l’objet de dépendance cible.

Pour plus d’informations sur les propriétés jointes, consultez Vue d’ensemble des propriétés jointes.

Définir et utiliser des ressources partagées

Vous pouvez inclure votre contrôle dans le même assembly que votre application, ou la mettre en package dans un assembly séparé qui peut être utilisé dans plusieurs applications. Pour la plupart, les informations abordées dans cette rubrique sont valables, quelle que soit la méthode que vous utilisez. Toutefois, il existe une différence notable. Quand vous placez un contrôle dans le même assembly qu’une application, vous êtes libre d’ajouter des ressources globales au fichier App.xaml. Toutefois, un assembly qui contient uniquement des contrôles n’a pas d’objet Application associé à celui-ci. Par conséquent, un fichier App.xaml n’est pas disponible.

Quand une application recherche une ressource, elle examine trois niveaux dans l’ordre suivant :

  1. Niveau de l’élément.

    Le système commence par l’élément qui référence la ressource, puis passe en revue les ressources du parent logique, et ainsi de suite, jusqu’à atteindre l’élément racine.

  2. Niveau de l’application.

    Ressources définies par l’objet Application .

  3. Niveau du thème.

    Les dictionnaires au niveau du thème sont enregistrés dans un sous-dossier nommé Themes. Les fichiers contenus dans ce dossier correspondent aux thèmes. Par exemple, vous pouvez avoir Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, etc. Vous pouvez également avoir un fichier nommé generic.xaml. Quand le système recherche une ressource au niveau des thèmes, il la cherche en premier lieu dans le fichier spécifique au thème, puis dans generic.xaml.

Si votre contrôle se trouve dans un assembly distinct de l’application, vous devez placer vos ressources globales au niveau de l’élément ou du thème. Les deux méthodes ont leurs avantages.

Définition de ressources au niveau de l’élément

Vous pouvez définir des ressources partagées au niveau de l’élément en créant un dictionnaire de ressources personnalisé et en le fusionnant avec le dictionnaire de ressources de votre contrôle. Quand vous utilisez cette méthode, vous pouvez donner à votre fichier de ressources le nom de votre choix, et il peut se trouver dans le même dossier que vos contrôles. Les ressources au niveau de l’élément peuvent également utiliser des chaînes simples comme clés. L’exemple suivant crée un LinearGradientBrush fichier de ressources nommé Dictionary1.xaml.

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <LinearGradientBrush 
    x:Key="myBrush"  
    StartPoint="0,0" EndPoint="1,1">
    <GradientStop Color="Red" Offset="0.25" />
    <GradientStop Color="Blue" Offset="0.75" />
  </LinearGradientBrush>
  
</ResourceDictionary>

Une fois que vous avez défini votre dictionnaire, vous devez le fusionner avec le dictionnaire de ressources de votre contrôle. Pour ce faire, utilisez XAML ou du code.

L’exemple suivant fusionne un dictionnaire de ressources à l’aide de XAML.

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

L’inconvénient de cette approche est qu’un ResourceDictionary objet est créé chaque fois que vous le référencez. Par exemple, si vous avez 10 contrôles personnalisés dans votre bibliothèque et que vous fusionnez les dictionnaires de ressources partagées pour chaque contrôle à l’aide de XAML, vous créez 10 objets identiques ResourceDictionary . Vous pouvez éviter cela en créant une classe statique qui fusionne les ressources dans le code et retourne le résultat ResourceDictionary.

L’exemple suivant crée une classe qui retourne un partage ResourceDictionary.

internal static class SharedDictionaryManager
{
    internal static ResourceDictionary SharedDictionary
    {
        get
        {
            if (_sharedDictionary == null)
            {
                System.Uri resourceLocater =
                    new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml",
                                    System.UriKind.Relative);

                _sharedDictionary =
                    (ResourceDictionary)Application.LoadComponent(resourceLocater);
            }

            return _sharedDictionary;
        }
    }

    private static ResourceDictionary _sharedDictionary;
}

L’exemple suivant fusionne la ressource partagée avec les ressources d’un contrôle personnalisé dans le constructeur du contrôle avant d’appeler InitializeComponent. Comme il SharedDictionaryManager.SharedDictionary s’agit d’une propriété statique, celle-ci ResourceDictionary n’est créée qu’une seule fois. Étant donné que le dictionnaire de ressources a été fusionné avant InitializeComponent d’être appelé, les ressources sont disponibles pour le contrôle dans son fichier XAML.

public NumericUpDown()
{
    this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
    InitializeComponent();
}

Définition de ressources au niveau de l’élément

WPF vous permet de créer des ressources pour différents thèmes Windows. En tant qu’auteur du contrôle, vous pouvez définir une ressource pour un thème spécifique pour changer l’apparence de votre contrôle selon le thème utilisé. Par exemple, l’apparence d’un Button thème Windows Classic (le thème par défaut pour Windows 2000) diffère d’un Button thème Windows Luna (le thème par défaut pour Windows XP), car il Button utilise un thème différent ControlTemplate pour chaque thème.

Les ressources spécifiques à un thème sont conservées dans un dictionnaire de ressources avec un nom de fichier spécifique. Ces fichiers doivent se trouver dans un dossier nommé Themes, qui est un sous-dossier du dossier qui contient le contrôle. Le tableau suivant répertorie les fichiers des dictionnaires de ressources et le thème associé à chaque fichier :

Nom de fichier du dictionnaire de ressources Thème Windows
Classic.xaml Aspect Windows 9x/2000 classique sur Windows XP
Luna.NormalColor.xaml Thème bleu par défaut sur Windows XP
Luna.Homestead.xaml Thème vert olive sur Windows XP
Luna.Metallic.xaml Thème argent sur Windows XP
Royale.NormalColor.xaml Thème par défaut sur Windows XP Édition Media Center
Aero.NormalColor.xaml Thème par défaut sur Windows Vista

Il n’est pas nécessaire de définir une ressource pour chaque thème. Si une ressource n’est pas définie pour un thème spécifique, le contrôle vérifie Classic.xaml pour la ressource. Si la ressource n’est pas définie dans le fichier correspondant au thème actif ou dans Classic.xaml, le contrôle utilise la ressource générique, qui se trouve dans un fichier de dictionnaire de ressources nommé generic.xaml. Le fichier generic.xaml se trouve dans le même dossier que les fichiers de dictionnaire de ressources spécifiques au thème. Bien que generic.xaml ne corresponde pas à un thème spécifique de Windows, il s’agit néanmoins d’un dictionnaire au niveau du thème.

Le contrôle personnalisé C# ou Visual Basic NumericUpDown avec l’exemple de prise en charge du thème et de l’automatisation de l’interface utilisateur contient deux dictionnaires de ressources pour le NumericUpDown contrôle : l’un est en generic.xaml et l’autre dans Luna.NormalColor.xaml.

Lorsque vous placez un ControlTemplate fichier de dictionnaire de ressources spécifique au thème, vous devez créer un constructeur statique pour votre contrôle et appeler la OverrideMetadata(Type, PropertyMetadata) méthode sur le DefaultStyleKey, comme illustré dans l’exemple suivant.

static NumericUpDown()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
               new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
    DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Définition et référencement de clés pour les ressources de thème

Quand vous définissez une ressource au niveau de l’élément, vous pouvez assigner une chaîne comme clé et accéder à la ressource par le biais de la chaîne. Lorsque vous définissez une ressource au niveau du thème, vous devez utiliser une ComponentResourceKey clé. L’exemple suivant définit une ressource dans generic.xaml.

<LinearGradientBrush 
     x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter}, 
                                  ResourceId=MyEllipseBrush}"  
                                  StartPoint="0,0" EndPoint="1,0">
    <GradientStop Color="Blue" Offset="0" />
    <GradientStop Color="Red" Offset="0.5" />
    <GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>

L’exemple suivant référence la ressource en spécifiant la ComponentResourceKey clé.

<RepeatButton 
    Grid.Column="1" Grid.Row="0"
    Background="{StaticResource {ComponentResourceKey 
                        TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                        ResourceId=ButtonBrush}}">
    Up
</RepeatButton>
<RepeatButton 
    Grid.Column="1" Grid.Row="1"
    Background="{StaticResource {ComponentResourceKey 
                    TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                    ResourceId=ButtonBrush}}">
    Down
 </RepeatButton>
Spécification de l’emplacement des ressources des thèmes

Pour rechercher les ressources relatives à un contrôle, l’application d’hébergement doit savoir quel l’assembly contient des ressources spécifiques au contrôle. Pour ce faire, ajoutez l’assembly ThemeInfoAttribute qui contient le contrôle. Il ThemeInfoAttribute a une GenericDictionaryLocation propriété qui spécifie l’emplacement des ressources génériques et une ThemeDictionaryLocation propriété qui spécifie l’emplacement des ressources spécifiques au thème.

L’exemple suivant définit les propriétés et ThemeDictionaryLocation les GenericDictionaryLocation propriétés SourceAssemblysur , pour spécifier que les ressources génériques et spécifiques au thème se trouvent dans le même assembly que le contrôle.

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
           ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>

Voir aussi