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 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?
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.
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.
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.
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.
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