|
Cet article a fait l'objet d'une traduction automatique. Déplacez votre pointeur sur les phrases de l'article pour voir la version originale de ce texte. Informations supplémentaires.
|
Traduction
Source
|
Vue d'ensemble de la création de contrôles
Cette rubrique comprend les sections suivantes.
-
Contenu riche. De nombreux contrôles WPF standard prennent en charge le contenu riche. Par exemple, la propriété de contenu d'un Button est de type Object, de sorte que, théoriquement, il est possible de tout afficher sur un Button. Si vous voulez qu'un bouton affiche une image et un texte, vous pouvez ajouter une image et un TextBlock à un StackPanel et assigner le StackPanel à la propriété Content. Comme les contrôles peuvent afficher des éléments visuels WPF et des données arbitraires, il est moins souvent 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 modèle de contenu de Button et d'autres modèles de contenu dans WPF, consultez Modèle de contenu WPF. -
Styles. Un Style est une collection de valeurs qui représentent les propriétés d'un contrôle. L'utilisation de styles vous permet de 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, supposez que vous voulez que tous vos contrôles TextBlock aient une police rouge, Arial, avec une taille de police de 14. Vous pouvez créer un style en tant que ressource et définir les propriétés appropriées en conséquence. Chaque TextBlock que vous ajoutez à votre application a alors la même apparence. -
Modèles de données. Un DataTemplate permet de personnaliser l'affichage des données sur un contrôle. Vous pouvez par exemple utiliser un DataTemplate pour définir l'affichage des données dans une ListBox. Pour obtenir un exemple, consultez Vue d'ensemble des modèles de données. En plus de personnaliser l'apparence des données, un DataTemplate peut inclure des éléments d'interface, ce qui vous offre une grande flexibilité dans les interfaces utilisateur personnalisées. Par exemple, vous pouvez créer une ComboBox dans laquelle chaque élément contient une case à cocher en utilisant un DataTemplate. -
Modèles de contrôle. De nombreux contrôles dans WPF utilisent un ControlTemplate pour définir la structure et l'apparence de contrôle, qui sépare l'apparence d'un contrôle de ses fonctionnalités. Vous pouvez totalement modifier 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 possède une interface utilisateur et des fonctionnalités simples. Il est constitué de trois cercles, dont un seul peut être allumé à la fois. Après quelques instants de réflexion, vous réalisez qu'une RadioButton offre la possibilité d'effectuer une sélection unique. L'apparence par défaut de la RadioButton ne ressemble toutefois en rien aux voyants d'un feu rouge. Comme la RadioButton utilise un modèle de contrôle pour définir son apparence, il est facile de redéfinir ce ControlTemplate pour répondre aux exigences du contrôle et utiliser des cases d'option pour créer votre feu rouge.
Remarque
Bien qu'une RadioButton puisse utiliser un DataTemplate, ce dernier ne suffit pas dans cet exemple. Le DataTemplate définit l'apparence du contenu d'un contrôle. Dans le cas d'une RadioButton, le contenu est l'élément qui apparaît à droite du cercle qui indique si la RadioButton est sélectionnée. Dans l'exemple du feu rouge, la case d'option doit juste être un cercle capable de "s'allumer". Comme l'apparence du feu rouge est très différente de l'apparence par défaut de la RadioButton, il est nécessaire de redéfinir le ControlTemplate. En général, un DataTemplate est utilisé pour définir le contenu (ou les données) d'un contrôle et un ControlTemplate pour définir sa structure. -
Déclencheurs. Un Trigger vous permet de modifier dynamiquement l'apparence et le comportement d'un contrôle sans créer de contrôle. Par exemple, imaginons vous ayez plusieurs contrôles ListBox dans votre application et souhaitiez que les éléments de chaque ListBox apparaissent en caractères gras et rouges lorsqu'ils sont sélectionnés. Votre premier réflexe vous dicte de créer une classe qui hérite de ListBox et substitue la méthode OnSelectionChanged pour modifier l'apparence de l'élément sélectionné. La solution idéale consiste toutefois à ajouter un déclencheur à un style de ListBoxItem qui modifie l'apparence de l'élément sélectionné. Un déclencheur vous permet de modifier les valeurs des propriétés ou de prendre des mesures sur la base de la valeur d'une propriété. Un EventTrigger vous permet de prendre des mesures lorsqu'un événement se produit.
Dériver de UserControl
Avantages de dériver de UserControl
-
Vous voulez créer votre contrôle de la même manière que vous générez une application. -
Votre contrôle contient uniquement des composants existants. -
Vous n'avez pas besoin de prendre en charge une personnalisation complexe.
Dériver de Control
Avantages de dériver de Control
-
Vous voulez pouvoir personnaliser l'apparence de votre contrôle via le ControlTemplate. -
Vous souhaitez que votre contrôle prenne en charge des thèmes différents.
Dériver de FrameworkElement
Avantages de dériver de FrameworkElement
-
Vous souhaitez avoir un contrôle précis sur l'apparence de votre contrôle allant 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 souhaitez composer des éléments existants de manière innovante au delà de ce qui est possible avec UserControl et Control.
Utiliser des propriétés de dépendance
-
Définir la propriété dans un style. -
Lier la propriété à une source de données. -
Utiliser une ressource dynamique en tant que valeur de la propriété. -
Animer la propriété.
-
Définissez un identificateur DependencyProperty nommé ValueProperty en tant que champ public static readonly. -
Enregistre le nom de la propriété avec le système de propriétés, en appelant DependencyProperty.Register, afin de spécifier les éléments suivants : -
le nom de la propriété ; -
Type de la propriété. -
le type propriétaire de la propriété ; -
Les métadonnées de la propriété. Les métadonnées contiennent la valeur par défaut de la propriété, un CoerceValueCallback et un PropertyChangedCallback.
-
-
Définissez une propriété de wrapper CLR nommée Value, qui est le nom utilisé pour enregistrer la propriété de dépendance, en implémentant les accesseurs get et set de la propriété. Notez que les accesseurs get et set appellent uniquement GetValue et SetValue, respectivement. Il est conseillé de ne pas inclure de logique supplémentaire dans les accesseurs de propriétés de dépendance car les clients et WPF peuvent ignorer les accesseurs et appeler directement GetValue et SetValue. Par exemple, lorsqu'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 les délégués ValidateValueCallback, CoerceValueCallback et PropertyChangedCallback pour répondre ou vérifier la valeur lorsque celle-ci 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, nommée 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ée OnValueChanged. OnValueChanged crée un objet RoutedPropertyChangedEventArgs<T> et se prépare à déclencher l'événement routé ValueChanged. Les événements routés sont examiné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); }
Utiliser RoutedEvents
-
Les événements peuvent être gérés 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'une ListBox (parce qu'il est inclus dans un DataTemplate), le développeur d'applications peut définir le gestionnaire de l'événement de votre contrôle sur la ListBox. Dans ce cas, chaque fois que l'événement se produit sur un des contrôles, le gestionnaire d'événements est appelé. -
Des événements routés peuvent être utilisés dans un EventSetter, ce qui permet aux développeurs d'applications de spécifier le gestionnaire d'un événement dans un style. -
Il est possible d'utiliser des événements routé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.
-
Définissez un identificateur RoutedEvent nommé ValueChangedEvent en tant que champ public static readonly. -
Enregistrez l'événement routé en appelant la méthode EventManager.RegisterRoutedEvent. L'exemple spécifie les informations suivantes lorsqu'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 que les gestionnaires d'événements sur les éléments parents de la source sont appelés dans l'ordre, 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>, qui est construit avec un type Decimal. -
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 afin d'utiliser les services d'événement 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); }
Utilisation de la liaison
<Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch"> <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/> </Border>
<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>
Conception pour les concepteurs
Propriétés de dépendance
Propriétés jointes
-
Préparez une DependencyPropertypublicstaticreadonly de type NomPropriétéProperty créé à l'aide de l'une des méthodes RegisterAttached. Le nom de propriété passé à RegisterAttached doit correspondre à NomPropriété. -
Implémentez deux méthodes publicstatic CLR nommées SetNomPropriété et GetNomPropriété. Les deux méthodes doivent accepter une classe dérivée de DependencyProperty pour leur premier argument. La méthode SetNomPropriété accepte également un argument dont le type correspond au type de données enregistré pour la propriété. La méthode GetNomPropriété doit retourner une valeur du même type. Si la méthode SetNomPropriété fait défaut, la propriété est marquée en lecture seule. -
Set PropertyName et GetPropertyName doivent router directement aux méthodes GetValue et SetValue sur l'objet de dépendance cible, respectivement. Les concepteurs peuvent accéder à la propriété attachée en l'appelant via le wrapper de méthode ou lançant un appel direct à l'objet de dépendance cible.
Définir et utiliser des ressources partagées
-
Niveau de l'élément. Le système commence par l'élément qui référence la ressource, puis il passe en revue les ressources du parent logique, et ainsi de suite, jusqu'à atteindre l'élément racine ; -
Niveau de l'application. Ressources définies par l'objet Application ; -
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. Lorsque 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.
Définition de ressources au niveau de l'élément
<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>
<UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Dictionary1.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources>
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;
}
public NumericUpDown()
{
this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
InitializeComponent();
}
Définition de ressources au niveau du thème
|
|
|
|---|---|
|
Classic.xaml |
|
|
Luna.NormalColor.xaml |
|
|
Luna.Homestead.xaml |
|
|
Luna.Metallic.xaml |
|
|
Royale.NormalColor.xaml |
|
|
Aero.NormalColor.xaml |
|
Définition et référencement de clés pour les ressources de thème
<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>
<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 spécifiques aux thèmes