3.4. Des contrôles dans les formulaires


Plan général:   ...........retour au plan général


1. Les contrôles et les fonds graphiques

1.1 Les composants en général
1.2 Les contrôles sur un formulaire
1.3 Influence de la propriété parent sur l'affichage visuel d'un contrôle
1.4 Des graphiques dans les formulaires avec le GDI+
1.5 Le dessin doit être persistant
1.6 Deux exemples de graphiques sur plusieurs contrôles
        - méthode générale pour tout redessiner
        - des dessins sur événements spécifiques





1. Les contrôles et les fonds graphiques

Nous renvoyons le lecteur à la documentation du constructeur pour la manipulation de l'interface du RAD qu'il aura choisi. Nous supposons que le lecteur a aquis une dextérité minimale dans cette manimulation visuelle (déposer des composants, utiliser l'inspecteur d'objet (ou inspecteur de propriétés), modidifer des propriétés, créer des événements. Dans la suite du document, nous portons notre attention sur le code des programmes et sur leur comportement.

Car il est essentiel que le lecteur sache par lui-même écrire le code qui sera généré automatiquement par un RAD, sous peine d'être prisonnier du RAD et de ne pas pouvoir intervenir sur ce code !



1.1 les composants en général

System.Windows.Forms.Control définit la classe de base des contrôles qui sont des composants avec représentation visuelle, les fiches en sont un exemple.

   System.Object
      System.MarshalByRefObject
         System.ComponentModel.Component
            System.Windows.Forms.Control
               System.Windows.Forms.ScrollableControl
                  System.Windows.Forms.ContainerControl
                     System.Windows.Forms.Form

Dans les deux RAD Visual C# et C#Builder la programmation visuelle des contrôles a lieu d'une façon très classique, par glisser déposer de composants situés dans une palette ou boîte d'outils. Il est possible de déposer visuellement des composants, certains sont visuels ils s'appellent contrôles, d'autres sont non visuels, ils s'appellent seulement composants.
 
Les contrôles visuels ou Windows Forms dans les deux RAD :

C# Builder :                                Visual C# :


Cette distinction entre visuel et non visuel n'est pas très précise et dépend de la présentation dans le RAD. Cette variabilité de la dénomination n'a aucune importance pour l'utilisateur car tous les composants ont un fonctionnement du code identique.

En effet si nous prenons la classe  System.Windows.Forms.MainMenu :

System.ComponentModel.Component
         System.Windows.Forms.Menu
            System.Windows.Forms.MainMenu

Nous voyons que Visual C#  range System.Windows.Forms.MainMenu dans les contrôles alors que C# Builder le range dans les composants (donc non visuels). Ci-dessous les outils de composants proposés par les deux RAD ::

C# Builder                                    Visual C# :
    

Pour pouvoir construire une IHM, il nous faut pouvoir utiliser à minima les composants visuels habituels que nous retrouvons dans les logiciels  windows-like. Ici aussi la documentation technique fournie avec le RAD détaillera les différentes entités mises à disposition de l'utilisateur.


1.2 les contrôles sur un formulaire

Voici un exemple de fiche comportant 7 catégories de contrôles différents :



Il existe des contrôles qui sont des conteneurs visuels, les quatre classes ci-après sont les principales classe de conteneurs visuels de C# :

System.Windows.Forms.Form
System.Windows.Forms.Panel
System.Windows.Forms.GroupBox
System.Windows.Forms.TabPage

Un conteneur visuel permet à d'autres contrôles de s'afficher sur lui et lui communique par lien de parenté des valeurs de propriétés par défaut (police de caractères, couleur du fond,...). Un objet de chacune de ces classes de conteneurs visuels peut être le "parent" de n'importe quel contrôle grâce à sa propriété Parent qui est en lecture et écriture :

public Control Parent {get; set;}
C'est un objet de classe Control qui représente le conteneur visuel du contrôle.

Sur la fiche précédente nous relevons outre le formulaire lui-même, quatre conteneurs visuels répartis en trois catégories de conteneurs visuels :



Sur chaque conteneur visuel a été déposé un contrôle de classe System.Windows.Forms.Button qui a "hérité" par défaut des caractéristiques de police et de couleur de fond de son parent. Ci-dessous les classes de tous les contrôles déposés :



Le code C# engendré pour cette interface :

public class  WinForm  System .Windows.Forms.Form
{
 
/// <summary>
 /// Variable requise par le concepteur.
 /// </summary>
 
private  System .ComponentModel.Container components  null;
 private 
System .Windows.Forms.Panel panel1 ;
 private 
System .Windows.Forms.Label label1 ;
 private 
System .Windows.Forms.Label label2 ;
 private 
System .Windows.Forms.Panel panel2 ;
 private 
System .Windows.Forms.PictureBox pictureBox1 ;
 private 
System .Windows.Forms.Button button1 ;
 private 
System .Windows.Forms.TextBox textBox1 ;
 private 
System .Windows.Forms.Button button2 ;
 private 
System .Windows.Forms.GroupBox groupBox1 ;
 private 
System .Windows.Forms.TabControl tabControl1 ;
 private 
System .Windows.Forms.TabPage tabPage1 ;
 private 
System .Windows.Forms.TabPage tabPage2 ;
 private 
System .Windows.Forms.Button button3 ;
 private 
System .Windows.Forms.TextBox textBox2 ;
 private 
System .Windows.Forms.ListBox listBox1 ;
 private 
System .Windows.Forms.Button button4 ;
 private 
System .Windows.Forms.Button button5 ;
 private 
System .Windows.Forms.TextBox textBox3 ;
 private 
System .Windows.Forms.MainMenu mainMenu1 ;
 
 public 
WinForm ( ) {
  
//
  // Requis pour la gestion du concepteur Windows Form
  //
  
InitializeComponent ( );
  
  
//
  // TODO: Ajouter tout le code du constructeur après l'appel de InitializeComponent
  //
 
}
 
 
/// <summary>
 /// Nettoyage des ressources utilisées.
 /// </summary>
 
protected override void  Dispose  ( bool  disposing ) {
  if (
disposing )  {
   if (
components  !=  null)   {
    
components.Dispose ( );
   }
  }
  
base .Dispose ( disposing );
 }
 
 
#region Code généré par le concepteur Windows Form
private void  InitializeComponent ( )
{
System .Resources.ResourceManager resources  =
           new  System .Resources.ResourceManager ( typeof ( WinForm ));
  this
.panel1  new  System .Windows.Forms.Panel ( );
  this
.button2  new  System .Windows.Forms.Button ( );
  this
.textBox1  new  System .Windows.Forms.TextBox ( );
  this
.label2  new  System .Windows.Forms.Label ( );
  this
.label1  new  System .Windows.Forms.Label ( );
  this
.panel2  new  System .Windows.Forms.Panel ( );
  this
.button1  new  System .Windows.Forms.Button ( );
  this
.pictureBox1  new  System .Windows.Forms.PictureBox ( );
  this
.groupBox1  new  System .Windows.Forms.GroupBox ( );
  this
.textBox3  new  System .Windows.Forms.TextBox ( );
  this
.button5  new  System .Windows.Forms.Button ( );
  this
.tabControl1  new  System .Windows.Forms.TabControl ( );
  this
.tabPage1  new  System .Windows.Forms.TabPage ( );
  this
.textBox2  new  System .Windows.Forms.TextBox ( );
  this
.button3  new  System .Windows.Forms.Button ( );
  this
.tabPage2  new  System .Windows.Forms.TabPage ( );
  this
.button4  new  System .Windows.Forms.Button ( );
  this
.listBox1  new  System .Windows.Forms.ListBox ( );
  this
.mainMenu1  new  System .Windows.Forms.MainMenu ( );
  this
.panel1.SuspendLayout ( );
  this
.panel2.SuspendLayout ( );
  this
.groupBox1.SuspendLayout ( );
  this
.tabControl1.SuspendLayout ( );
  this
.tabPage1.SuspendLayout ( );
  this
.tabPage2.SuspendLayout ( );
  this
.SuspendLayout ( );
  // 
  // panel1
  //
 
this.panel1.BackColor  System .Drawing.Color.NavajoWhite ;
  this
.panel1.Controls.Add (this .button2 );
  this
.panel1.Controls.Add (this .textBox1 );
  this
.panel1.Controls.Add (this .label2 );
  this
.panel1.Location  new  System .Drawing.Point ( 32, 40 );
  this
.panel1.Name  "panel1";
  this
.panel1.Size  new  System .Drawing.Size ( 224, 104 );
  this
.panel1.TabIndex  0 ;
 
// 
  // button2
  //
  this.button2.Location  new  System .Drawing.Point ( 88, 24 );
  this
.button2.Name  "button2";
  this
.button2.Size  new  System .Drawing.Size ( 72, 24 );
  this
.button2.TabIndex  4 ;
  this
.button2.Text  "button2";
  //
  // textBox1
  //
 
this.textBox1.Location  new  System .Drawing.Point ( 16, 76 );
  this
.textBox1.Name  "textBox1";
  this
.textBox1.Size  new  System .Drawing.Size ( 192, 20 );
  this
.textBox1.TabIndex  3 ;
  this
.textBox1.Text  "textBox1";
 
//
  // label2
  //
 
this.label2.BackColor  System .Drawing.SystemColors.Info ;
  this
.label2.Location  new  System .Drawing.Point ( 16, 32 );
  this
.label2.Name  "label2";
  this
.label2.Size  new  System .Drawing.Size ( 88, 40 );
  this
.label2.TabIndex  2 ;
  this
.label2.Text  "label2";

  // toutes ces lignes correspondent à ceci :
 
  //
  // panel2
  //
 
this.panel2.BackColor  System .Drawing.Color.PaleTurquoise ;
  this
.panel2.Controls.Add (this .button1 );
  this
.panel2.Controls.Add (this .pictureBox1 );
  this
.panel2.Location  new  System .Drawing.Point ( 200, 16 );
  this
.panel2.Name  "panel2";
  this
.panel2.Size  new  System .Drawing.Size ( 208, 112 );
  this
.panel2.TabIndex  2 ;
 
//
  // button1
  //

  this
.button1.Location  new  System .Drawing.Point ( 24, 48 );
  this
.button1.Name  "button1";
  this
.button1.Size  new  System .Drawing.Size ( 72, 24 );
  this
.button1.TabIndex  4 ;
  this
.button1.Text  "button1";
 
//
  // pictureBox1
  //
 
this.pictureBox1.BackColor  System .Drawing.Color.DarkKhaki ;
  this
.pictureBox1.Image  (( System .Drawing.Image )( resources.GetObject ("pictureBox1.Image")));
 
// la ligne précédente charge l'image :
   
  this
.pictureBox1.Location  new  System .Drawing.Point ( 120, 32 );
  this
.pictureBox1.Name  "pictureBox1";
  this
.pictureBox1.Size  new  System .Drawing.Size ( 70, 63 );
  this
.pictureBox1.SizeMode  System .Windows.Forms.PictureBoxSizeMode.AutoSize ;
  this
.pictureBox1.TabIndex  0 ;
  this
.pictureBox1.TabStop  false ;

 
 // toutes ces lignes correspondent à ceci :
 
  //
  // groupBox1
  //
 
this.groupBox1.BackColor  System .Drawing.Color.SkyBlue ;
  this
.groupBox1.Controls.Add (this .textBox3 );
  this
.groupBox1.Controls.Add (this .button5 );
  this
.groupBox1.Location  new  System .Drawing.Point ( 8, 160 );
  this
.groupBox1.Name  "groupBox1";
  this
.groupBox1.Size  new  System .Drawing.Size ( 184, 104 );
  this
.groupBox1.TabIndex  4 ;
  this
.groupBox1.TabStop  false ;
  this
.groupBox1.Text  "groupBox1";
 
//
  // textBox3
  //
 
this.textBox3.Location  new  System .Drawing.Point ( 8, 72 );
  this
.textBox3.Name  "textBox3";
  this
.textBox3.Size  new  System .Drawing.Size ( 136, 20 );
  this
.textBox3.TabIndex  1 ;
  this
.textBox3.Text  "textBox3";
 
//
  // button5
  //
 
this.button5.Location  new  System .Drawing.Point ( 16, 32 );
  this
.button5.Name  "button5";
  this
.button5.Size  new  System .Drawing.Size ( 72, 24 );
  this
.button5.TabIndex  0 ;
  this
.button5.Text  "button5";

  // toutes ces lignes correspondent à ceci :
 
  //
  // tabControl1
  //
 
this.tabControl1.Controls.Add (this .tabPage1 );
  this
.tabControl1.Controls.Add (this .tabPage2 );
  this
.tabControl1.Location  new  System .Drawing.Point ( 224, 144 );
  this
.tabControl1.Name  "tabControl1";
  this
.tabControl1.SelectedIndex  0 ;
  this
.tabControl1.Size  new  System .Drawing.Size ( 200, 120 );
  this
.tabControl1.TabIndex  5 ;
 
//
  // tabPage1
  //
 
this.tabPage1.BackColor  System .Drawing.Color.DarkTurquoise ;
  this
.tabPage1.Controls.Add (this .textBox2 );
  this
.tabPage1.Controls.Add (this .button3 );
  this
.tabPage1.Location  new  System .Drawing.Point ( 4, 22 );
  this
.tabPage1.Name  "tabPage1";
  this
.tabPage1.Size  new  System .Drawing.Size ( 192, 94 );
  this
.tabPage1.TabIndex  0 ;
  this
.tabPage1.Text  "tabPage1";
 
//
  // textBox2
  //
 
this.textBox2.Location  new  System .Drawing.Point ( 16, 16 );
  this
.textBox2.Name  "textBox2";
  this
.textBox2.Size  new  System .Drawing.Size ( 160, 20 );
  this
.textBox2.TabIndex  1 ;
  this
.textBox2.Text  "textBox2";
 
//
  // button3
  //
  
this.button3.Location  new  System .Drawing.Point ( 32, 56 );
  this
.button3.Name  "button3";
  this
.button3.Size  new  System .Drawing.Size ( 128, 24 );
  this
.button3.TabIndex  0 ;
  this
.button3.Text  "button3";

  // toutes ces lignes correspondent à ceci :
 

  //
  // tabPage2
  //
 
this.tabPage2.BackColor  System .Drawing.Color.Turquoise ;
  this
.tabPage2.Controls.Add (this .button4 );
  this
.tabPage2.Controls.Add (this .listBox1 );
  this
.tabPage2.Location  new  System .Drawing.Point ( 4, 22 );
  this
.tabPage2.Name  "tabPage2";
  this
.tabPage2.Size  new  System .Drawing.Size ( 192, 94 );
  this
.tabPage2.TabIndex  1 ;
  this
.tabPage2.Text  "tabPage2";
 
// 
  // button4
  // 
  
this.button4.Location  new  System .Drawing.Point ( 8, 32 );
  this
.button4.Name  "button4";
  this
.button4.Size  new  System .Drawing.Size ( 64, 24 );
  this
.button4.TabIndex  1 ;
  this
.button4.Text  "button4";
 
//
  // listBox1
  //
 
this.listBox1.Location  new  System .Drawing.Point ( 80, 8 );
  this
.listBox1.Name  "listBox1";
  this
.listBox1.Size  new  System .Drawing.Size ( 96, 69 );
  this
.listBox1.TabIndex  0 ;

  // toutes ces lignes correspondent à ceci :
 
  // 
  // label1
  // 
 
this.label1.BackColor  System .Drawing.Color.MediumAquamarine ;
  this
.label1.Location  new  System .Drawing.Point ( 48, 8 );
  this
.label1.Name  "label1";
  this
.label1.Size  new  System .Drawing.Size ( 88, 16 );
  this
.label1.TabIndex  1 ;
  this
.label1.Text  "label1";
 
// les 6 lignes précédentes correspondent à ceci :

 
  // 
  // WinForm
  //
 
this.AutoScaleBaseSize  new  System .Drawing.Size ( 5, 13 );
  this
.ClientSize  new  System .Drawing.Size ( 432, 269 );
  this
.Controls.Add (this .tabControl1 );
  this
.Controls.Add (this .groupBox1 );
  this
.Controls.Add (this .panel2 );
  this
.Controls.Add (this .label1 );
  this
.Controls.Add (this .panel1 );
  this
.Menu  this .mainMenu1 ;
  this
.Name  "WinForm";
  this
.Text  "WinForm";
  this.panel1.ResumeLayout ( false );
  this
.panel2.ResumeLayout ( false );
  this
.groupBox1.ResumeLayout ( false );
  this
.tabControl1.ResumeLayout ( false );
  this
.tabPage1.ResumeLayout ( false );
  this
.tabPage2.ResumeLayout ( false );
  this
.ResumeLayout ( false );

  // toutes ces lignes correspondent à ceci :
 

}
 
#endregion


1.3 Influence de la propriété parent sur l'affichage visuel d'un contrôle


Dans l'IHM précédente, programmons par exemple la modification de la propriété Parent du contrôle textBox1 en réaction au click de souris sur les Button button1 et button2.

Il faut abonner le gestionnaire du click de button1 "private void  button1_Click" , au délégué button1.Click :
this.button1.Click += new System.EventHandler(this.button1_Click);

De même il faut abonner le gestionnaire du click de button2 "private void  button2_Click" , au délégué button2.Click :
this.button2.Click += new System.EventHandler(this.button2_Click);


Le RAD engendre automatiquement les gestionnaires:
private void  button1_Click ( object  sender,  System .EventArgs e ){  }
private void  button1_Click ( object  sender,  System .EventArgs e ){  }

Les lignes d'abonnement sont engendrées dans la méthode InitializeComponent ( ) :
private void  InitializeComponent ( )
{
....

this.button1.Click += new System.EventHandler(this.button1_Click);
this.button2.Click += new System.EventHandler(this.button2_Click);
}


Le code et les affichages obtenus (le textBox1 est positionné par construction en X=16 et Y=76 sur son parent)  :
// Gestionnaire du click sur button1 :

private void  button1_Click ( object  sender,  System .EventArgs e ){
  
textBox1.Parent  panel1 ;
}

Lorsque l'on clique sur le button1, texteBox1 a pour parent panel1 :
 


// Gestionnaire du click sur button2 :

private void  button2_Click ( object  sender,  System .EventArgs e ) {
  
textBox1.Parent  this;
}

Lorsque l'on clique sur le button2, texteBox1 a pour parent la fiche elle-même :


Le contrôle pictureBox1 permet d'afficher des images :  ico, bmp, gif, png, jpg, jpeg

// chargement d'un fichier image dans le pictureBox1 par un click sur button3 :

private void  InitializeComponent ( )
{
....

  this.button1.Click += new System.EventHandler(this.button1_Click);
  this.button2.Click += new System.EventHandler(this.button2_Click);
  this.button3.Click += new System.EventHandler(this.button3_Click);
}

private void 
button3_Click ( object  sender,  System .EventArgs e ) {
    pictureBox1.Image   Image.FromFile("csharp.jpg") ;
}

Lorsque l'on clique sur le button3, l'image "csharp.jpg" est chargée dans pictureBox1 :

    ----->  




1.4 Des graphiques dans les formulaires avec le GDI+

Nous avons remarqué que C# possède un contrôle permettant l'affichage d'images de différents formats, qu'en est-il de l'affichage de graphiques construits pendant l'exécution ? Le GDI+ répond à cette question.

Le Graphical Device Interface+ est la partie de NetFrameWork qui fournit les graphismes vectoriels à deux dimensions, les images et la typographie. GDI+ est une interface de périphérique graphique qui permet aux programmeurs d'écrire des applications indépendantes des périphériques physiques (écran, imprimante,...).

Lorsque l'on dessine avec GDI+, on utilise des  méthodes de classes situées dans le GDI+, donnant des directives de dessin, ce sont ces méthodes qui, via le CLR du NetFrameWork, font appel aux pilotes du périphérique physique, les programmes ainsi conçus ne dépendent alors pas du matériel sur lequel ils s'afficheront.

Pour dessiner des graphiques sur n'importe quel périphérique d'affichage, il faut un objet Graphics. Un objet de classe System.Drawing.Graphics est associé à une surface de dessin, généralement la zone cliente d'un formulaire (objet de classe Form). Il n'y a pas de constructeur dans la classe Graphics :



Comme le dessin doit avoir lieu sur la surface visuelle d'un objet visuel donc un contrôle, c'est cet objet visuel qui fournit le fond, le GDI+ fournit dans la classe System.Windows.Forms.Control la méthode CreateGraphics qui permet de créer un objet de type Graphics qui représente le "fond de dessin" du contrôle :



Syntaxe :
public
Graphics CreateGraphics( );

Afin de comprendre comment utiliser un objet Graphics construisons un exemple fictif de code dans lequel on suppose avoir instancié ObjVisuel un contrôle (par exemple : une fiche, un panel,...), on utilise deux méthodes dessin de la classe Graphics pour dessiner un trait et un rectangle :



Code C#
Explication
Graphics fond = ObjVisuel.CreateGraphics ( );
Obtention d'un fond de dessin sur ObjVisuel (création d'un objet Graphics associé à ObjVisuel)
Pen blackPen = new Pen ( Color.Black, 2 );
Création d'un objet de pinceau de couleur noire et d'épaisseur 2 pixels
fond.DrawLine ( blackPen, 10f, 10f, 50f, 50f );
Utilisation du pinceau blackPen pour tracer une ligne droite sur le fond d'ObjVisuel entre les deux points A(10,10) et B(50,50).

fond.FillRectangle ( Brushes.SeaGreen,5,70,100,50 );
Utilisation d'une couleur de brosse SeaGreen, pour remplr l'intérieur d'un rectangle spécifié par une paire de coordonnées (5,70), une largeur(100 pixels) et une hauteur (50 pixels).

Ces quatre instructions ont permis de dessiner le trait noir et le rectangle vert sur le fond du contrôle ObjVisuel représenté ci-dessous par un rectangle à fond blanc :



Note technique de Microsoft
L'objet Graphics retourné doit être supprimé par l'intermédiaire d'un appel à sa méthode Dispose lorsqu'il n'est plus nécessaire.

La classe Graphics implémente l'interface IDisposable :

public sealed class Graphics : MarshalByRefObject, IDisposable

Les objets Graphics peuvent donc être libérés par la méthode Dispose( ).

Afin de respecter ce conseil d'optimisation de gestion de la mémoire, nous rajoutons dans notre code l'appel à la méthode Dispose de l'objet  Graphics. Nous prenons comme ObjVisuel  un contrôle de type panel que nous nommons panelDessin  ( private System.Windows.Forms.Panel panelDessin ):

//...
Graphics fond = panelDessin.CreateGraphics ( );
Pen blackPen = new Pen (  Color.Black, 2 );
fond.DrawLine (  blackPen, 10f, 10f, 50f, 50f );
fond.FillRectangle  (  Brushes.SeaGreen,5,70,100,50  );
fond.Dispose( );
//...suite du code où l'objet fond n'est plus utilisé

Nous pouvons aussi utiliser l'instruction using déjà vue (cf. Fenêtres et ressources ch3.3) qui libère automatiquement l'objet par appel à sa méthode Dispose :
//...
using( Graphics fond = panelDessin.CreateGraphics ( ) ) {
  Pen blackPen = new Pen (  Color.Black, 2 );
  fond.DrawLine (  blackPen, 10f, 10f, 50f, 50f );
  fond.FillRectangle  (  Brushes.SeaGreen,5,70,100,50  );
}
//...suite du code où l'objet fond n'est plus utilisé



1.5 Le dessin doit être persistant

Reprenons le dessin précédent et affichons-le à la demande.

Nous supposons disposer d'un formulaire nommé WinForm1 contenant un panneau nommé panelDessin  :
( private System.Windows.Forms.Panel panelDessin )
et un bouton nommé buttonDessin :
( private System.Windows.Forms.Button buttonDessin )
 


Nous programmons un gestionnaire de l'événement click du buttonDessin, dans lequel nous copions le code de traçage de notre dessin :
 
private void buttonDessin_Click(object sender, System.EventArgs e)     {
   Graphics fond = panelDessin.CreateGraphics ( );
   Pen blackPen = new Pen (  Color.Black, 2 );
   fond.DrawLine (  blackPen, 10f, 10f, 50f, 50f );
   fond.FillRectangle  (  Brushes.SeaGreen,5,70,100,50  );
   fond.Dispose( );
}

Lorsque nous cliquons sur le bouton  buttonDessin, le trait noir et le rectangle vert se dessinent sur le fond du panelDessin  :


Faisons apparaître une fenêtre de bloc-note contenant du texte, qui masque partiellement notre formulaire WinForm1 qui passe au second plan comme ci-dessous :


Si nous nous refocalisons sur le formulaire en cliquant sur lui par exemple, celui-ci repasse au premier plan, nous constatons que notre dessin est abîmé. Le rectangle vert est amputé de la partie qui était recouverte par la fenêtre de bloc-note. Le formulaire s'est bien redessiné, mais pas nos traçés ;



Il faut cliquer une nouvelle fois sur le bouton pour lancer le redessinement des tracés :


Il existe un moyen simple permettant d'effectuer le redessinement de nos traçés lorsque le formulaire se redessine lui-même automatiquement : il nous faut "consommer" l'événement  Paint du formulaire qui se produit lorsque le formulaire est redessiné (ceci est d'ailleurs valable pour n'importe quel contrôle). La consommation de l'événement Paint s'effectue grâce au gestionnaire Paint de notre formulaire WinForm1 :

private void WinForm1_Paint (object sender, System.Windows.Forms.PaintEventArgs e)   {
  Graphics fond = panelDessin.CreateGraphics ( );
  Pen blackPen = new Pen (  Color.Black, 2 );
  fond.DrawLine (  blackPen, 10f, 10f, 50f, 50f );
  fond.FillRectangle  (  Brushes.SeaGreen,5,70,100,50  );
  fond.Dispose( );
}

Le RAD a enregistré (abonné) le gestionnaire WinForm1_Paint auprès du délégué Paint dans le corps de laméthode InitializeComponent :

private void InitializeComponent( )   {
  ...
  this.Paint += new System.Windows.Forms.PaintEventHandler ( this.WinForm1_Paint );
  ...
}


1.6 Deux exemples de graphiques sur plusieurs contrôles

Premier exemple : une méthode générale pour tout redessiner.

Il est possible de dessiner sur tous les types de conteneurs visuels ci-dessous un formulaire nommé WinForm et deux panel (panel2 : jaune foncé et panel1 : violet), la label1 ne sert qu'à afficher du texte en sortie  :


Nous écrivons une méthode TracerDessin  permettant de dessiner sur le fond de la fiche, sur le fond des deux panel et d'écrire du texte sur le fond d'un panel. La méthode TracerDessin  est appelée dans le gestionnaire de l'événement Paint du formulaire lors de son  redessinement de la fiche afin d'assurer la persistance de tous les traçés. Voici le code que nous créons :

private void InitializeComponent( )   {
  ...
  this.Paint += new System.Windows.Forms.PaintEventHandler ( this.WinForm_Paint );
  ...
}

private void WinForm_Paint (object sender, System.Windows.Forms.PaintEventArgs e)   {

            TracerDessin  ( e.Graphics  ) ;
/*  Explications sur l'appel de la méthode TracerDessin :
Le paramètre e de type PaintEventArgs contient les données relatives à l'événement Paint
en particulier une propriété Graphics qui renvoie le graphique utilisé pour peindre sur la fiche
c'est pourquoi e.Graphics est passé comme fond en paramètre à notre méthode de dessin.
*/

}

private void  TracerDessin ( Graphics x  ){
 
string  Hdcontext ;
 
Hdcontext x.GetType ( ) .ToString ( );
 
label1.Text = "Graphics x : " + Hdcontext.ToString ( );
 
x.FillRectangle ( Brushes.SeaGreen,5,70,100,50 );

 using(
Graphics g  this .CreateGraphics ( ) )  {
  
g.FillRectangle ( Brushes.SkyBlue,5,10,100,50 );
 }
using( Graphics g  panel1.CreateGraphics ( ) ) {
  
g.FillEllipse ( Brushes.MediumSlateBlue,5,10,40,30 );
 }
using( Graphics h  panel2.CreateGraphics ( ) ) {
  
h.FillEllipse ( Brushes.Tomato,10,15,120,80 );
  
h.DrawString ("Bonjour" , new  Font (this .Font.FontFamily.Name,18 ) ,Brushes.Lime,15,25 );
 }
}


L'exécution de code produit l'affichage suivant :





Illustrons les actions de dessin de chaque ligne
de code de la méthode
TracerDessin :


private void 
 TracerDessin ( Graphics x  ) {



On dessine deux rectangles sur le fond de la fiche, nous notons que ces deux rectangles ont une intersection non vide avec le panel2 (jaune foncé)et que cela n'altère pas le dessin du panel. En effet le panel est un contrôle et donc se redessine lui-même. Nous en déduisons que le fond graphique est situé "en dessous" du dessin des contrôles.



L'instruction dessine l'ellipse bleue sur le fond du panel1


La première instruction dessine l'ellipse rouge, la seconde écrit le texte "Bonjour" en vert sur le fond du panel2.

 
Deuxième exemple :  traçé des dessins sur des événements spécifiques

Le deuxième exemple montre que l'on peut dessiner aussi sur le fond d'autres contrôles différents des contrôles conteneurs; nous dessinons deux rectangles vert et bleu sur le fond du formulaire et un petit rectangle bleu ciel dans un ListBox, un TextBox et Button déposés sur le formulaire :



Les graphiques sont gérés dans le code ci-après :

namespace ProjWindowsApplication2
{
    /// <summary>
    /// Description Résumé de WinForm1.
    /// </summary>

    public class WinForm1 : System.Windows.Forms.Form
    {
        /// <summary>
        /// Variable requise par le concepteur.
        /// </summary>

        private System.ComponentModel.Container components = null ;
        private System.Windows.Forms.ListBox listBox1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.Button button1;

        private WinForm1( )
        {
            //
            // Requis pour la gestion du concepteur Windows Form
            //

            InitializeComponent();
            //
            // TODO: Ajouter tout le code du constructeur après l'appel de InitializeComponent
            //

        }
        /// <summary>
        /// Nettoyage des ressources utilisées.
        /// </summary>

        protected override void Dispose (bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        #region Code généré par le concepteur Windows Form
        /// <summary>
        /// Méthode requise pour la gestion du concepteur - ne pas modifier
        /// le contenu de cette méthode avec l'éditeur de code.
        /// </summary>

        private void InitializeComponent( )
        {
            this.listBox1 = new System.Windows.Forms.ListBox( );
            this.textBox1 = new System.Windows.Forms.TextBox( );
            this.button1 = new System.Windows.Forms.Button( );
            this.SuspendLayout( );
            //
            // listBox1
            //

            this.listBox1.Items.AddRange ( new object[] {
                        "zéro",
                        "un",
                        "deux",
                        "trois",
                        "quatre",
                        "cinq",
                        "FIN"
}  );
            this.listBox1.Location = new System.Drawing.Point(224, 8);
            this.listBox1.Name = "listBox1";
            this.listBox1.Size = new System.Drawing.Size(144, 95);
            this.listBox1.TabIndex = 9;
            this.listBox1.SelectedIndexChanged += new System.EventHandler ( this.listBox1_SelectedIndexChanged );
            //
            // textBox1
            //

            this.textBox1.Location = new System.Drawing.Point(8, 72);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(200, 20);
            this.textBox1.TabIndex = 8;
            this.textBox1.Text = "textBox1";
            this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
            //
            // button1
            //

            this.button1.Location = new System.Drawing.Point(24, 24);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(144, 32);
            this.button1.TabIndex = 7;
            this.button1.Text = "button1";
            this.button1.Paint += new System.Windows.Forms.PaintEventHandler(this.button1_Paint);
            //
            // WinForm1
            //

            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(384, 117);
            this.Controls.Add(this.listBox1);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button1);
            this.Name = "WinForm1";
            this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
            this.Text = "WinForm1";
            this.Paint += new System.Windows.Forms.PaintEventHandler(this.WinForm1_Paint);
            this.ResumeLayout(false);
        }
        #endregion

        //-- dessin persistant sur le fond de la fiche par gestionnaire Paint:       
        private void WinForm1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
              Graphics g  = e.Graphics;
              g.FillRectangle  (  Brushes.SeaGreen,5,70,100,50  );
              g.FillRectangle ( Brushes.SkyBlue,5,10,100,50 );
        }
        //-- dessin persistant sur le fond du bouton par gestionnaire Paint:   
        private void button1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics x = e.Graphics;
            x.FillRectangle ( Brushes.SkyBlue,5,5,20,20 );
        }
        //-- dessin non persistant sur le fond du ListBox par gestionnaire SelectedIndexChange :   
        private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
        {
           Rectangle Rect = listBox1.GetItemRectangle(listBox1.SelectedIndex);
           int Haut = listBox1.ItemHeight;
           textBox1.Text= "x="+Rect.X.ToString()+" y="+Rect.Y.ToString()+" h="+Haut.ToString();
           using ( Graphics k = listBox1.CreateGraphics( ) )
           {
               k.FillRectangle(Brushes.SkyBlue,Rect.X,Rect.Y,Haut,Haut);
            }
        }
        //-- dessin non persistant sur le fond du TextBox par gestionnaire TextChanged :   
        private void textBox1_TextChanged(object sender, System.EventArgs e)
        {
          using (Graphics k = textBox1.CreateGraphics( ) )
          {
               k.FillRectangle(Brushes.SkyBlue,5,5,10,10);
          }
        }
    }
}
Après exécution, sélection de la 3ème ligne de la liste et ajout d'un texte au clavier dans le TextBox :