Komunikacja między dwoma formami okien w C#

Mam dwie formy, jedna jest formą główną, a druga jest formą opcji. Powiedzmy na przykład, że użytkownik kliknie Moje menu w głównym formularzu: Tools -> Options, spowoduje to wyświetlenie formularza opcji.

Moje pytanie brzmi: Jak mogę wysłać dane z mojego formularza opcji z powrotem do mojego formularza głównego? Wiem, że przydałyby mi się właściwości, ale mam wiele opcji i wydaje mi się, że to jestżmudna dziwna rzecz do zrobienia.

Więc jaki jest najlepszy sposób?
Author: Servy, 2009-11-03

11 answers

Form1 uruchamia Form2 do otwarcia. Form2 ma przeciążony konstruktor, który przyjmuje wywołanie formularza jako argument i dostarcza jego odniesienie do elementów Form2. To rozwiązuje problem komunikacji. Na przykład wystawiłem właściwość Label jako public W Form1, która została zmodyfikowana w Form2.

Dzięki takiemu podejściu można komunikować się na różne sposoby.

Link do pobrania przykładowego projektu

//Twój Formularz 1

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

//Twój 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

 54
Author: this. __curious_geek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-28 00:30:02

W komentarzach do zaakceptowanej odpowiedzi Neeraj Gulia pisze:

Prowadzi to do ścisłego łączenia formularzy Form1 i Form2, myślę, że zamiast tego należy używać niestandardowych zdarzeń dla tego rodzaju scenariuszy.

Komentarz jest dokładnie słuszny. Przyjęta odpowiedź nie jest zła; dla prostych programów, a zwłaszcza dla osób dopiero uczących się programowania i starających się uruchomić podstawowe scenariusze, jest to bardzo przydatny przykład tego, jak para formularzy może interakcja.

Jednak prawdą jest, że sprzężenie, które powoduje przykład, może i powinno być unikane, i że w konkretnym przykładzie, Zdarzenie osiągnęłoby to samo w ogólnoświatowy, oddzielony sposób.

Poniżej znajduje się przykład użycia kodu odpowiedzi jako linii bazowej:

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

Powyższy kod tworzy nową instancję Form2, a następnie przed jej pokazaniem dodaje obsługę zdarzenia do Button1Click wydarzenie.

Zauważ, że wyrażenie (sender, e) => Lbl.Text = ((Form2)sender).Message jest automatycznie konwertowane przez kompilator do metody, która wygląda podobnie (ale zdecydowanie nie do końca tak jak):

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

Istnieje wiele sposobów / składni implementacji i subskrybowania obsługi zdarzenia. Na przykład, używając anonimowej metody jak powyżej, tak naprawdę nie musisz rzucać parametru sender; zamiast tego możesz po prostu użyć zmiennej lokalnej frm bezpośrednio: (sender, e) => Lbl.Text = frm.Message.

Idąc w drugą stronę, ty nie musisz używać anonimowej metody. W rzeczywistości można po prostu zadeklarować zwykłą metodę, tak jak wygenerowany przeze mnie kompilator, który pokazałem powyżej, a następnie zapisać tę metodę do zdarzenia: frm.Button1Click += frm_Message; (gdzie oczywiście użyto nazwy frm_Message dla metody, tak jak w moim przykładzie powyżej).

Niezależnie od tego, jak to zrobisz, oczywiście będziesz musiał Form2 faktycznie zaimplementować to zdarzenie Button1Click. To bardzo proste ...

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

Oprócz event, zadeklarowałem również właściwość Message, która eksponuje właściwość Text (i tylko właściwość Text, a w rzeczywistości tylko do odczytu) kontrolki txtMessage. Pozwala to abonentowi zdarzenia uzyskać wartość i zrobić z nim wszystko, czego potrzebuje.

Zauważ, że wszystko, co robi zdarzenie, to powiadomienie Abonenta, że przycisk został faktycznie kliknięty. To do abonenta należy decyzja, jak zinterpretować lub zareagować na to zdarzenie (np. pobierając wartość Message własność i przypisanie jej do czegoś).

W przeciwieństwie do tego, w jaki sposób można przekazać tekst wraz z samym zdarzeniem, deklarując nową podklasę EventArgs i używając jej zamiast zdarzenia:
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));
        }
    }
}

Wtedy abonent może po prostu pobrać wartość wiadomości bezpośrednio z obiektu event:

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


Ważną rzeczą we wszystkich powyższych wariantach jest to, że w żadnym momencie Klasa Form2 nie musi wiedzieć nic o Form1. Posiadanie Form1 wiedzy o Form2 jest nieuniknione; w końcu jest to obiekt, który utworzy nową instancję Form2 i użyje jej. Ale relacja może być asymetryczna, z {[5] } jest użyteczna przez dowolny obiekt, który potrzebuje funkcji, które oferuje. Poprzez wystawienie funkcji jako zdarzenia (i opcjonalnie z właściwością), staje się ona użyteczna, nie ograniczając jej użyteczności tylko do klasy Form1.

 11
Author: Peter Duniho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-26 01:39:55

Najlepszym w tym przypadku byłoby posiadanie OptionsService klasy / interfejsu, który jest dostępny przez IServiceProvider.

Wystarczy dodać zdarzenie, gdy coś się zmieni, a reszta aplikacji może na nie odpowiedzieć.

 6
Author: leppie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 06:11:49

Properties jest jedną opcją, shared static class-inną opcją, events-inną opcją...

 1
Author: Dani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 06:11:23

Możesz spróbować AutoMapper . Zachowaj swoje opcje w osobnej klasie, a następnie użyj AutoMapper, aby przenieść dane między klasą A formularzem.

 1
Author: Andy S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 06:13:32

Utwórz klasę i umieść wszystkie swoje właściwości wewnątrz klasy .. Utwórz właściwość w klasie rodzica i ustaw ją z formularza

 1
Author: Zuhaib,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 06:13:56

Możesz mieć funkcję w postaci B TAK:

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

I można to nazwać tak:

...
using(var fb = new FormB())
{
    var s = fb.GetNewSettings();
    ...
    // Notify other parts of the application that settings have changed.
}
 1
Author: John Gietzen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 06:13:57

MVC, MVP , MVVM -- lekka przesada dla kogoś, kto twierdzi, że chce tutoriali. Są to teorie, które mają całe kursy poświęcone im.

Jak już napisano, przekazywanie obiektu jest prawdopodobnie najłatwiejsze. Jeśli traktowanie klasy jako obiektu (wymiennego w tym sensie) jest nowe, możesz poświęcić kolejne 2-4 tygodnie na wymyślanie właściwości i konstruktorów itp.

W żadnym razie nie jestem mistrzem C#, ale te pojęcia muszą być dość konkretne, jeśli chcesz pójść o wiele dalej niż przekazywanie wartości między dwoma formami(również klasami / obiektami). Nie próbuję być wredny, po prostu brzmi to tak, jakbyś przechodził z czegoś takiego jak VB6 (lub jakikolwiek język z globalami) do czegoś znacznie bardziej ustrukturyzowanego.

W końcu kliknie.

 1
Author: Sarkazein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 07:04:55

Istnieje wiele sposobów komunikacji między dwiema formami. Niektóre z nich zostały Ci już wyjaśnione. Pokazuję ci na odwrót.

Zakładając, że musisz zaktualizować niektóre ustawienia z formularza potomnego do formularza rodzica. Możesz również skorzystać z tych dwóch sposobów:

  1. Korzystanie Z Systemu.Action (tutaj po prostu przekazujesz funkcję main forms jako parametr do postaci potomnej, tak jak funkcja zwrotna)
  2. metoda OpenForms (wywołujesz bezpośrednio Twoich otwartych formularzy)

Korzystanie Z Systemu.Akcja

Można o tym myśleć jako o funkcji zwrotnej przekazywanej do postaci potomnej.

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

Metoda OpenForms

Ta metoda jest łatwa (2 linie). Ale działa tylko z formami, które są otwarte. Wszystko, co musisz zrobić, to dodać te dwie linie, w których kiedykolwiek chcesz przekazać niektóre dane.

Main frmMain = (Main)Application.OpenForms["Main"];
frmMain.UpdateSettings("Some data");
 1
Author: Ozesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-22 05:30:50

Jest to prawdopodobnie omijanie problem trochę, ale moje ustawienia okno dialogowe używa Ustawienia aplikacji construct. http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

Nie mogę znaleźć dobrego przykładu, który byłby podobny do tego, jak to robię( czyli faktycznie posiadanie rzeczywistego obiektu class+), ale obejmuje to inny sposób:

Odczyt domyślnych ustawień aplikacji w C #

 0
Author: Oren Mazor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45

Forma jest klasą, tak jak każda inna klasa. Dodaj kilka publicznych zmiennych do swojej klasy formularza i ustaw je po kliknięciu przycisku, aby zamknąć formularz (technicznie po prostu go ukrywają).

A VB.NET przykład, ale wpadniesz na pomysł -

W klasie OptionsForm:

Public Option1 as String = ""

Itd. Ustaw je po naciśnięciu przycisku "Ok".

Więc w głównym formularzu, gdy naciśną przycisk "Opcje" - tworzysz swój formularz opcji:

OptionsForm.ShowDialog()

Kiedy wychodzi, zbierasz ustawienia opcji z publicznych zmiennych w formularzu:

option1 = OptionsForm.Option1

Itd.

 0
Author: Ron Savage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-27 16:15:24