webentwicklung-frage-antwort-db.com.de

Kommunikation zwischen zwei Windows-Formularen in C #

Ich habe zwei Formen, eine ist die Hauptform und die andere ist eine Optionsform. Sagen Sie zum Beispiel, dass der Benutzer im Hauptformular auf mein Menü klickt: Tools -> Options. Dadurch würde mein Optionsformular angezeigt.

Meine Frage ist, wie kann ich Daten aus meinem Optionsformular an mein Hauptformular zurücksenden? Ich weiß, ich könnte Eigenschaften verwenden, aber ich habe viele Optionen und dies scheint eine langweilig seltsame Sache zu tun. 

Was ist der beste Weg?

35
Bob Dylan

Form1 löst das Öffnen von Form2 aus. Form2 hat einen überladenen Konstruktor, der aufrufende Form als Argument annimmt und seine Referenz auf Form2-Member enthält. Dies löst das Kommunikationsproblem. Zum Beispiel habe ich Label-Eigenschaft als öffentlich in Form1 verfügbar gemacht, die in Form2 geändert wird.

Mit diesem Ansatz können Sie auf verschiedene Arten kommunizieren.

Download-Link für Beispielprojekt

// Ihre 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; }
    }
}

// Ihre Form2

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

In den Kommentaren zur akzeptierten Antwort schreibt Neeraj Gulia:

Dies führt zu einer engen Kopplung der Formulare Form1 und Form2, stattdessen sollten benutzerdefinierte Ereignisse für solche Szenarien verwendet werden.

Der Kommentar ist genau richtig. Die akzeptierte Antwort ist nicht schlecht. Für einfache Programme und insbesondere für Menschen, die nur das Programmieren lernen und versuchen, grundlegende Szenarien zum Laufen zu bringen, ist dies ein sehr nützliches Beispiel für die Interaktion eines Formularpaares.

Es ist jedoch wahr, dass die Kopplung, die das Beispiel verursacht, vermieden werden kann und sollte, und dass ein Ereignis in dem bestimmten Beispiel dasselbe auf eine allgemeine, entkoppelte Weise erreichen würde.

Hier ein Beispiel, das den akzeptierten Antwortcode als Basis verwendet:

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();
    }
}

Der obige Code erstellt eine neue Instanz von Form2 und fügt dem Ereignis Button1Click dieses Formulars dann einen Ereignishandler hinzu.

Beachten Sie, dass der Ausdruck (sender, e) => Lbl.Text = ((Form2)sender).Message vom Compiler automatisch in eine Methode konvertiert wird, die ungefähr so ​​aussieht (aber definitiv nicht genau so aussieht):

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

Es gibt viele Möglichkeiten/Syntax, um den Event-Handler zu implementieren und zu abonnieren. Wenn Sie beispielsweise eine anonyme Methode wie oben verwenden, müssen Sie den Parameter sender nicht wirklich umwandeln. Stattdessen können Sie die lokale Variable frm direkt verwenden: (sender, e) => Lbl.Text = frm.Message.

Umgekehrt müssen Sie keine anonyme Methode verwenden. Sie können in der Tat eine reguläre Methode genauso wie die vom Compiler generierte Methode, die ich oben angezeigt habe, deklarieren und diese Methode dann für das Ereignis abonnieren: frm.Button1Click += frm_Message; (wobei Sie natürlich den Namen frm_Message für die Methode verwendet haben, genau wie in meinem obigen Beispiel ).

Unabhängig davon, wie Sie das tun, natürlich müssen Sie Form2 tatsächlich dieses Button1Click-Ereignis implementieren. Das ist sehr einfach…

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);
        }
    }
}

Neben dem Ereignis habe ich auch eine Eigenschaft Message deklariert, die die Text-Eigenschaft (und only die Text-Eigenschaft und tatsächlich nur als schreibgeschützt) des txtMessage-Steuerelements verfügbar macht. Dies ermöglicht es dem Abonnenten des Ereignisses, den Wert abzurufen und alles zu tun, was damit verbunden ist.

Beachten Sie, dass das Ereignis den Teilnehmer lediglich darauf aufmerksam macht, dass auf die Schaltfläche tatsächlich geklickt wurde. Es ist Sache des Abonnenten, zu entscheiden, wie er dieses Ereignis interpretieren oder darauf reagieren soll (z. B. durch Abrufen des Werts der Message-Eigenschaft und Zuweisen zu etwas).

Alternativ können Sie den Text tatsächlich zusammen mit dem Ereignis selbst liefern, indem Sie eine neue EventArgs-Unterklasse deklarieren und stattdessen das für das Ereignis verwenden:

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));
        }
    }
}

Dann kann der Abonnent den Nachrichtenwert direkt vom Ereignisobjekt abrufen:

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


Der wichtige Hinweis bei allen obigen Variationen ist, dass die Klasse Form2 zu keinem Zeitpunkt etwas über Form1 wissen muss. Form1 über Form2 zu wissen, ist unvermeidlich; Immerhin ist es das Objekt, das eine neue Form2-Instanz erstellt und verwendet. Die Beziehung kann jedoch asymmetrisch sein, wobei Form2 von any - Objekten verwendet werden kann, die die von ihr angebotenen Funktionen benötigen. Durch das Verfügbarmachen der Funktionalität als Ereignis (und optional mit einer Eigenschaft) macht sie sich nützlich, ohne sich auf die Klasse Form1 zu beschränken.

11
Peter Duniho

In diesem Fall ist es am besten, eine OptionsService-Klasse/-Schnittstelle zu haben, auf die über IServiceProvider zugegriffen werden kann. 

Fügen Sie einfach ein Ereignis hinzu, wenn sich etwas ändert, und der Rest der App kann darauf reagieren.

6
leppie

Eigenschaften sind eine Option, eine gemeinsam genutzte statische Klasse - eine weitere Option, Ereignisse - eine weitere Option ...

1
Dani

Möglicherweise versuchen Sie AutoMapper . Behalten Sie Ihre Optionen in einer separaten Klasse und verwenden Sie AutoMapper, um die Daten zwischen Klasse und Formular zu verschieben.

1
Andy S

Erstellen Sie eine Klasse, und fügen Sie alle Ihre Eigenschaften in die Klasse ein. Erstellen Sie eine Eigenschaft in der übergeordneten Klasse und legen Sie sie in Ihrem untergeordneten Formular (Optionen) fest

1
Zuhaib

Sie können eine Funktion in Form B wie folgt haben:

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

Und du kannst es so nennen:

...
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 - leichter Overkill für jemanden, der zugibt, dass er Tutorials möchte. Das sind Theorien, denen ganze Kurse gewidmet sind.

Wie bereits erwähnt, ist das Weitergeben eines Objekts wahrscheinlich am einfachsten. Wenn die Behandlung einer Klasse als Objekt (in diesem Sinne austauschbar) neu ist, möchten Sie möglicherweise weitere 2-4 Wochen damit verbringen, Eigenschaften und Konstruktoren und so weiter herauszufinden. 

Ich bin auf keinen Fall ein C # -Master, aber diese Konzepte müssen ziemlich konkret sein, wenn Sie über die Weitergabe von Werten zwischen zwei Formularen (auch Klassen/Objekten selbst) hinausgehen möchten. Sie wollen hier nicht gemein sein, es klingt einfach so, als würden Sie von etwas wie VB6 (oder einer anderen Sprache mit Globals) zu etwas weit strukturierter wechseln.

Eventuell wird es klicken. 

1
Sarkazein

Es gibt viele Möglichkeiten, die Kommunikation zwischen zwei Formularen durchzuführen. Einige davon wurden Ihnen bereits erklärt. Ich zeige dich umgekehrt.

Angenommen, Sie müssen einige Einstellungen vom untergeordneten Formular auf das übergeordnete Formular aktualisieren. Sie können auch diese beiden Möglichkeiten nutzen:

  1. Verwenden von System.Action (Hier übergeben Sie einfach die Hauptformularfunktion als Parameter an das untergeordnete Formular wie eine Rückruffunktion)
  2. OpenForms-Methode (Sie rufen direkt eines Ihrer geöffneten Formulare an)

System.Action verwenden

Sie können es sich als eine Rückruffunktion vorstellen, die an das untergeordnete Formular übergeben wird.

// -------- 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");
}

OpenForms-Methode  

Diese Methode ist einfach ( 2 Zeilen ). Funktioniert jedoch nur mit Formularen, die geöffnet sind ..__ Alles, was Sie tun müssen, ist, diese beiden Zeilen dort hinzuzufügen, wo immer Sie Daten übergeben möchten.

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

Dies ist wahrscheinlich ein wenig, um das Problem zu umgehen, aber mein Einstellungsdialogfeld verwendet das Konstrukt für Anwendungseinstellungen. http://msdn.Microsoft.com/de-de/library/k4s6c3a0.aspx

Ich kann kein gutes Beispiel finden, das ähnlich ist, wie ich es mache (was tatsächlich eine Klasse + ein Objekt hat), aber dies deckt eine andere Möglichkeit ab:

Standardanwendungseinstellungen in C # lesen

0
Oren Mazor

Der beste Weg, um mit der Kommunikation zwischen Containern umzugehen, ist die Implementierung einer Observer-Klasse

Das Beobachtermuster ist ein Software-Entwurfsmuster, in dem ein Objekt, das als Subjekt bezeichnet wird, eine Liste seiner abhängigen Personen, sogenannte Observer, verwaltet und diese automatisch über alle Zustandsänderungen benachrichtigt, normalerweise durch Aufrufen einer ihrer Methoden. (Wikipedia)

die Art und Weise, wie ich dies tue, ist das Erstellen einer Observer-Klasse, und in ihr schreiben Sie etwas wie folgt:

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    }

grundsätzlich gilt: Die erste Zeile besagt, dass es eine Funktion gibt, die ein anderer implementieren wird

die zweite Zeile erstellt ein Ereignis, das auftritt, wenn die delegierte Funktion aufruft

und die dritte Zeile ist die Erstellung der Funktion, die das Ereignis aufruft

in Ihrem UserControl sollten Sie also eine Funktion wie diese hinzufügen:

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);
}

und in deinem Formular solltest du so etwas tun:

public static int signal = 0;

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

jetzt können Sie diese Funktion für eine Reihe anderer Steuerelemente und Container registrieren, und alle erhalten das Signal

Ich hoffe das würde helfen :)

0
Dor Lugasi

Ein Formular ist eine Klasse, genau wie jede andere Klasse. Fügen Sie Ihrer Formularklasse einige öffentliche Variablen hinzu, und legen Sie diese fest, wenn Sie auf die Schaltfläche klicken, um das Formular zu schließen (technisch gesehen verstecken sie es nur).

Ein VB.NET-Beispiel, aber Sie bekommen die Idee -

In Ihrer OptionsForm-Klasse:

Public Option1 as String = ""

usw. Stellen Sie sie ein, wenn Sie auf die Schaltfläche "Ok" klicken.

Wenn Sie also in Ihrem Hauptformular auf die Schaltfläche "Optionen" klicken, erstellen Sie Ihr Optionsformular:

OptionsForm.ShowDialog()

wenn es beendet wird, sammeln Sie Ihre Optionseinstellungen aus den öffentlichen Variablen des Formulars:

option1 = OptionsForm.Option1

usw.

0
Ron Savage