Подтвердить что ты не робот

Общаться между двумя окнами в С#

У меня две формы: одна - основная форма, а другая - форма опций. Например, скажем, что пользователь нажимает на мое меню в основной форме: Tools -> Options, это приведет к отображению формы параметров.

Мой вопрос в том, как я могу отправить данные из формы своих параметров обратно в свою основную форму? Я знаю, что могу использовать свойства, но у меня есть много вариантов, и это похоже на необычную вещь ded.

Итак, что является лучшим способом?

4b9b3361

Ответ 1

Form1 запускает Form2 для открытия. Form2 имеет перегруженный конструктор, который принимает вызывающую форму в качестве аргумента и предоставляет ссылку на члены Form2. Это решает проблему связи. Например, я выставил свойство Label как общедоступное в Form1, которое изменено в Form2.

При таком подходе вы можете общаться по-разному.

Ссылка для скачивания для примера проекта

//Ваша форма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; }
    }
}

//Ваша форма2

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
(источник: ruchitsurati.net)

alt text
(источник: ruchitsurati.net)

Ответ 2

В комментариях к принятому ответу Нирадж Гуля пишет:

Это приводит к тесной связи форм Form1 и Form2, я думаю, вместо этого следует использовать пользовательские события для таких сценариев.

Комментарий совершенно прав. Принятый ответ не плох; для простых программ, особенно для тех, кто только изучает программирование и пытается заставить работать базовые сценарии, это очень полезный пример взаимодействия пары форм.

Тем не менее, верно, что связывание, которое вызывает этот пример, можно и нужно избегать, и что в конкретном примере событие выполнит то же самое в общем, отделенном виде.

Вот пример, использующий принятый код ответа в качестве базовой линии:

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

Приведенный выше код создает новый экземпляр Form2, а затем перед его отображением добавляет обработчик события в событие Button1Click формы.

Обратите внимание, что выражение (sender, e) => Lbl.Text = ((Form2)sender).Message автоматически преобразуется компилятором в метод, который выглядит примерно так (но определенно не совсем так), как это:

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

На самом деле существует множество способов/синтаксисов для реализации и подписки обработчика событий. Например, используя анонимный метод, как описано выше, вам не нужно приводить параметр sender; вместо этого вы можете просто использовать локальную переменную frm напрямую: (sender, e) => Lbl.Text = frm.Message.

В противном случае вам не нужно использовать анонимный метод. На самом деле вы можете просто объявить обычный метод, подобный сгенерированному компилятором, который я показал выше, и затем подписать этот метод на событие: frm.Button1Click += frm_Message; (где вы, конечно, использовали имя frm_Message для метода, как в моем примере выше).

Независимо от того, как вы это делаете, конечно, вам нужно, чтобы Form2 действительно реализовала это событие Button1Click. Это очень просто...

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

В дополнение к событию я также объявил свойство Message которое предоставляет свойство Text (и только свойство Text, и фактически только для чтения) txtMessage управления txtMessage. Это позволяет подписчику на событие получить значение и делать с ним все, что ему нужно.

Обратите внимание, что все, что происходит в событии, - это предупреждает подписчика о том, что кнопка была нажата. Абонент сам решает, как интерпретировать или реагировать на это событие (например, путем извлечения значения свойства Message и присвоения его чему-либо).

В качестве альтернативы, вы могли бы фактически доставить текст вместе с самим событием, объявив новый подкласс EventArgs и используя его вместо этого:

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

Тогда подписчик может просто получить значение сообщения непосредственно из объекта события:

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


Важно отметить, что во всех вышеперечисленных вариациях класс Form2 ни в коем случае не должен ничего знать о Form1. Наличие Form1 знать о Form2 неизбежно; в конце концов, это объект, который создаст новый экземпляр Form2 и будет использовать его. Но отношения могут быть асимметричными, поскольку Form2 может использоваться любым объектом, который нуждается в функциях, которые он предлагает. Предоставляя функциональность как событие (и, возможно, со свойством), она становится полезной, не ограничивая ее полезность только классом Form1.

Ответ 3

В этом случае лучше всего иметь некоторый класс/интерфейс OptionsService, доступный через IServiceProvider.

Просто добавьте событие, когда что-то изменится, и остальная часть приложения сможет ответить на него.

Ответ 4

Свойства - это один вариант, общий статический класс - еще один вариант, события - еще один вариант...

Ответ 5

Вы можете попробовать AutoMapper. Сохраняйте свои параметры в отдельном классе, а затем используйте AutoMapper для передачи данных между классом и формой.

Ответ 6

Создайте класс и поместите все свои свойства внутри класса. Создайте свойство в родительском классе и установите его из формы вашего ребенка (параметры)

Ответ 7

Вы можете иметь функцию в форме B следующим образом:

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

И вы можете назвать это следующим образом:

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

Ответ 8

MVC, MVP, MVVM - незначительный перебор для кого-то, по общему признанию говоря, что они хотят учебников. Это те теории, которые посвящены целым курсам.

Как уже было опубликовано, прохождение объекта вокруг, наверное, проще всего. Если рассматривать класс как объект (взаимозаменяемый в этом смысле) является новым, тогда вы можете потратить еще 2-4 недели на выяснение свойств и конструкторов и т.д.

Я не являюсь мастером С# любыми способами, но эти концепции должны быть довольно конкретными, если вы хотите пойти намного дальше, нежели передавать значения между двумя формами (также самими классами/объектами). Не пытаясь быть здесь вообще злым, это просто звучит так, будто вы переходите от чего-то вроде VB6 (или любого языка с глобалями) к чему-то гораздо более структурированному.

В конце концов, он щелкнет.

Ответ 9

Существует множество способов установить связь между двумя формами. Некоторые из них вам уже объяснили. Я показываю вам все наоборот.

Предполагая, что вам нужно обновить некоторые настройки из дочерней формы в родительскую форму. Вы также можете использовать эти два способа:

  1. Использование System.Action (здесь вы просто передаете функцию основных форм в качестве параметра дочерней форме, например, функции обратного вызова)
  2. Метод OpenForms (Вы напрямую вызываете одну из ваших открытых форм)

Использование System.Action

Вы можете думать об этом как о функции обратного вызова, передаваемой дочерней форме.

// -------- 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

Этот метод прост (2 строки). Но работает только с открытыми формами. Все, что вам нужно сделать, это добавить эти две строки, где бы вы ни хотели передать некоторые данные.

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

Ответ 10

Это, вероятно, немного обойдется вашей проблеме, но в моем диалоговом окне настроек используется настройка параметров приложения. http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

Я не могу найти хороший пример, аналогичный тому, как я это делаю (на самом деле имеющему фактический объект класса +), но это охватывает другой способ:

Чтение настроек приложения по умолчанию на С#

Ответ 11

Форма - это класс, как и любой другой класс. Добавьте некоторые общедоступные переменные в ваш класс формы и установите их, когда они нажимают кнопку, чтобы закрыть форму (технически они просто скрывают ее).

Пример VB.NET, но вы получите идею -

В вашем классе OptionsForm:

Public Option1 as String = ""

и т.д.. Установите их, когда они нажмут кнопку "Ok".

Итак, в вашей основной форме, когда они нажимают кнопку "Параметры", вы создаете свою форму:

OptionsForm.ShowDialog()

когда он выйдет, вы собираете настройки параметров из общедоступных переменных в форме:

option1 = OptionsForm.Option1

и др.

Ответ 12

Лучший способ справиться с коммуникацией между контейнерами - реализовать класс наблюдателя.

Шаблон наблюдателя является шаблоном разработки программного обеспечения, в котором объект, называемый субъектом, поддерживает список своих зависимых, называемых наблюдателями, и автоматически уведомляет их о любых изменениях состояния, обычно вызывая один из их методов. (Википедия)

Я делаю это, создавая класс Observer, и внутри него пишу что-то вроде этого:

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    }

так в основном: первая строка говорит, что будет функция, которую кто-то другой будет реализовывать

вторая строка создает событие, которое происходит, когда делегированная функция будет вызывать

а третья строка - создание функции, которая вызывает событие

поэтому в вашем UserControl вы должны добавить такую функцию:

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

и в вашей форме вы должны сделать что-то вроде:

public static int signal = 0;

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

и теперь вы можете зарегистрировать эту функцию для целого ряда других элементов управления и контейнеров, и все они получат сигнал

Я надеюсь, что это поможет :)