Interactions entre Form en .Net (C# et VB.Net)

Il peut arriver lors du développement d'un application d'avoir besoin de plusieurs formulaires.
On prendra l'exemple d'un agenda, sur le formulaire principal (frmMain), on a une grille qui représente des créneaux horaires répartis sur une semaine. Le second formulaire (frmRDV) présentera les détails d'un rendez vous.
On ne s'attardera pas à l'ensemble des contrôles de ces deux formulaires pour ce tutoriel, il faut juste penser que frmRDV s'ouvrira lors d'un click sur un créneau horaire.

La méthode Show

La façon « basique » d'afficher frmRDV est de créer une instance de ce formulaire et de l'afficher avec la méthode Show

Code C#

        private void crenauHoraire_Click(object sender, EventArgs e)
        {
            frmRDV frm = new frmRDV();
            frm.Show();
        }

ET encore plus simplement en VB.
Code VB.Net

  Private Sub crenauHoraire_Click(ByVal sender As Object, ByVal e As EventArgs)
   frmRDV.Show()
  End Sub

Mais là zéro interaction, chaque formulaire vie sa vie indépendamment. Comment savoir dans frmRDV quelle est l'heure de début du créneaux, celle de fin, s'il y a déjà un rendez vous etc... Et de même une fois les détails créés, modifiés ou supprimés comment frmMain saura si le créneau doit être affiché occupé ou non?

La méthode ShowDialog

Cette méthode rend frmRDV modale, c'est à dire que l'utilisateur devra fermer ce formulaire pour accéder à nouveau à frmMain.
Code C#

        private void crenauHoraire_Click(object sender, EventArgs e)
        {
            frmRDV frm = new frmRDV();
            frm.ShowDialog(this);
        }

Code VB.Net

  Private Sub crenauHoraire_Click(ByVal sender As Object, ByVal e As EventArgs)
   Dim frm As New frmRDV()
   frm.ShowDialog(Me)
  End Sub

Il y a un début d'interaction, car on bloque frmMain pendant l'affichage de frmRDV, mais toujours pas de transfert de données.

L'InputBox

Le contrôle InputBox tel qu'il existe en VB6 (par exemple) n'apparait pas dans l'arsenal des contrôles .Net.
Cependant il ne s'agit ni plus ni moins que d'un formulaire modal qui retourne une information à un autre.
Admettons que l'on veuille récupérer un string, ajoutons une méthode publique de type string à frmRDV, cette méthode affichera le formulaire en mode modal et retournera le contenu d'un textbox une fois la fermeture de celui-ci.
Code C#

        public string InputBox()
        {
            this.ShowDialog();//affiche le formulaire en mode modal, l'exécution du code de cette méthode est bloquée à cette ligne tant que le formulaire est affiché
            return leTextBox.Text;//retourne le contenu du TextBox une fois que le formulaire est fermé
        }

Code VB.Net

  Public Function InputBox() As String
   Me.ShowDialog() 'affiche le formulaire en mode modal, l'exécution du code de cette méthode est bloquée à cette ligne tant que le formulaire est affiché
   Return leTextBox.Text 'retourne le contenu du TextBox une fois que le formulaire est fermé

Nous avons enfin un transfert de données, mais dans un seul sens, d'un seul type et uniquement à la fermeture de frmRDV.

Avec une classe DetailRDV, on peut donc retourner à frmMain toutes les informations nécessaires à l'enregistrement d'un rendez-vous. Cependant, il n'est possible ainsi de transférer des informations dans l'autre sens.

L'InputBox bidirectionnel

Puisque nous avons transformé frmRDV en InputBox à l'aide d'une méthode dédiée, passer des paramètres à cette méthode est une solution pour transférer des données de frmMain à frmRDV.

Code C#

        private DetailRDV rdvEnCours;
        public DetailRDV InputBox(DetailRDV RdvAafficher)
        {
            rdvEnCours = RdvAafficher;
            //Ecrire ici le code pour l'affichage et éventuellement le traitement du rendez vous à afficher
            this.ShowDialog();
            return rdvEnCours;//retourne le rendez vous en cours, dans l'état ou il est à la fermeture du formulaire
        }

Code VB.Net

  Private rdvEnCours As DetailRDV

  Public Function InputBox(ByVal RdvAafficher As DetailRDV) As DetailRDV
   rdvEnCours = RdvAafficher
   'Ecrire ici le code pour l'affichage et éventuellement le traitement du rendez vous à afficher
   Me.ShowDialog()
   Return rdvEnCours 'retourne le rendez vous en cours, dans l'état ou il est à la fermeture du formulaire
  End Function

Ainsi nos avons enfin un échange entre les deux formulaire, restent deux inconvénients:
- L'ouverture de l'un bloque l'exécution de l'autre;
- Le transfert d'informations retour ne peut se faire qu'à la fermeture du second formulaire.

Passage de paramètres dans le constructeur du formulaire et génération d'évènements

Si notre formulaire frmRDV possède un constructeur avec paramètres et s'il peut générer des événements publiques à différentes phases de la saisie (par exemple un événement quand l'heure de début est choisie ou modifiée, idem pour l'heure de fin et un pour la validation finale des autres détails), alors on va pouvoir passer nos paramètres à frmRDV à l'instanciation de celui-ci et abonner frmMain aux différents événements existants.

Bidou a écrit une source en C# qui est un parfait exemple de ce principe.
Pour le lecteur qui coderait en VB, il existe des traducteurs en lignes gratuits (mots clés : « convert C# VB.Net » dans son moteur de recherche favori). Mais avec un peu de gymnastique intellectuelle c'est, je pense, à la portée de chacun de comprendre le code.

Binding et PropertyChanged

J'ai déjà abordé des notions de Binding ici. Dans un même formulaire ça marche tout seul.
Entre deux formulaires il faut que la classe utilisée implémente INotifyPropertyChanged.
Code C#

using System;
using System.ComponentModel;

namespace test
{
    public class DetailRDV:INotifyPropertyChanged
    {

        private DateTime debut;

        private DateTime fin;

        private string titre;


        public DateTime Debut
        {
            get { return debut; }
            set
            {
                debut = value;
                GenerereLaNotification("Debut");
            }
        }

        public DateTime Fin
        {
            get { return fin; }
            set
            {
                fin = value;
                GenerereLaNotification("Fin");
            }
        }

        public string Titre
        {
            get { return titre; }
            set
            {
                titre = value;
                GenerereLaNotification("Titre");
            }
        }


        #region INotifyPropertyChanged Membres

        public event PropertyChangedEventHandler PropertyChanged;

        private void GenerereLaNotification(string NomProp)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(NomProp));
        }

        #endregion
    }
}

Code VB.Net

Imports System
Imports System.ComponentModel

Public Class DetailRDV
    Implements INotifyPropertyChanged

    Private pdebut As Date
    Private pfin As Date
    Private ptitre As String


    Public Property Debut() As Date
        Get
            Return pdebut
        End Get
        Set(ByVal value As Date)
            pdebut = value
            GenerereLaNotification("Debut")
        End Set
    End Property

    Public Property Fin() As Date
        Get
            Return pfin
        End Get
        Set(ByVal value As Date)
            pfin = value
            GenerereLaNotification("Fin")
        End Set
    End Property

    Public Property Titre() As String
        Get
            Return ptitre
        End Get
        Set(ByVal value As String)
            ptitre = value
            GenerereLaNotification("Titre")
        End Set
    End Property


#Region "INotifyPropertyChanged Membres"

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private Sub GenerereLaNotification(ByVal NomProp As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NomProp))
    End Sub

#End Region
End Class

Commençons par poser un DataGridView sur frmMain bindé sur notre classe detailRDV.
Ajoutons un bouton pour afficher frmRDV.

Puis sur frmRDV, 3 Texbox eux aussi bindés sur detailRDV.

Dans frmMain, initialisons les données et le binding, et dans le bouton écrivons le code pour afficher le premier enregistrement.
Code C#

        List<DetailRDV> rdvs = new List<DetailRDV>();

        public frmMain()
        {
            InitializeComponent();

            rdvs.Add(new DetailRDV { Debut = DateTime.Parse("11:30:00"), Fin = DateTime.Parse("12:30:00"), Titre = "Apéro" });
            rdvs.Add(new DetailRDV { Debut = DateTime.Parse("12:30:00"), Fin = DateTime.Parse("14:00:00"), Titre = "Repas" });
            rdvs.Add(new DetailRDV { Debut = DateTime.Parse("14:00:00"), Fin = DateTime.Parse("16:00:00"), Titre = "Sieste" });

            detailRDVBindingSource.DataSource = rdvs;

            
        }

        private void button1_Click(object sender, EventArgs e)
        {
            frmRDV frm = new frmRDV(rdvs[0]);
            frm.Show();
        }

Code VB.Net

Public Class frmMain

    Private rdvs As New List(Of DetailRDV)()

    Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        rdvs.Add(New DetailRDV With {.Debut = Date.Parse("11:30:00"), .Fin = Date.Parse("12:30:00"), .Titre = "Apéro"})
        rdvs.Add(New DetailRDV With {.Debut = Date.Parse("12:30:00"), .Fin = Date.Parse("14:00:00"), .Titre = "Repas"})
        rdvs.Add(New DetailRDV With {.Debut = Date.Parse("14:00:00"), .Fin = Date.Parse("16:00:00"), .Titre = "Sieste"})

        DetailRDVBindingSource.DataSource = rdvs

    End Sub

    Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
        Dim frm As New frmRDV(rdvs(0))
        frm.Show()
    End Sub
End Class

Dans frmRDV, il faut écrire un constructeur pour passer le rendez-vous et initialiser le binding.
Code C#

    public partial class frmRDV : Form
    {
        public frmRDV(DetailRDV Rdv)
        {
            InitializeComponent();

            detailRDVBindingSource.DataSource = Rdv;
        }

     }

Code VB.Net

Public Class frmRDV
    Public Sub New(ByVal Rdv As DetailRDV)
        InitializeComponent()

        DetailRDVBindingSource.DataSource = Rdv
    End Sub
End Class

Si l'on modifie une valeur, dans l'un ou l'autre des formulaires, dès que la modification est validée, c'est à dire que le focus passe à un autre contrôle ou un autre cellule, la valeur affichée change aussi dans l'autre formulaire.
Valeurs initiales:

Valeurs modifiées:

Et en cours de frappe?
Et bien c'est possible aussi avec les TextBox. Revenons aux propriétés d'un TextBox de frmRDV.

Dans (DataBindings) cliquons sur le bouton ... dans (Avancées).

Dans la liste déroulante de droite choisissons OnPropertyChanged.
Maintenant, lors de la frappe dans ce TextBox, la valeur affichée est aussitôt modifiée dans le DataGridView.

Source exemple à télécharger ici
https://codes-sources.commentcamarche.net/source/browse/102741/InteractionsEntreFormCS

Ce document intitulé « Interactions entre Form en .Net (C# et VB.Net) » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous