IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Delphi Discussion :

XE7 : Form - Création et passage de variables


Sujet :

Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité
    Invité(e)
    Par défaut XE7 : Form - Création et passage de variables
    Bonjour,

    Pour transmettre une variable d'une Form à une autre, j'utilise une méthode assez classique.
    Exemple sur 3 Forms : Unit1 -> showmodal -> Unit2 -> showmodal -> Unit3
    Code du projet et des 3 Forms
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    program Project1;
     
    uses
      Vcl.Forms,
      Unit1 in 'Unit1.pas' {Form1},
      Unit2 in 'Unit2.pas' {Form2},
      Unit3 in 'Unit3.pas' {Form3};
     
    {$R *.res}
     
    begin
      Application.Initialize;
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TForm1, Form1);
      Application.CreateForm(TForm2, Form2);
      Application.CreateForm(TForm3, Form3);
     
      Application.Run;
    end.
     
    unit Unit1;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, unit2;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Déclarations privées }
        FString : string;
      public
        { Déclarations publiques }
        Property prString : string read FString;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      FString := 'Test';
      with Form2 do
         try
           ShowModal;
          finally
            Free;
          end;
    end;
     
    unit Unit2;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, unit3;
     
    type
      TForm2 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
     
      private
        { Déclarations privées }
        FString : string;
      public
        { Déclarations publiques }
        Property prString : string read FString;
      end;
     
    var
      Form2: TForm2;
     
    implementation
     
    {$R *.dfm}
    uses Unit1;
     
     
    procedure TForm2.Button1Click(Sender: TObject);
    begin
      FString := Form1.prString;
      with Form3 do
        try
          ShowModal;
        finally
          Free;
        end;
    end;
     
    end.
     
    unit Unit3;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
     
    type
      TForm3 = class(TForm)
        procedure FormShow(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      Form3: TForm3;
     
    implementation
     
    {$R *.dfm}
    uses Unit2;
     
    procedure TForm3.FormShow(Sender: TObject);
    begin
      showmessage(Form2.prString);
    end;
     
    end.
    Le code ci-dessus est tout à fait fonctionnel.


    Maintenant, je modifie le code de l'Unit1 ainsi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      FString := 'Test';
     
      if Assigned(Form2) then FreeAndNil(Form2);
      if not Assigned(Form2) then begin
        with TForm2.Create(Application) do
          try
            ShowModal;
          finally
            Free;
          end;
       end;
    end;
    La Form2 apparaît bien (i.e. sans erreur) et je peux obtenir Form1.prString (par un showmessage par exemple).
    Mais l'appel de Form3 à partir de Form2 entraîne une violation d'accès... au moment de lire Form2.prString.

    Dans Form1, c'est visiblement la création avec with TForm2.Create(nil) qui pose problème... lors du passage de la variable de Form2 vers Form3. Pourquoi ? Le nil ? Qu'est-ce qui est rompu et pas recréé (un pointeur ?).

    Je crois que je vais essayer une autre méthode de gestion des Forms. La désassignation me semble très périlleuse et visiblement je n'en comprends pas son mécanisme... ou ma perception (ce à quoi elle sert exactement) en est très erronée. Si un spécialiste passe par là...
    Merci.
    Dernière modification par Invité ; 30/11/2014 à 20h20.

  2. #2
    Membre Expert
    Avatar de Barbibulle
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    2 052
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 2 052
    Par défaut
    C'est normal Form2 est libéré et est mis à nil...

  3. #3
    Membre Expert
    Avatar de Barbibulle
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    2 052
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 2 052
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      FString := 'Test';
     
      if Assigned(Form2) then FreeAndNil(Form2);
      if not Assigned(Form2) then begin
        Form2 := TForm2.Create(Application);
      with Form2 do
        try
          ShowModal;
        finally
          Free;
        end;
      end;
    end;
    Je ne comprend pas la logique de vos passage de parametre. Ce que vous faites ne fonctionne pas, Form3 ne recoit pas de parametre (un oubli dans l'exemple ?) mais vous devriez faire autrement.

    Personnellement je préfère envoyer les paramètres lors de l'initialisation de ma fenetre. Ainsi, la fenêtre créée est autonome (ca va fonctionner même si on est pas en showmodal).

    Soit en redéfinissant le constructeur de la form pour passer tous les éléments necessaire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Form2 := TForm2.Create(Application,MonParam);
    soit apres le
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Form2 := TForm2.Create(Application);
    j'affecte les propriétés de Form2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Form2.Param1 := monParam;
    Form2.Showmodal;
    Ainsi Form2 'vie sa vie' sans avoir besoin de form1.

  4. #4
    Expert éminent
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 56
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    tout à fait, d'ailleurs il n'est pas nécessaire de le vérifier dans ton code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    if Assigned(Form2) then FreeAndNil(Form2);
      if not Assigned(Form2) then begin // c'est toujours vrai !
    c'est là que tu dois considérer si Form1 2 et 3 sont des singletons, et donc une seule variable globale peut permettre des les identifier, ou si tu préfères un code "plus propre" du type

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
          with TForm2.Create(Application) do
          try
            prString := Self.FString; // passage de Form1.FString à Form2.FSting sans passer par des variables globales (prString doit avoir un setter)
            ShowModal;
          finally
            Free;
          end;
    ou si Form2 a besoin de plus d'infos dans Form1

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
          with TForm2.Create(Application) do
          try
            SetForm1(Self); // passage de l'instance de TForm1 à l'instance de TForm2 sans passer par des variables globales
            ShowModal;
          finally
            Free;
          end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  5. #5
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Alors je reprends : Une TForm (une classe) me permet d'afficher les bulletins scolaires. Je veux pouvoir afficher simultanément plusieurs instances de la classe autrement dit visualer plusieurs bulletins à l'écran. Chaque TForm d'ailleurs permet d'ouvrir d'autres Forms... Pour l'instant, j'ai un code fonctionnel mais trop peu rigoureux à mon goût. Je sais que les programmeurs professionnels assujettis à la contrainte de productivité seraient pour la plupart tentés de me dire : "pourquoi se casser la tête puisque cela fonctionne ?". Mais ce n'est pas mon attitude... car pas ma contrainte.

    Donc j'utilise un code de modélisation du problème que j'ai précisé ci-dessus.

    Dans l'ordre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    if Assigned(Form2) then FreeAndNil(Form2);
      if not Assigned(Form2) then begin // c'est toujours vrai !
    ... Oui, enfin cela ne l'était pas au départ chez moi car je "désassignais" mal [Close + Destroy] donc je vérifiais.Il s'agit donc d'une redondance mais sans aucun impact sur le problème. C'est curieux. Je me suis penché sur pas mal d'objets mais jamais sur les TForms. Je crois que c'est un des rares objets que j'utilise que je n'ai pas dérivé.

    Je ne comprend pas la logique de vos passage de parametre. Ce que vous faites ne fonctionne pas, Form3 ne recoit pas de parametre (un oubli dans l'exemple ?) mais vous devriez faire autrement.
    ... Si, si... le code initial (le premier code fourni) est fait pour cela.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    //TForm3
    implementation
    uses Form2;
    procedure TForm3.FormShow(Sender: TObject);
    begin
      showmessage(Form2.prString);
    end;
    TForm3 ne reçoit pas de paramètre mais en appelle un... Il va se "servir" et pour cela, il faut que le paramètre soit correctement référencé... Or, il ne l'est pas. Et d'ailleurs comme je l'ai écrit, il est totalement fonctionnel si je ne désassigne pas Form2. Le problème est le même si je remplace (nil) par (Application) lors de la création de la Form. Normalement, "Form2" est instanciée par défaut avec l'écriture utilisée.

    Donc, je cherche à comprendre pourquoi cela ne fonctionne pas. Une gestion plus fine des exceptions ne me fournit rien... en réalité n'est jamais déclenchée. A priori (et testé de multiples façons), le code avec désassignation est fonctionnel sans l'échange de données. L'écriture me semble correcte et en fait elle ne l'est pas. Mais je ne sais pas pourquoi. J'ai compris : il faut faire autrement. On pratiquera le mimétisme comme approche. Maiscela n'empêche pas de comprendre pourquoi le code est erroné ?

    Finalement la question semble se résumer à cela : "Pourquoi la propriété publique d'une Form2 ouverte avec (nil) n'est pas appelable avec une Form3 dans laquelle est référencée correctement (sur le papier mais pas dans la réalité du fonctionnement de Delphi) Form2 ? Qu'est-ce qui justifie cette impossibilité ?"

    Merci.
    Dernière modification par Invité ; 01/12/2014 à 09h50.

  6. #6
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 937
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 937
    Par défaut
    Ton erreur vient du fait que tu penses que l'instance de TForm2 créée va automatiquement être assignée à la variable Form2, ce qui n'est pas le cas. Delphi (le RAD) crée automatiquement une structure de base ET un variable de ce type mais il n'y a pas d'autre automatisme. Il faut obligatoirement que les deux soient liés par code, soit par Application.CreateForm(TForm2, Form2), soit par Form2 := TForm2.Create.

  7. #7
    Invité
    Invité(e)
    Par défaut
    Bonjour Andnotor,

    Oui, oui... En effet !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    if Assigned(Form2) then FreeAndNil(Form2);
      if not Assigned(Form2) then begin
       with TForm2.Create(nil) do
          if Assigned(Form2) then showmessage('Assignée') else showmessage('Non assignée');
      [...]
    ... renvoie "Non assignée"....
    Je pourrais peut-être profiter de cet "inconvénient"... Est-il possible de récupérer le nom de l'instanciation, l'appellation remplaçant Form2 ?

    Bonjour Barbibulle,

    Non, non, on ne complique pas les choses . Créer un pool de TForm, c'est OK... Enfin, non... La programmation n'est pas encore dynamique et c'est ce à quoi je m'attelle. Mais pour faire cela, je dois comprendre plus finement le comportement des TForm... Et j'avais la réponse sous les yeux... Form2 n'existe pas... Voilà l'erreur. J'ai pensé que de manière implicite Form2 était affectée lors de la création. sinon, ma nouvelle approche devrait être de ce type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    interface
     
    uses
      [...], unit2;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Déclarations privées }
        FString : string;
      public
        { Déclarations publiques }
        Forms2Array : array of TForm2;
     
        Property prString : string read FString;
     
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
       i : integer;
    begin
      SetLength(Forms2Array, Length(Forms2Array)+1);
      i := Length(Forms2Array)-1;
      FString := IntToStr(i);
      Forms2Array[i] := TForm2.Create(Self);
      Forms2Array[i].Show;[...]
    end;
    Pour ne pas allonger le tableau de manière idiote, je compte désassigner les Form2x qui ne servent plus... et réassigner les "trous". Donc de toute façon, le problème évoqué ne sera pas rencontré puisque le nom de l'instanciation est explicite. Et finalement ma simulation dans le cas précis est inutile... sauf que je ne comprenais pas le problème. La réponse d'Andnotor est finalement la mise au point nécessaire.

    Donc résolu... Merci à tous.

    ADD : Version avec récupération des trous
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    procedure TForm1.Button1Click(Sender: TObject);
    var
       arrayCount, i, iFree : integer;
    begin
      iFree := -1;
      arrayCount := Length(Forms2Array);
      if arrayCount > 0 then
        for i:= 0 to arrayCount -1 do
         if not Assigned(Forms2Array[i]) then begin
           iFree := i;
           break;
         end;
      if iFree = -1 then begin
        SetLength(Forms2Array, Length(Forms2Array)+1);
        iFree := Length(Forms2Array)-1;
      end;
     
    {Simulation d'une fenêtre détruite}
      if IFree = 2 then  begin
        //Forms2Array[1].Close;          //<- Tjs en suspens : OK mais ????
          FreeandNil(Forms2Array[1]);
       end;
     
      FString := IntToStr(iFree);
      Forms2Array[iFree] := TForm2.Create(Self);
      Forms2Array[iFree].Show;
     
    end;
    J'ai encore quelques doutes dans la partie {Simulation d'une fenêtre détruite}. La gestion des Forms ne peut souffrir d'aucune approximation... Mais je vais utiliser le FreeOrReleaseAndNil proposé dans une réponse.
    Dernière modification par Invité ; 01/12/2014 à 11h06.

  8. #8
    Membre Expert
    Avatar de Barbibulle
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    2 052
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 2 052
    Par défaut
    Et bien si le but c'est d'avoir plusieurs instance de Form il ne faut pas utiliser la variable Formx définie par défaut....


    Form1 n'est définie qu'une fois et est unique

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Form1 := TForm1.create(Application);
    Form1 := TForm1.create(Application);
    Form1 := TForm1.create(Application);
    Va bien créer 3 fenêtres.

    Mais Form1 ne pointera que vers la dernière....

    Maintenant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Form1 := TForm1.create(Application);
    Form1 := TForm1.create(Application);
    Form1 := TForm1.create(Application);
    Form1 := nil;
    Crée 3 fenêtres également mais Form1 pointe vers ... rien :p

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. passage de variable via form
    Par emilek dans le forum Langage
    Réponses: 2
    Dernier message: 21/11/2007, 14h12
  2. [vb net]passage de variable entre form !
    Par ChristopheOce dans le forum Windows Forms
    Réponses: 5
    Dernier message: 12/09/2006, 13h06
  3. [VB.Net][debutant]passage de variable entre windows form
    Par ChristopheOce dans le forum Windows Forms
    Réponses: 1
    Dernier message: 26/07/2006, 14h18
  4. Réponses: 2
    Dernier message: 29/06/2006, 01h36
  5. [C#] Passage de variables entre 2 forms
    Par Tips dans le forum C#
    Réponses: 6
    Dernier message: 06/12/2005, 16h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo