I. Les contrôles et les liaisons▲
Comme les contrôles supportent les liaisons, commençons par analyser comment ils implémentent cette fonctionnalité.
L'explorateur d'objets va nous renseigner sur ce point, il nous indique que la classe ControlClasse Control hérite de ou implémente ce qui suit :
On observe tout de suite une interface au nom explicite : IBindableComponentInterface IBindableComponent.
MSDN nous indique à son propos : « Permet à un composant qui n'est pas un contrôle d'émuler le comportement de liaison de données d'un contrôle Windows Forms. »
Exactement ce que l'on souhaite faire.
- BindingContextPropriété BindingContext de type BindingContextClasse BindingContext ;
- DataBindingsPropriété DataBindings de type ControlBindingsCollectionClasse ControlBindingsCollection.
DataBindings est justement la propriété que l'on déroule dans la page de propriétés pour obtenir l'éditeur de liaison.
On a trouvé ce qu'il manque à nos composants.
II. Comment combler ce manque ?▲
II-A. Nouveaux composants▲
Dans le cas d'un nouveau composant, aucune difficulté, il suffit d'implémenter cette interface et ce composant supportera les liaisons.
II-B. Composants existants▲
Dans notre problématique, on souhaite ajouter cette interface sur des composants existants.
En effet je me vois mal hériter de tous les composants des Windows Forms pour leur faire implémenter cette interface.
Ce serait inélégant et surtout le point de départ d'une vraie catastrophe. Imaginez un peu, une arborescence complète de composants à chaque nouvelle fonctionnalité !
Complètement ingérable comme solution.
II-B-1. Comment implémenter IBindableComponent sur des composants existants ?▲
En réalité, ce n'est pas nécessaire.
Il suffit de faire croire aux liaisons de données que l'objet auquel elles sont reliées implémente cette interface.
Enfin, « suffit », c'est vite dit, c'est un peu plus complexe que d'implémenter l'interface en question.
- la capacité d'extension des Windows Forms pour ajouter la ou les propriétés manquantes aux composants existants ;
- le patron de conception Bridge ou Pont en français ;
- le patron de conception Adapter ou Adaptateur en français ;
- le modèle objet utilisé par le moteur de liaison afin d'implémenter les deux patrons de conception ci-dessus.
La capacité d'extension des Windows Forms est représentée par les objets IExtenderProviderInterface IExtenderProvider et ProvidePropertyAttributeL'attribut ProvidePropertyAttribute.
MSDN nous dit à propos de IExtenderProviderInterface IExtenderProvider : "Définit l'interface pour étendre les propriétés à d'autres composants dans un conteneur."
Le but d'un Pont est de séparer l'aspect d'implémentation d'un objet de son aspect de représentation et d'interface.
Ainsi, d'une part l'implémentation peut être totalement encapsulée et d'autre part l'implémentation et la représentation peuvent évoluer indépendamment et sans que l'une exerce une contrainte sur l'autre.
Le but d'un Adaptateur est de convertir l'interface d'une classe existante en l'interface attendue par des clients également existants afin qu'ils puissent travailler ensemble.
Il s'agit de conférer à une classe existante une nouvelle interface pour répondre aux besoins des clients.
Le modèle objetVue d'ensemble du descripteur de type. utilisé par le moteur de liaison est exposé dans l'espace de nom System.ComponentModelEspace de nom System.ComponentModel.
Ce sujet est vaste, je me contente d'une explication succincte ici.
Le moteur de liaison n'accède pas au modèle objet directement par la réflexion et les objets type et memberInfoLa réflexion, mais par une vue sur ce modèle.
Vue qui est constituée principalement par les objets TypeDescriptorClasse TypeDescriptor et MemberDescriptorClasse MemberDescriptor.
- Souplesse et modularité grâce aux Design PatternsSouplesse et modularité grâce aux Design Patterns ;
- Comprendre le Design Pattern AdaptateurComprendre le Design Pattern Adaptateur ;
- Le forum de Développez.comLe forum de Développez.com.
Les définitions des patrons de conception ci-dessus sont tirées du livre : Design Patterns Pour JavaDesign Patterns Pour Java.
II-B-2. La réponse en image : le principe de fonctionnement▲
Ce schéma nous permet de mieux visualiser le processus d'adaptation.
Par contre, la fonction du Pont reste encore floue.
II-B-3. Le Pont : mise en situation et intervenants▲
On constate que la fonction première du Pont est de dérouter le processus de description sur l'instance réelle exposée par le Pont.
L'interface du Pont prend ici tout son intérêt ; en effet, redéfinir toute la logique de description peut être un travail long et pénible.
En implémentant ce concept de façon autonome, on rend sa réutilisation et sa compréhension plus faciles.
- le Fournisseur de descripteur de Pont hérite de TypeDescriptionProviderClasse TypeDescriptionProvider ;
- le Descripteur de Pont hérite de CustomTypeDescriptorClasse CustomTypeDescriptor ;
- le Descripteur de propriété du Pont hérite de PropertyDescriptorClasse PropertyDescriptor ;
- le Convertisseur de type hérite de TypeConverterClasse TypeConverter.
III. Le tour de magie en pratique▲
La solution envisagée peut paraître complexe à première vue.
Dans la pratique, comme vous allez pouvoir le constater, il n'en est rien : le code des objets est simple, voire extrêmement simple.
Le seul point vraiment indispensable à la compréhension du code, est l'astuce utilisée par l'Adaptateur.
Astuce qui est marquée par l'image adéquate dans l'explication du code de l'Adaptateur.
J'entends déjà des personnes s'écrier : « Mais il a la paternité aiguë celui-là ! »
Il est vrai que dans cet article j'utilise deux patrons de conception.
Il est vrai aussi que l'utilisation du Pont peut paraître superflue.
En ce qui me concerne, il est surtout vrai que sans le Pont, l'article et le code auraient été beaucoup plus compliqués à comprendre.
Concernant l'Adaptateur, c'est le meilleur cas d'utilisation que j'aie jamais vu en DotNet ; donc la question ne se pose même pas.
Je m'explique : nous allons ajouter une fonctionnalité du Framework à des objets du Framework, sans modifier pour autant ni l'un ni l'autre.
On se rapproche fortement du cas d'école ! Non ? Un cas d'école utile qui plus est.
III-A. Les extensions : le DataBindingsProvider▲
L'objectif de ce composant est d'ajouter la propriété DataBindingsPropriété DataBindings aux composants existants.
Cette propriété doit fournir le même fonctionnement que la propriété DataBindingsPropriété DataBindings des contrôles.
Il correspond au fournisseur d'extensions du premier schéma.
<
ProvideProperty
(
"DataBindings"
, GetType
(
IComponent))>
Public
Class
DataBindingsProvider
Implements
IExtenderProvider
Public
Function
CanExtend
(
ByVal
extendee As
Object
) As
Boolean
Implements
IExtenderProvider.CanExtend
If
extendee Is
Nothing
Then
Return
False
If
TypeOf
extendee Is
IBindableComponent Then
Return
False
Return
TypeOf
extendee Is
IComponent
End
Function
<
Category
(
"Data"
)>
<
RefreshProperties
(
RefreshProperties.All
)>
<
ParenthesizePropertyName
(
True
)>
Public
Function
GetDataBindings
(
ByVal
component As
IComponent) As
ControlBindingsCollection
End
Function
Public
Sub
SetDataBindings
(
ByVal
component As
IComponent, ByVal
bindings As
ControlBindingsCollection)
End
Sub
End
Class
- on déclare que l'on fournit la propriété DataBindingsPropriété DataBindings pour tous les objets IComponentInterface IComponent via l'attribut ProvidePropertyL'attribut ProvideProperty ;
- on implémente l'interface IExtenderProviderInterface IExtenderProvider en filtrant les types étendus via la méthode CanExtendMéthode CanExtend (en plus de la validation du type attendu, on précise que l'on n'étend pas les objets qui implémentent déjà l'interface IBindableComponentInterface IBindableComponent) ;
- puis on fournit le code pour les accesseurs de la propriété.
C'est tout concernant l'ajout d'une propriété à un composant visuel par extension.
La logique de stockage des valeurs de propriétés n'a pas d'importance ici. Il s'agit d'un simple stockage par dictionnaire.
Pour aller plus loin sur ce sujet : MSDNComment implémenter un fournisseur d'extendeurs..
- lors de l'extension d'un objet, le concepteur appelle la méthode GetDataBindings sur DataBindingsProvider ;
-
la méthode GetDataBindings demande la valeur de la propriété à l'Adaptateur, si nécessaire, la stocke et renvoie cette valeur ;
Dans le code fourni, la ligne correspondant à l'utilisation de l'adaptateur est la suivante :Sélectionnez
ComponentBindings
=
New
BindableAdapter
(
component).DataBindings
-
la logique de persistance des valeurs des propriétés pourrait être faite en utilisant la logique par défaut.
Dans la preuve de faisabilité, j'ai préféré utiliser un sérialiser spécifique qui permet d'obtenir le code suivant :SélectionnezMe
.DataBindingsProvider1.AddBinding
(
Me
.ToolStripButton1
,New
System.Windows.Forms.Binding
(
"Enabled"
,Me
.BindingSource
,"Command1Enabled"
,True
, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged
))
III-B. L'adaptation : le BindableAdapter▲
L'objectif ici est de fournir l'implémentation de l'interface IBindableComponentInterface IBindableComponent au composant adapté.
Cet objet est nommé Adaptateur de liaison dans le premier schéma et Adaptateur dans le second.
<
TypeDescriptionProvider
(
GetType
(
BridgeDescriptionProvider))>
Public
Class
BindableAdapter
Inherits
Component
Implements
IBindableComponent
Implements
IBridge
Private
_Bindings As
ControlBindingsCollection
Private
_Context As
BindingContext
Private
ReadOnly
_RealInstance As
Object
Private
Sub
New
(
)
End
Sub
Public
Sub
New
(
ByVal
realInstance As
Object
)
If
realInstance Is
Nothing
Then
Throw
New
ArgumentNullException
(
"realInstance"
)
Me
._RealInstance
=
realInstance
End
Sub
Public
Property
BindingContext As
BindingContext Implements
IBindableComponent.BindingContext
Get
If
Me
._Context
Is
Nothing
Then
Me
._Context
=
New
BindingContext
Return
Me
._Context
End
Get
Set
(
ByVal
value As
BindingContext)
Me
._Context
=
value
End
Set
End
Property
Public
ReadOnly
Property
DataBindings As
ControlBindingsCollection Implements
IBindableComponent.DataBindings
Get
If
Me
._Bindings
Is
Nothing
Then
Me
._Bindings
=
New
ControlBindingsCollection
(
Me
)
Return
Me
._Bindings
End
Get
End
Property
Public
Function
GetRealInstance
(
) As
Object
Implements
IBridge.GetRealInstance
Return
Me
._RealInstance
End
Function
End
Class
Les éléments nécessaires au bon fonctionnement des liaisons sont l'implémentation de l'interface IBindableComponentInterface IBindableComponent et l'héritage de ComponentClasse Component.
L'implémentation de IBindableComponentInterface IBindableComponent est simple, comme on le voit dans le code, les valeurs sont créées et fournies à la demande.
Ensuite, l'Adaptateur utilise la logique du Pont afin de se faire passer pour le composant adapté au niveau du modèle de description et donc des liaisons.
- la déclaration du Fournisseur de description du Pont via l'attribut TypeDescriptionProviderL'attribut TypeDescriptionProvider ;
- l'implémentation de l'interface IBridge, qui est la condition sine qua non pour l'utilisation du Pont tel qu'il est conçu ici.
L'astuce est la suivante :
La ControlBindingsCollectionClasse ControlBindingsCollection est reliée via son constructeurConstructeur de la classe ControlBindingsCollection à l'Adaptateur et non à l'objet adapté.
Donc, quand le moteur de liaison cherche à résoudre les noms des propriétés, il utilise le descripteur de type fourni par l'Adaptateur qui est celui du pont, et qui va rediriger cette résolution sur l'instance réelle exposée par le Pont.
Instance qui correspond à l'adapté, c'est-à-dire, le composant étendu avec le support des liaisons.
III-C. La description : le Pont▲
Le Pont est la partie la plus complexe de cet article.
Il demande une bonne connaissance du modèle objetVue d'ensemble du descripteur de type utilisé par les liaisons.
Son but est le suivant : permettre à un objet de se faire passer pour un autre au niveau du modèle objet ou, plus précisément, déporter ses descriptions sur l'instance réelle exposée par le Pont.
III-C-1. L'interface du Pont : IBridge▲
Public
Interface
IBridge
Function
GetRealInstance
(
) As
Object
End
Interface
Impossible de faire plus simple.
III-C-2. Le fournisseur de descripteur de Pont : BridgeDescriptionProvider▲
Pour que la magie du Pont rentre en jeu, comme on l'a vu précédemment via l'Adaptateur, il faut utiliser l'attribut TypeDescriptionProviderAttributeL'attribut TypeDescriptionProviderAttribute.
Celui-ci va nous permettre de spécifier nous-mêmes le descripteur utilisé pour l'objet qui déclare cet attribut.
L'attribut TypeDescriptionProviderAttributeL'attribut TypeDescriptionProviderAttribute attend comme paramètre un TypeDescriptionProviderClasse TypeDescriptionProvider qui fournit les métadonnées supplémentaires au TypeDescriptorClasse TypeDescriptor.
Public
Class
BridgeDescriptionProvider
Inherits
TypeDescriptionProvider
Public
Overrides
Function
GetTypeDescriptor
(
ByVal
objectType As
Type
,
ByVal
instance As
Object
) As
ICustomTypeDescriptor
Return
New
BridgeDescriptor
(
DirectCast
(
instance, IBridge))
End
Function
End
Class
III-C-3. Le descripteur de Pont : BridgeDescriptor▲
Un TypeDescriptionProviderClasse TypeDescriptionProvider doit retourner un ICustomTypeDescriptorInterface ICustomTypeDescriptor.
Public
Class
BridgeDescriptor
Inherits
CustomTypeDescriptor
Private
ReadOnly
_Bridge As
IBridge
Public
Sub
New
(
ByVal
realInstance As
IBridge)
If
realInstance Is
Nothing
Then
Throw
New
ArgumentNullException
(
"realInstance"
)
Me
._Bridge
=
realInstance
End
Sub
Private
Function
GetBridgePropertyDescriptorCollection
(
ByVal
realDescriptors As
PropertyDescriptorCollection) As
PropertyDescriptorCollection
Return
New
PropertyDescriptorCollection
(
Me
.GetBridgePropertyDescriptors
(
realDescriptors))
End
Function
Private
Function
GetBridgePropertyDescriptors
(
ByVal
realDescriptors As
PropertyDescriptorCollection) As
BridgePropertyDescriptor
(
)
Dim
Descriptors As
New
List
(
Of
BridgePropertyDescriptor)
For
Each
RealDescriptor As
PropertyDescriptor In
realDescriptors
Descriptors.Add
(
New
BridgePropertyDescriptor
(
Me
._Bridge
, RealDescriptor))
Next
Return
Descriptors.ToArray
End
Function
Public
Overrides
Function
GetProperties
(
) As
PropertyDescriptorCollection
Return
Me
.GetBridgePropertyDescriptorCollection
(
TypeDescriptor.GetProperties
(
Me
._Bridge.GetRealInstance
))
End
Function
Public
Overrides
Function
GetProperties
(
ByVal
attributes
(
) As
Attribute) As
PropertyDescriptorCollection
Return
Me
.GetBridgePropertyDescriptorCollection
(
TypeDescriptor.GetProperties
(
Me
._Bridge.GetRealInstance
, attributes))
End
Function
End
Class
III-C-4. Le descripteur de propriété du Pont : BridgePropertyDescriptor▲
Pour décrire ses propriétés, le BridgeDescriptor utilise un BridgePropertyDescriptor par propriété.
Public
Class
BridgePropertyDescriptor
Inherits
PropertyDescriptor
#Region
"Fields"
Private
_Bridge As
IBridge
Private
_RealProperty As
PropertyDescriptor
#End
Region
#Region
"Constructors"
Public
Sub
New
(
ByVal
bridge As
IBridge, ByVal
realProperty As
PropertyDescriptor)
MyBase
.New
(
realProperty)
If
bridge Is
Nothing
Then
Throw
New
ArgumentNullException
(
"bridge"
)
If
realProperty Is
Nothing
Then
Throw
New
ArgumentNullException
(
"realProperty"
)
Me
._Bridge
=
bridge
Me
._RealProperty
=
realProperty
End
Sub
#End
Region
#Region
"Overrided Properties"
Public
Overrides
ReadOnly
Property
ComponentType As
Type
Get
Return
Me
._RealProperty.ComponentType
End
Get
End
Property
Public
Overrides
ReadOnly
Property
IsReadOnly As
Boolean
Get
Return
Me
._RealProperty.IsReadOnly
End
Get
End
Property
Public
Overrides
ReadOnly
Property
PropertyType As
Type
Get
Return
Me
._RealProperty.PropertyType
End
Get
End
Property
Public
Overrides
ReadOnly
Property
SupportsChangeEvents As
Boolean
Get
Return
Me
._RealProperty.SupportsChangeEvents
End
Get
End
Property
#End
Region
#Region
"Overrided Methods"
Public
Overrides
Function
GetValue
(
ByVal
component As
Object
) As
Object
Return
Me
._RealProperty.GetValue
(
Me
.GetRealComponent
(
component))
End
Function
Public
Overrides
Sub
SetValue
(
ByVal
component As
Object
, ByVal
value As
Object
)
Me
._RealProperty.SetValue
(
Me
.GetRealComponent
(
component), value)
End
Sub
Public
Overrides
Function
ShouldSerializeValue
(
ByVal
component As
Object
) As
Boolean
Return
Me
._RealProperty.ShouldSerializeValue
(
Me
.GetRealComponent
(
component))
End
Function
Public
Overrides
Function
CanResetValue
(
ByVal
component As
Object
) As
Boolean
Return
Me
._RealProperty.CanResetValue
(
Me
.GetRealComponent
(
component))
End
Function
Public
Overrides
Sub
ResetValue
(
ByVal
component As
Object
)
Me
._RealProperty.ResetValue
(
Me
.GetRealComponent
(
component))
End
Sub
Public
Overrides
Sub
AddValueChanged
(
ByVal
component As
Object
, ByVal
handler As
EventHandler)
Me
._RealProperty.AddValueChanged
(
Me
.GetRealComponent
(
component), handler)
End
Sub
Protected
Overrides
Sub
OnValueChanged
(
ByVal
component As
Object
, ByVal
e As
System.EventArgs
)
MyBase
.OnValueChanged
(
Me
.GetRealComponent
(
component), e)
End
Sub
Public
Overrides
Sub
RemoveValueChanged
(
ByVal
component As
Object
, ByVal
handler As
System.EventHandler
)
Me
._RealProperty.RemoveValueChanged
(
Me
.GetRealComponent
(
component), handler)
End
Sub
Protected
Overrides
Function
GetInvocationTarget
(
ByVal
type
As
Type
, ByVal
instance As
Object
) As
Object
If
type
Is
GetType
(
IBridge) Then
Return
Me
._Bridge
If
type
Is
Me
._Bridge.GetRealInstance.GetType
Then
Return
Me
._Bridge.GetRealInstance
Return
MyBase
.GetInvocationTarget
(
type
, instance)
End
Function
#End
Region
#Region
"Methods"
Private
Function
GetRealComponent
(
ByVal
candidate As
Object
) As
Object
If
candidate Is
Me
._Bridge
Then
Return
Me
._Bridge.GetRealInstance
Else
Return
candidate
End
If
End
Function
#End
Region
End
Class
Le BridgePropertyDescriptor est un PropertyDescriptorClasse PropertyDescriptor qui redirige les demandes de description sur l'instance réelle exposée par le Pont au lieu de l'instance du Pont.
Le convertisseur de type n'est pas présenté dans cet article.
Il n'apporte rien au sujet et n'est pas utilisé par le code de cet article.
Toutefois, pour les curieux, il est implémenté dans le code livré avec cet article.
IV. Démonstration▲
Pour démontrer le fonctionnement des liaisons sur les composants, nous allons utiliser le modèle suivant :
Public
Class
SampleDataSource
Implements
INotifyPropertyChanged
Public
Event
PropertyChanged
(
ByVal
sender As
Object
, ByVal
e As
PropertyChangedEventArgs)
Implements
INotifyPropertyChanged.PropertyChanged
Private
_Command1Enabled As
Boolean
=
True
Private
_Command2Enabled As
Boolean
=
True
<
DefaultValue
(
True
)>
Public
Property
Command1Enabled As
Boolean
Get
Return
Me
._Command1Enabled
End
Get
Set
(
ByVal
value As
Boolean
)
Me
._Command1Enabled
=
value
Me
.NotifyPropertyChanged
(
"Command1Enabled"
)
End
Set
End
Property
<
DefaultValue
(
True
)>
Public
Property
Command2Enabled As
Boolean
Get
Return
Me
._Command2Enabled
End
Get
Set
(
ByVal
value As
Boolean
)
Me
._Command2Enabled
=
value
Me
.NotifyPropertyChanged
(
"Command2Enabled"
)
End
Set
End
Property
Private
Sub
NotifyPropertyChanged
(
ByVal
propertyName As
String
)
RaiseEvent
PropertyChanged
(
Me
, New
PropertyChangedEventArgs
(
propertyName))
End
Sub
Public
Sub
ExecuteCommand1
(
ByVal
owner As
IWin32Window)
Me
.Command1Enabled
=
Not
Me
._Command1Enabled
MessageBox.Show
(
owner, "Command 1 State was toggled."
,
"Command 1 Executed"
, MessageBoxButtons.OK
, MessageBoxIcon.Information
)
End
Sub
Public
Sub
ExecuteCommand2
(
ByVal
owner As
IWin32Window)
Me
.Command2Enabled
=
Not
Me
._Command2Enabled
MessageBox.Show
(
owner, "Command 2 State was toggled."
,
"Command 2 Executed"
, MessageBoxButtons.OK
, MessageBoxIcon.Information
)
End
Sub
End
Class
Deux propriétés qui indiquent le statut actif / inactif de deux méthodes.
Deux méthodes qui inversent leur propre état à chaque utilisation et affichent un simple message.
Rien de bien transcendant.
Un ToolstripButton pour chaque commande.
Un PropertyGrid pour afficher le modèle de test.
Un BindingSource relié au modèle ci-dessus.
Un DataBindingsProvider pour fournir les liaisons aux composants.
Bonne nouvelle, le support des liaisons a bien été ajouté aux composants.
En plus, le comportement est strictement le même que pour les contrôles.
Private
Sub
ToolStripButton1_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
)
Handles
ToolStripButton1.Click
DirectCast
(
Me
.BindingSource.DataSource
, SampleDataSource).ExecuteCommand1
(
Me
)
Me
.PropertyGrid.Refresh
(
)
End
Sub
Private
Sub
ToolStripButton2_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
)
Handles
ToolStripButton2.Click
DirectCast
(
Me
.BindingSource.DataSource
, SampleDataSource).ExecuteCommand2
(
Me
)
Me
.PropertyGrid.Refresh
(
)
End
Sub
Public
Sub
New
(
)
' Cet appel est requis par le concepteur.
InitializeComponent
(
)
' Ajoutez une initialisation quelconque après l'appel InitializeComponent().
Me
.BindingSource.DataSource
=
New
SampleDataSource
Me
.PropertyGrid.SelectedObject
=
Me
.BindingSource.DataSource
End
Sub
Puis on lance la démonstration :
Quand on clique sur un des boutons, la commande s'exécute, et l'état de cette commande est modifié.
Quand on modifie l'état d'une commande via la PropertyGrid, ce changement se répercute sur le ToolStripButton relié à la propriété.
V. Conclusion▲
Le code de cet article fonctionne et prouve l'idée générale.
Par contre, il lui manque certaines choses :
- le pont ne gère que les propriétés ;
- la propriété BindingContextPropriété BindingContext n'est pas exposée par les extensions ;
- les sélections multiples des propriétés ne sont pas gérées.
En terminant cet article, nous clôturons la partie « Correction des lacunes et faiblesses de la liaison de données ».
Nous avons amélioré l'ergonomie des liaisonsPartie 1: Présentation et amélioration de l'ergonomie des liaisons et ajouté leur support à n'importe quel composant.
En ce faisant, une jolie transition s'est offerte à nous pendant la démonstration.
Transition qui nous emmène dans la partie « Extensions des fonctionnalités de la liaison de données » de cette série d'articles et qui introduit parfaitement l'article suivant.
En effet, dans cette démonstration, nous avons utilisé du code sans aucun intérêt pour déclencher les commandes sur les évènements des boutons.
Et si on pouvait se passer de ce genre de code ?
Et si nous pouvions faire des liaisons sur les évènements ?
La réponse dans le prochain article de cette série.
Merci à vous et à bientôt pour un article intitulé : « Des liaisons sur les évènementsWindows Forms - de la liaison de données à la liaison d'objets - partie 3 : des liaisons sur les évènements ».
Remerciements▲
J'adresse ici tous mes remerciements à tous les membres de l'équipe de rédaction de "developpez.com" pour le temps qu'ils ont bien voulu passer à la correction et à l'amélioration de cet article.
Merci notamment à Claude LELOUP et à Jacques JeanProfil de Jacques Jean pour leurs corrections et leurs relectures attentives.
Contact et code source▲
Si vous constatez une erreur dans l'article, dans les sources ou pour toute autre information, n'hésitez pas à me contacter via le forum de l'articleForum de l'article.
Le code source de la preuve de faisabilité est disponible iciLes sources de la preuve de faisabilité.