Dans la version 1.1 du framework .Net, on pouvait dessiner des « ornements » pour le Design Time en créant un ControlDesigner personnalisé et en redéfinissant la méthode OnPaintAdorments. Le problème de cette solution est que vous devez gérer vous-même le dessin de tous les ornements dans une seule méthode. De plus, si vous vouliez que les ornements soient interactifs, il fallait redéfinir des méthodes de ce même Designer comme OnMouseDragBegin et fabriquer vous-même la logique de sélection de la bonne action. Depuis la version 2.0, un nouveau service est apparu : le service de comportement au moment du Design, Behavior Service se chargeant de gérer les ornements et leur interaction avec l'utilisateur de l'environnement de développement. Cela se fait de manière indépendante et répartie : chaque ornement s'autogère.
Plus spécifiquement le Behavior Service permet de gérer de manière uniforme toute la manipulation des éléments d'interface utilisateur comme la manipulation à la souris, avec les commandes de menus et les opérations de glisser-déposer au moment du Design.
Pour dessiner sur un contrôle au moment du Design, le service Behavior gère une série d'ornements ou adorners . Chaque ornement possède un ou plusieurs glyphs afin de réaliser le dessin d'une partie ou de la totalité de l'ornement. Un Behavior peut être associé à un glyph afin que l'utilisateur puisse interagir avec le glyph (dessin). Cependant un Behavior peut être seul et agir globalement.
Le service Behavior se compose donc d'une pile de Behaviors et d'une série d'Adorners/Glyphs.
Par défaut, on trouve quelques ornements/Behaviors :
Définition : un « glyph » est un élément graphique d'interface utilisateur, c'est-à-dire n'importe quelle zone ajoutée au contrôle et qui peut interagir avec lui au clic, déplacement de souris : par exemple, les poignées de redimensionnement. Elle assure son dessin et permet de tester si la souris se trouve dans sa surface. Un glyph n'implémente que le dessin pas l'interaction.
Définition : un « adorner » est un ensemble de « glyphes » qui peut être désactivé par le « BehaviorService ». C'est une couche invisible.
Définition : un « behavior » associe une interaction associée à un « glyph ».
Définition : le « BehaviorService » permet de gérer l'interaction de l'utilisateur avec les « glyphes » présents sur chaque contrôle. Il se charge de tester sur quel glyph se trouve la souris et d'exécuter le bon « behavior » associé au glyph.
Tout d'abord, il convient d'ajouter la référence à l'assembly System.Design.dll. Ensuite, toutes les classes mentionnées dans les sections qui suivent, appartiennent à l'espace de nom System.Windows.Forms.Design.Behavior.
Un Adorner est un ensemble d'éléments graphiques qui peuvent être activés ou désactivés ensemble. S'ils sont désactivés, aucun dessin ne sera réalisé et aucun hit test ne sera fait pour savoir si la souris se trouve sur un des glyphes de l'Adorner désactivé. De plus, la classe Adorner possède une propriété BehaviorService de sorte que l'Adorner connaît son service Behavior. Enfin, on peut appeler la méthode Invalidate de l'Adorner afin de forcer le rafraîchissement du dessin de tous ses glyphes.
La classe Adorner possède les membres suivants :
Vous devez créer une classe dérivée de Glyph afin d'implémenter le dessin de vos glyphes . La classe Glyph demande un Behavior mais si votre Glyph n'a pas d'interaction vous pouvez lui passer null/Nothing.
La classe Glyph possède les propriétés suivantes que vous pouvez redéfinir :
Vous devez redéfinir au moins les membres suivants :
En général, on redéfinira Bounds aussi afin de spécifier le cadreduglyph.
/// <summary> /// Notre glyph "texte central" : élément graphique (interactif en l'occurence) /// dessinant un texte au milieu du contrôle /// </summary> private class CenterTextGlyph : Glyph { //le contrôle sur lequel on dessine private Control myControl = null; //le service de comportement pour pouvoir par exemple, forcer le redessin du glyph private BehaviorService behaviorSvc = null; /// <summary> /// Construit le glyph avec les éléments dont il aura besoin. /// Le constructeur parent prend en paramètre le "comportement" associé au glyph : /// on lui passe donc une instance de notre classe dérivée de Behavior /// qui gère l'affichage du message au clic sur le texte /// </summary> /// <param name="control"></param> /// <param name="behaviorService"></param> public CenterTextGlyph(Control control, BehaviorService behaviorService) : base(newCenterTextBehavior()) { this.myControl = control; this.behaviorSvc = behaviorService; } /// <summary> /// Renvoie les bornes du glyph /// </summary> public override Rectangle Bounds { get { //récupère la position et la taille de notre contrôle sur la surface de Design Rectangle bounds = behaviorSvc.ControlRectInAdornerWindow(this.myControl); //calcule la taille de la chaîne "Margin" //avec le double de taille de la police du contrôle Font textFont = newFont("Arial", this.myControl.Font.SizeInPoints * 2); SizeF textSizeF = SizeF.Empty; using (Graphics g = this.myControl.CreateGraphics()) { textSizeF = g.MeasureString("Margin", textFont); } //calcule la position du texte pour le centrer dans le contrôle return new Rectangle( bounds.Left + (int)(bounds.Width - textSizeF.Width) / 2, bounds.Top + (int)(bounds.Height - textSizeF.Height) / 2, (int)textSizeF.Width, (int)textSizeF.Height ); } } /// <summary> /// Indique le curseur à afficher quand le pointeur de la souris /// se trouve au point p /// (en général le curseur peut être différent s'il se trouve ou non /// dans la surface du glyph) /// Cela permet de savoir indirectement si la souris se trouve sur le glyph. /// Dans notre cas, on change le curseur quand on se trouve sur le texte. /// </summary> /// <param name="p"></param> /// <returns></returns> public override Cursor GetHitTest(Point p) { if (this.Bounds.Contains(p)) return Cursors.Hand; else return null; } /// <summary> /// Dessine le glyph : texte "Margin" au centre du contrôle en magenta foncé /// Attention : les coordonnées sont relatifs à la surface de Design. /// Il faut donc se référer aux Bounds du glyph calculées /// à partir des Bounds de notre contrôle /// telles que renvoyées par le BehaviorService /// </summary> /// <param name="pe"></param> public override void Paint(PaintEventArgs pe) { //dessine en magenta using (Pen p = newPen(Color.DarkMagenta)) { //le texte "Margin" récupère le rectangle dans lequel on va placer le texte Rectangle r = this.Bounds; using (Brush b = newSolidBrush(Color.DarkMagenta)) { //double de la police du contrôle Font f = newFont("Arial", this.myControl.Font.SizeInPoints * 2); pe.Graphics.DrawString("Margin", f, b, r.Left, r.Top); } } } }
Le Behavior associe un comportement à un glyph. On peut aussi ajouter directement un Behavior dans le BehaviorService . Par exemple, un Behavior peut être appelé pour un drag-drop ou un clic. Le Behavior actif se trouve au sommet de la pile des Behavior dans le BehaviorService puis une fois utilisé, il est dépilé.
La classe Behavior possède les membres suivants :
On peut redéfinir chacune des méthodes et propriétés précédentes.
/// <summary> /// Gère le comportement au clic sur le glyph associé /// </summary> private class CenterTextBehavior : Behavior { /// <summary> /// On affiche juste un message au clic gauche /// </summary> /// <param name="g"></param> /// <param name="button"></param> /// <param name="mouseLoc"></param> /// <returns></returns> public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) { if (button == MouseButtons.Left) MessageBox.Show("Vous m'avez cliqué dessus ? Emplacement " +mouseLoc.ToString()); return base.OnMouseDown(g, button, mouseLoc); } }
Il est nécessaire de créer un Designer dérivant de ControlDesigner et de redéfinir sa méthode Initialize afin d'ajouter un ou plusieurs Adorner s contenant un ou plusieurs Glyph au service Behavior :
Cela se fera avec le code suivant :
//créer un "orneur" (Adorner) pour contenir la liste de nos glyphs //en rapport avec les marges du contrôle Adorner myAdorner = newAdorner(); //Ajoute les glyphs : //-> un non interactif qui est le cadre autour des marges de notre contrôle //-> un interactif qui permet d'afficher un message au clic // que le texte central qu'il affiche myAdorner.Glyphs.Add(newMarginPaintGlyph(this.Control,behaviorSvc,compSvc)); myAdorner.Glyphs.Add(newCenterTextGlyph(this.Control, behaviorSvc)); //ajoute "l'orneur" au service de comportement (BehaviorService) behaviorSvc.Adorners.Add(myAdorner);
Le BehaviorService possède les membres suivants :
Voici un exemple montrant divers Adornments et comportements au Design :
/// <summary> /// Démontre comment créer un contrôle avec des "ornements" interactifs /// et non interactifs au moment du Design /// L'attribut Designer permet de spécifier notre Designer personnalisé /// ajoutant nos "ornements" au service de comportement /// (BehaviorService). Un "ornement" est une instance de la classe Adorner /// contenant une liste de "choses graphiques" ou "glyph" /// (le dessin) qui ont un rapport commun. (par exemple, il y a un Adorner /// contenant les 8 poignées (glyph) de redimensionnement) /// </summary> [DesignerAttribute(typeof(BehaviorUserControlDesigner))] public partial class BehaviorUserControl : UserControl { public BehaviorUserControl() { InitializeComponent(); } /// <summary> /// Notre Designer ajoutant nos "adorners" et nos "glyphs" /// dans le service de comportement (BehaviorService) /// </summary> private class BehaviorUserControlDesigner : ControlDesigner { /// <summary> /// Appelé à l'initialisation du Designer /// </summary> /// <param name="component"> composant auquel on est attaché </param> public override void Initialize(IComponent component) { base.Initialize(component); //récupère le service de comportement qui permet d'ajouter //les adorners et les glyphs BehaviorService behaviorSvc = (BehaviorService) this.GetService(typeof(BehaviorService)); //le service de changement des composants pour rafraîchir //la taille et position des glyphs //quand notre composant change ses marges IComponentChangeService compSvc = (IComponentChangeService) this.GetService(typeof(IComponentChangeService)); //qui permet de savoir quel contrôle est actuellement sélectionné //et de savoir quand la sélection change ISelectionService selSvc = (ISelectionService)this.GetService(typeof(ISelectionService)); if (behaviorSvc != null) { //créer un "orneur" (Adorner) pour contenir la liste de nos glyphs //en rapport avec les marges du contrôle Adorner myAdorner = newAdorner(); //Ajoute les glyphs : //-> un non interactif qui est le cadre autour des marges //de notre contrôle //-> un interactif qui permet d'afficher un message au clic //que le texte central qu'il affiche myAdorner.Glyphs.Add(new MarginPaintGlyph(this.Control,behaviorSvc,compSvc)); myAdorner.Glyphs.Add(newCenterTextGlyph(this.Control, behaviorSvc)); //ajoute "l'orneur" au service de comportement (BehaviorService) behaviorSvc.Adorners.Add(myAdorner); //créer un "orneur" permettant de modifier l'ancrage du contrôle // (sa propriété Anchor) Adorner myAnchorAdorner = newAdorner(); //Ajoute les glyphs des quatre bords permet d'activer l'ancrage //sur le bord correspondant myAnchorAdorner.Glyphs.Add(new AnchorGlyph(this.Control, behaviorSvc, compSvc, selSvc, myAnchorAdorner, AnchorGlyph.Border.Left)); myAnchorAdorner.Glyphs.Add(new AnchorGlyph(this.Control, behaviorSvc, compSvc, selSvc, myAnchorAdorner, AnchorGlyph.Border.Right)); myAnchorAdorner.Glyphs.Add(new AnchorGlyph(this.Control, behaviorSvc, compSvc, selSvc, myAnchorAdorner, AnchorGlyph.Border.Top)); myAnchorAdorner.Glyphs.Add(new AnchorGlyph(this.Control, behaviorSvc, compSvc, selSvc, myAnchorAdorner, AnchorGlyph.Border.Bottom)); //ajoute "l'orneur" au service de comportement (BehaviorService) behaviorSvc.Adorners.Add(myAnchorAdorner); } } } /// <summary> /// Notre glyph de marge : élément graphique (non intéractif en l'occurence) /// dessinant le cadre des marges du contrôle /// </summary> private class MarginPaintGlyph : Glyph { //le contrôle sur lequel on dessine private Control myControl = null; //le service de comportement pour pouvoir par exemple, forcer le redessin du glyph private BehaviorService behaviorSvc = null; //le service de changement de composant, pour pouvoir repositionner le glyph //quand il change ses marges private IComponentChangeService compSvc = null; /// <summary> /// Construit le glyph avec les éléments dont il aura besoin. /// Le constructeur parent attend une instance d'un comportement. /// Dans notre cas, on lui passe "null", car le glyph n'est pas intéractif /// </summary> /// <param name="control"></param> /// <param name="behaviorService"></param> /// <param name="compChangeService"></param> public MarginPaintGlyph(Control control, BehaviorService behaviorService, IComponentChangeService compChangeService) : base(null) { this.myControl = control; this.behaviorSvc = behaviorService; this.compSvc = compChangeService; //demande la notification quand un composant de la surface de Design change //et en particulier le nôtre if (compChangeService != null) compChangeService.ComponentChanged += new ComponentChangedEventHandler(compChangeService_ComponentChanged); } //au changement des marges, de la position ou de la taille de notre composant, //on doit redessiner le rectangle des marges qu'affiche ce glyph void compChangeService_ComponentChanged(object sender, ComponentChangedEventArgs e) { //si le composant modifié est le notre et que l'on modifie ses marges if (object.ReferenceEquals(e.Component, this.myControl) && (e.Member.Name == "Margin" || e.Member.Name == "Size" || e.Member.Name == "Location") ) { //on doit redessiner le glyph pour rafraichir le rectangle des marges behaviorSvc.Invalidate(); } } /// <summary> /// Indique le curseur à afficher quand le pointeur de la souris se trouve /// au point p /// (en général le curseur peut être différent s'il se trouve /// ou non dans la surface du glyph) /// Cela permet de savoir indirectement si la souris se trouve sur le glyph. /// Dans notre cas, on ne change pas le curseur quand on est dans le rectangle /// des marges /// puisque notre glyph n'est pas interactif /// </summary> /// <param name="p"></param> /// <returns></returns> public override Cursor GetHitTest(Point p) { return null; } /// <summary> /// Renvoie les bornes du glyph /// </summary> public override Rectangle Bounds { get { //récupère la position et la taille de notre contrôle sur la surface de Design Rectangle bounds = behaviorSvc.ControlRectInAdornerWindow(this.myControl); //ajoute les marges aux bornes du contrôle return Rectangle.FromLTRB( bounds.Left - this.myControl.Margin.Left, bounds.Top - this.myControl.Margin.Top, bounds.Right + this.myControl.Margin.Right, bounds.Bottom + this.myControl.Margin.Bottom ); } } /// <summary> /// Dessine le glyph : cadre des marges en magenta foncé /// Attention : les coordonnées sont relative à la surface de Design. /// Il faut donc se référer aux Bounds du glyph calculées à partir /// des Bounds de notre contrôle telles que renvoyées /// par le BehaviorService /// </summary> /// <param name="pe"></param> public override void Paint(PaintEventArgs pe) { //dessine en magenta using(Pen p = newPen(Color.DarkMagenta)) { //un rectangle représentant les marges de notre contrôle Graphics g = pe.Graphics; g.DrawRectangle(p, this.Bounds); } } } /// <summary> /// Notre glyph "texte central" : élément graphique (intéractif en l'occurence) /// dessinant un texte au milieu du contrôle /// </summary> private class CenterTextGlyph : Glyph { //le contrôle sur lequel on dessine private Control myControl = null; //le service de comportement pour pouvoir par exemple, forcer le redessin du glyph private BehaviorService behaviorSvc = null; /// <summary> /// Construit le glyph avec les éléments dont il aura besoin. /// Le constructeur parent prend en paramètre le "comportement" associé au glyph : /// on lui passe donc une instance de notre classe dérivée de Behavior /// qui gère l'affichage du message au clic sur le texte /// </summary> /// <param name="control"></param> /// <param name="behaviorService"></param> public CenterTextGlyph(Control control, BehaviorService behaviorService) : base(newCenterTextBehavior()) { this.myControl = control; this.behaviorSvc = behaviorService; } /// <summary> /// Renvoie les bornes du glyph /// </summary> public override Rectangle Bounds { get { //récupère la position et la taille de notre contrôle //sur la surface de Design Rectangle bounds = behaviorSvc.ControlRectInAdornerWindow(this.myControl); //calcule la taille de la chaîne "Margin" avec le double de la taille //de la police du contrôle Font textFont = newFont("Arial", this.myControl.Font.SizeInPoints * 2); SizeF textSizeF = SizeF.Empty; using (Graphics g = this.myControl.CreateGraphics()) { textSizeF = g.MeasureString("Margin", textFont); } //calcule la position du texte pour le centrer dans le contrôle return new Rectangle( bounds.Left + (int)(bounds.Width - textSizeF.Width) / 2, bounds.Top + (int)(bounds.Height - textSizeF.Height) / 2, (int)textSizeF.Width, (int)textSizeF.Height ); } } /// <summary> /// Indique le curseur à afficher quand le pointeur de la souris /// se trouve au point p /// (en général le curseur peut être différent s'il se trouve /// ou non dans la surface du glyph) /// Cela permet de savoir indirectement si la souris se trouve sur le glyph. /// Dans notre cas, on change le curseur quand on se trouve sur le texte. /// </summary> /// <param name="p"></param> /// <returns></returns> public override Cursor GetHitTest(Point p) { if (this.Bounds.Contains(p)) return Cursors.Hand; else return null; } /// <summary> /// Dessine le glyph : texte "Margin" au centre du contrôle en magenta foncé /// Attention : les coordonnées sont relatives à la surface de Design. /// Il faut donc se référer aux Bounds du glyph calculées à partir des Bounds /// de notre contrôle /// telles que renvoyées par le BehaviorService /// </summary> /// <param name="pe"></param> public override void Paint(PaintEventArgs pe) { //dessine en magenta using (Pen p = newPen(Color.DarkMagenta)) { //le texte "Margin" //récupère le rectangle dans lequel on va placer le texte Rectangle r = this.Bounds; using (Brush b = newSolidBrush(Color.DarkMagenta)) { //double de la police du contrôle Font f = newFont("Arial", this.myControl.Font.SizeInPoints * 2); pe.Graphics.DrawString("Margin", f, b, r.Left, r.Top); } } } /// <summary> /// Gère le comportement au clic sur le glyph associé /// </summary> private class CenterTextBehavior : Behavior { /// <summary> /// On affiche juste un message au clic gauche /// </summary> /// <param name="g"></param> /// <param name="button"></param> /// <param name="mouseLoc"></param> /// <returns></returns> public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) { if (button == MouseButtons.Left) MessageBox.Show("Vous m'avez cliqué dessus ? Emplacement " +mouseLoc.ToString()); return base.OnMouseDown(g, button, mouseLoc); } } } /// <summary> /// Notre glyph de définition des ancres : élément graphique /// (interactif en l'occurence) /// dessinant un rectangle sur chacun des bords du contrôle /// (rectangle rouge si une ancre est définie sur ce bord, vert sinon) /// </summary> private class AnchorGlyph : Glyph { //taille du glyph en position verticale (horizontale, on inverse) private const int glyphHeight = 30; private const int glyphWidth = 20; //le contrôle sur lequel on dessine private Control myControl = null; //le service de comportement pour pouvoir par exemple, forcer le redessin du glyph private BehaviorService behaviorSvc = null; //le service de changement de composant, pour pouvoir repositionner le glyph //quand le contrôle change de place ou de taille private IComponentChangeService compSvc = null; //permet de savoir quand la sélection change et quel contrôle est sélectionné private ISelectionService selectionService = null; //permet de ne redessiner/activer/désactiver que nos glyphs private Adorner myAdorner = null; //indique sur quel bord doit se trouver notre glyph d'ancre public enum Border { Left, Right, Top, Bottom } private Border border; /// <summary> /// Construit le glyph avec les éléments dont il aura besoin /// </summary> /// <param name="control"></param> /// <param name="behaviorService"></param> /// <param name="compChangeService"></param> /// <param name="selectionService"></param> /// <param name="parentAdorner"></param> /// <param name="border"></param> public AnchorGlyph(Control control, BehaviorService behaviorService, IComponentChangeService compChangeService, ISelectionService selectionService, Adorner parentAdorner, Border border) : base(newAnchorBehavior(control,parentAdorner,border)) { this.myControl = control; this.behaviorSvc = behaviorService; this.compSvc = compChangeService; this.selectionService = selectionService; this.border = border; this.myAdorner = parentAdorner; //demande la notification quand un composant de la surface de Design change if (compChangeService != null) compChangeService.ComponentChanged += new ComponentChangedEventHandler(compChangeService_ComponentChanged); //demande la notification quand la sélection change if (selectionService != null) selectionService.SelectionChanged += new EventHandler(selectionService_SelectionChanged); } //permet d'activer le glyph uniquement quand notre contrôle est sélectionné // (comme sélection principale) void selectionService_SelectionChanged(object sender, EventArgs e) { //si le composant sélectionné est le nôtre if (object.ReferenceEquals(selectionService.PrimarySelection, this.myControl)) //on affiche (active) nos glyphs this.myAdorner.Enabled = true; else //sinon on les cache this.myAdorner.Enabled = false; } //au changement de la position/taille/sa propriété Anchor de notre composant, //on doit redessiner le glyph sur son bord au bon endroit void compChangeService_ComponentChanged(object sender, ComponentChangedEventArgs e) { //si le composant modifié est le nôtre et que l'on modifie sa position //ou sa taille //ou sa propriété Anchor (changement éventuel de la couleur du glyph //associé au bord) if (object.ReferenceEquals(e.Component, this.myControl) && (e.Member.Name == "Anchor" || e.Member.Name == "Size" || e.Member.Name == "Location") ) { //on doit redessiner le glyph pour rafraîchir le rectangle de //l'ancre et/ou sa couleur this.myAdorner.Invalidate(); } } /// <summary> /// Renvoie les bornes du glyph /// </summary> public override Rectangle Bounds { get { //récupère la position et la taille de notre contrôle //sur la surface de Design Rectangle bounds = behaviorSvc.ControlRectInAdornerWindow(this.myControl); //place le contrôle au milieu de son bord affecté //note ; la largeur et la hauteur du rectangle de ce glyph //est inversé pour les bords horizontaux) switch (border) { case Border.Left: return new Rectangle( bounds.Left, bounds.Top + (int)(bounds.Height - glyphHeight) / 2, glyphWidth, glyphHeight ); case Border.Right: return new Rectangle( bounds.Right - glyphWidth, bounds.Top + (int)(bounds.Height - glyphHeight) / 2, glyphWidth, glyphHeight ); case Border.Top: return new Rectangle( bounds.Left + (int)(bounds.Width - glyphHeight) / 2, bounds.Top, glyphHeight, glyphWidth ); case Border.Bottom: return new Rectangle( bounds.Left + (int)(bounds.Width - glyphHeight) / 2, bounds.Bottom - glyphWidth, glyphHeight, glyphWidth ); default: return Rectangle.Empty; } } } /// <summary> /// Indique le curseur à afficher quand le pointeur de la souris /// se trouve au point p /// (en général le curseur peut être différent s'il se trouve /// ou non dans la surface du glyph) /// Cela permet de savoir indirectement si la souris se trouve sur le glyph. /// Dans notre cas, on change le curseur quand on se trouve /// sur le rectangle d'ancre. /// </summary> /// <param name="p"></param> /// <returns></returns> public override Cursor GetHitTest(Point p) { if (this.Bounds.Contains(p)) return Cursors.Hand; else return null; } /// <summary> /// Dessine le glyph : rectangle sur un bord du contrôle /// (rouge si l'ancre est définie pour ce bord, vert sinon) /// Attention : les coordonnées sont relatives à la surface de Design. /// Il faut donc se référer aux Bounds du glyph calculées à partir /// des Bounds de notre contrôle /// telles que renvoyées par le service BehaviorService /// </summary> /// <param name="pe"></param> public override void Paint(PaintEventArgs pe) { Color bg = (((AnchorBehavior)this.Behavior).AnchorForBorder ? Color.Red : Color.Green); //dessine un rectangle de la couleur bg avec bordure noire using (Pen p = newPen(Color.Black)) { //récupère le rectangle dans lequel on va dessiner Rectangle r = this.Bounds; using (Brush b = newSolidBrush(bg)) { pe.Graphics.FillRectangle(b, r); } pe.Graphics.DrawRectangle(p, r); } } /// <summary> /// Définit le comportement de modification de l'ancre associée /// au bord du glyph associé... /// </summary> private class AnchorBehavior : Behavior { //le contrôle sur lequel on dessine private Control myControl = null; //adorner parent pour rafraîchir les quatre glyphs au changement //de la propriété Anchor par ce Behavior private Adorner myAdorner = null; //sur quel bord du contrôle sommes-nous ? private Border border; public AnchorBehavior(Control myControl, Adorner parentAdorner, Border border) { this.border = border; this.myAdorner = parentAdorner; this.myControl = myControl; } //renvoie l'AnchorStyles associé au bord sur lequel est le glyph //qui a ce Behavior private AnchorStyles getAnchorStyleForBorder() { AnchorStyles ancStyle = AnchorStyles.None; switch (this.border) { case Border.Left: ancStyle = AnchorStyles.Left; break; case Border.Right: ancStyle = AnchorStyles.Right; break; case Border.Top: ancStyle = AnchorStyles.Top; break; case Border.Bottom: ancStyle = AnchorStyles.Bottom; break; } return ancStyle; } /// <summary> /// Définir la propriété Anchor en fonction de la valeur de ce Behavior /// et de son glyph /// Renvoie true si une ancre est présente sur le bord /// auquel ce Behavior est associé /// </summary> public bool AnchorForBorder { get { //renvoie true si un Anchor est défini pour le bord associé //à ce Behavior AnchorStyles ancStyle = getAnchorStyleForBorder(); return (this.myControl.Anchor & ancStyle) == ancStyle; } set { //récupère la propriété Anchor PropertyDescriptor propAnchor = TypeDescriptor.GetProperties(this.myControl)["Anchor"]; //récupère le AnchorStyles de notre bord AnchorStyles ancStyle = getAnchorStyleForBorder(); //lit directement la valeur de la propriété Anchor AnchorStyles anchorValue = this.myControl.Anchor; //active ou désactive l'ancre pour notre bord if (value) { anchorValue |= ancStyle; } else { anchorValue &= ~ancStyle; } //définit la valeur en avertissant le service IComponentChangeService //et donc redessine les glyphs du contrôle propAnchor.SetValue(this.myControl, anchorValue); } } public override bool OnMouseDown(Glyph g, MouseButtons button, Point mouseLoc) { //toggle la définition de l'ancre pour notre bord this.AnchorForBorder = !this.AnchorForBorder; return base.OnMouseDown(g, button, mouseLoc); } } } }