web-dev-qa-db-fra.com

Communiquer entre deux formulaires Windows en C #

J'ai deux formulaires, l'un est la forme principale et l'autre est un formulaire d'options. Donc, disons par exemple que l'utilisateur clique sur mon menu dans le formulaire principal: Tools -> Options, mon formulaire d'options s'afficherait.

Ma question est la suivante: comment puis-je envoyer les données de mon formulaire d’options à mon formulaire principal? Je sais que je pourrais utiliser des propriétés, mais j’ai beaucoup d’options et cela semble être un fastidieux chose étrange à faire. 

Alors, quel est le meilleur moyen?

35
Bob Dylan

Form1 déclenche l'ouverture de Form2. Form2 a un constructeur surchargé qui prend la forme appelante en argument et fournit sa référence aux membres Form2. Cela résout le problème de communication. Par exemple, j'ai exposé Label Property comme public dans Form1, lequel est modifié dans Form2.

Avec cette approche, vous pouvez faire la communication de différentes manières.

Lien de téléchargement pour exemple de projet

// Votre Form1

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Form2 frm = new Form2(this);
        frm.Show();
    }

    public string LabelText
    {
        get { return Lbl.Text; }
        set { Lbl.Text = value; }
    }
}

// Votre formulaire2

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
    }

    private Form1 mainForm = null;
    public Form2(Form callingForm)
    {
        mainForm = callingForm as Form1; 
        InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {

    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.mainForm.LabelText = txtMessage.Text;
    }
}

alt text http://demo.ruchitsurati.net/files/frm1.png

alt text http://demo.ruchitsurati.net/files/frm2.png

55

Dans les commentaires à la réponse acceptée, Neeraj Gulia écrit:

Cela conduit à un couplage étroit des formulaires Form1 et Form2. J'imagine que vous devriez plutôt utiliser des événements personnalisés pour ce type de scénario.

Le commentaire est tout à fait correct. La réponse acceptée n'est pas mauvaise. pour les programmes simples, et particulièrement pour les personnes qui apprennent seulement à programmer et à essayer de faire fonctionner des scénarios de base, il s'agit d'un exemple très utile de la façon dont une paire de formulaires peut interagir.

Cependant, il est vrai que le couplage que cet exemple provoque peut et doit être évité, et que dans l'exemple particulier, un événement accomplirait la même chose de manière générale, découplée.

Voici un exemple, en utilisant le code de la réponse acceptée comme base:

Form1.cs:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Form2 frm = new Form2();

        frm.Button1Click += (sender, e) => Lbl.Text = ((Form2)sender).Message;

        frm.Show();
    }
}

Le code ci-dessus crée une nouvelle instance de Form2, puis avant de l'afficher, ajoute un gestionnaire d'événements à l'événement Button1Click de ce formulaire.

Notez que l'expression (sender, e) => Lbl.Text = ((Form2)sender).Message est automatiquement convertie par le compilateur en une méthode ressemblant à quelque chose de similaire (mais pas tout à fait comme):

private void frm_Message(object sender, EventArgs e)
{
    Lbl.Text = ((Form2)sender).Message;
}

Il y a en fait beaucoup de façons/syntaxes d'implémenter et de souscrire le gestionnaire d'événements. Par exemple, en utilisant une méthode anonyme comme ci-dessus, vous n'avez pas vraiment besoin de transtyper le paramètre sender; à la place, vous pouvez simplement utiliser directement la variable locale frm: (sender, e) => Lbl.Text = frm.Message.

En allant dans l'autre sens, vous n'avez pas besoin d'utiliser une méthode anonyme. Vous pouvez en fait déclarer une méthode régulière comme celle que j'ai montrée ci-dessus, générée par le compilateur, puis abonner cette méthode à l'événement: frm.Button1Click += frm_Message; (où vous avez bien sûr utilisé le nom frm_Message pour la méthode, comme dans l'exemple ci-dessus. ).

Quelle que soit la façon dont vous le faites, vous aurez bien sûr besoin de Form2 pour réellement implémenter cet événement Button1Click. C'est très simple…

Form2.cs:

public partial class Form2 : Form
{
    public event EventHandler Button1Click;

    public string Message { get { return txtMessage.Text; } }

    public Form2()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        EventHandler handler = Button1Click;

        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

En plus de l'événement, j'ai également déclaré une propriété Message qui expose la propriété Text (et only la propriété Text et uniquement en lecture seule en fait) du contrôle txtMessage. Cela permet à l’abonné de l’événement d’obtenir la valeur et d’en faire le nécessaire.

Notez que tout ce que fait l'événement consiste à alerter l'abonné que le bouton a été cliqué. Il appartient à l’abonné de décider comment interpréter cet événement ou y réagir (par exemple, en récupérant la valeur de la propriété Message et en l’assignant à quelque chose).

Sinon, vous pouvez en fait livrer le texte avec l'événement lui-même, en déclarant une nouvelle sous-classe EventArgs et en l'utilisant pour l'événement:

public class MessageEventArgs : EventArgs
{
    public string Message { get; private set; }

    public MessageEventArgs(string message)
    {
        Message = message;
    }
}

public partial class Form2 : Form
{
    public event EventHandler<MessageEventArgs> Button1Click;

    public Form2()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        EventHandler handler = Button1Click;

        if (handler != null)
        {
            handler(this, new MessageEventArgs(txtMessage.Text));
        }
    }
}

Ensuite, l'abonné peut simplement extraire la valeur du message directement à partir de l'objet événement:

frm.Button1Click += (sender, e) => e.Message;


La remarque importante dans toutes les variantes ci-dessus est qu’à aucun moment la classe Form2 n’a besoin de savoir quoi que ce soit sur Form1. Il est inévitable que Form1 connaisse Form2; Après tout, c'est l'objet qui va créer une nouvelle instance Form2 et l'utiliser. Mais la relation peut être asymétrique, Form2 pouvant être utilisé par tout objet nécessitant les fonctionnalités proposées. En exposant la fonctionnalité en tant qu'événement (et éventuellement avec une propriété), il devient utile sans limiter son utilité à la classe Form1.

11
Peter Duniho

Dans ce cas, le mieux serait d’avoir une classe/interface OptionsService accessible via IServiceProvider

Ajoutez simplement un événement lorsque quelque chose change et le reste de l'application peut y répondre.

6
leppie

Les propriétés sont une option, la classe statique partagée - une autre option, les événements - une autre option ...

1
Dani

Vous pouvez essayer AutoMapper . Conservez vos options dans une classe séparée, puis utilisez AutoMapper pour transférer les données entre la classe et le formulaire.

1
Andy S

Créez une classe et placez toutes vos propriétés dans la classe. Créez une propriété dans la classe parente et définissez-la à partir de votre formulaire enfant (options).

1
Zuhaib

Vous pouvez avoir une fonction dans le formulaire B comme suit:

public SettingsResults GetNewSettings()
{
    if(this.ShowDialog() == DialogResult.Ok)
    {
         return new SettingsResult { ... };
    }
    else
    {
         return null;
    }
}

Et vous pouvez l'appeler comme ça:

...
using(var fb = new FormB())
{
    var s = fb.GetNewSettings();
    ...
    // Notify other parts of the application that settings have changed.
}
1
John Gietzen

MVC, MVP, MVVM - légère surdose pour quelqu'un qui affirme vouloir des tutoriels. Ce sont des théories qui ont des cours entiers qui leur sont dédiés.

Comme déjà signalé, passer un objet est probablement le plus facile. Si traiter une classe en tant qu'objet (interchangeable en ce sens) est nouveau, vous voudrez peut-être passer encore deux à quatre semaines à déterminer les propriétés, les constructeurs, etc. 

Je ne suis en aucun cas un maître C #, mais ces concepts doivent être assez concrets si vous voulez aller beaucoup plus loin que de transmettre des valeurs entre deux formes (également des classes/objets à part entière). Sans vouloir être méchant du tout, cela donne simplement l’impression que vous passez de quelque chose comme VB6 (ou n’importe quel langage avec des globaux) à quelque chose de beaucoup plus structuré.

Finalement, il va cliquer. 

1
Sarkazein

Il existe de nombreuses façons d’effectuer une communication entre deux formulaires. Certains d'entre eux vous ont déjà été expliqués. Je vous montre le contraire.

En supposant que vous deviez mettre à jour certains paramètres du formulaire enfant au formulaire parent. Vous pouvez également utiliser ces deux méthodes:

  1. Using System.Action (Ici, vous transmettez simplement la fonction de formulaire principale en tant que paramètre au formulaire enfant, comme une fonction de rappel)
  2. Méthode OpenForms (vous appelez directement l'un de vos formulaires ouverts)

Utilisation de System.Action

Vous pouvez le considérer comme une fonction de rappel transmise au formulaire enfant.

// -------- IN THE MAIN FORM --------

// CALLING THE CHILD FORM IN YOUR CODE LOOKS LIKE THIS
Options frmOptions = new Options(UpdateSettings);
frmOptions.Show();

// YOUR FUNCTION IN THE MAIN FORM TO BE EXECUTED
public void UpdateSettings(string data)
{
   // DO YOUR STUFF HERE
}

// -------- IN THE CHILD FORM --------

Action<string> UpdateSettings = null;

// IN THE CHILD FORMS CONSTRUCTOR
public Options(Action<string> UpdateSettings)
{
    InitializeComponent();
    this.UpdateSettings = UpdateSettings;
}

private void btnUpdate_Click(object sender, EventArgs e)
{
    // CALLING THE CALLBACK FUNCTION
    if (UpdateSettings != null)
        UpdateSettings("some data");
}

Méthode OpenForms  

Cette méthode est facile ( 2 lignes ). Mais ne fonctionne qu'avec les formulaires ouverts . Tout ce que vous avez à faire est d'ajouter ces deux lignes à n'importe quel endroit où vous souhaitez transmettre des données.

Main frmMain = (Main)Application.OpenForms["Main"];
frmMain.UpdateSettings("Some data");
1
Ozesh

C'est probablement en contournant un peu votre problème, mais ma boîte de dialogue de configuration utilise la construction Paramètres d'application. http://msdn.Microsoft.com/en-us/library/k4s6c3a0.aspx

Je ne trouve pas un bon exemple semblable à la façon dont je le fais (qui consiste à avoir une classe + un objet), mais cela couvre une autre façon de le faire:

Lecture des paramètres d'application par défaut en C #

0
Oren Mazor

La meilleure façon de gérer la communication entre les conteneurs est de mettre en place une classe d'observateur.

Le modèle d'observateur est un modèle de conception logicielle dans lequel un objet, appelé sujet, maintient une liste de ses dépendants, appelés observateurs, et les informe automatiquement de tout changement d'état, généralement en appelant l'une de leurs méthodes. (Wikipédia)

pour ce faire, je crée une classe Observer et j'écris quelque chose comme ceci:

1    public delegate void dlFuncToBeImplemented(string signal);
2    public static event dlFuncToBeImplemented OnFuncToBeImplemented;
3    public static void FuncToBeImplemented(string signal)
4    {
5         OnFuncToBeImplemented(signal);
6    }

donc, fondamentalement, la première ligne dit qu'il y aurait une fonction que quelqu'un d'autre mettra en œuvre

la deuxième ligne crée un événement qui se produit lorsque la fonction déléguée appellera

et la troisième ligne est la création de la fonction qui appelle l'événement

donc dans votre UserControl, vous devriez ajouter une fonction comme celle-ci:

private void ObserverRegister()//will contain all observer function registration
{
    Observer.OnFuncToBeImplemented += Observer_OnFuncToBeImplemented;
    /*and more observer function registration............*/
}


void Observer_OnFuncToBeImplemented(string signal)//the function that will occur when  FuncToBeImplemented(signal) will call 
{
    MessageBox.Show("Signal "+signal+" received!", "Atention!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

et dans votre formulaire, vous devriez faire quelque chose comme:

public static int signal = 0;

public void button1_Click(object sender, EventArgs e)
{
      Observer.FuncToBeImplemented(signal);//will call the event in the user control
}

et maintenant, vous pouvez enregistrer cette fonction sur de nombreux autres contrôles et conteneurs et ils recevront tous le signal.

J'espère que cela aiderait :)

0
Dor Lugasi

Un formulaire est une classe, comme n'importe quelle autre classe. Ajoutez des variables publiques à votre classe de formulaire et définissez-les quand ils cliquent sur le bouton pour fermer le formulaire (techniquement, ils le cachent).

Un exemple de VB.NET, mais vous aurez l’idée -

Dans votre classe OptionsForm:

Public Option1 as String = ""

etc. Définissez-les lorsqu'ils appuient sur le bouton "Ok".

Donc, dans votre formulaire principal, lorsqu'ils appuient sur le bouton "options", vous créez votre formulaire d'options:

OptionsForm.ShowDialog()

quand il se ferme, vous collectez vos paramètres d'option à partir des variables publiques du formulaire:

option1 = OptionsForm.Option1

etc.

0
Ron Savage