Windows forms передача данных между формами

.NET 4.x

Как передавать данные между формами

06.04.2012, 05:44. Показов 68672. Ответов 4


Hi!
Написал по сабжу статью (первоначально — себе в блог), но решил выложить тут. Полезнее будет, может кому и пригодится.

В процессе изучения C# вообще и WinForms в частности, у многих неофитов возникает вполне закономерный вопрос — а как передавать данные (в общем — объекты, но для начала хотя бы просто строки/числа). Кроме того, данные порой нужно передавать не только из основной формы в дочернюю, но и в обратном направлении. Для каждого из этих действий есть несколько способов реализации, и применение каждого из них зависит от контекста задачи, а также — от стиля и опыта программиста. Как правило, программисты выбирают себе несколько способов, которые используют в своих проектах. Я постарался в данной статье привести все известные мне способы, а так же их комбинации. Статья логически разделена на две части — прямая передача данных (из основной формы в дочернюю) и обратная.

Задача 1: Передать текстовую строку из основной формы в дочернюю
Реализация 1: Передать через конструктор дочерней формы
Самый простой способ. Класс дочерней формы конструируется таким образом, чтобы конструктор (или одна из его перегрузок) класса принимал в качестве аргумента или аргументов некие данные. Способ удобен тем, что в дочернюю форму можно передать практически неограниченное количество данных фактически любого типа. Неудобен он тем, что класс дочерней формы в этом случае становится слишком узкоспециализированным. При разработке небольших проектов это не почувствуется, но если вы возьметесь за масштабное модульное бизнес-приложение, сразу поймете всю узкость данного подхода. Но, тем не менее, не рассмотреть его было бы несправедливо.
Листинг 1.1.1. Основная форма:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    //Основная форма
    public partial class MasterForm : Form
    {
        public MasterForm()
        {
            InitializeComponent();
        }
 
        //Обрабатываем событие Click на кнопке
        private void SendB_Click(object sender, EventArgs e)
        {
            //Создаем экземпляр дочерней формы. В качестве аргумента конструктора указываем значение свойства Text текстбокса DataTB
            SlaveForm SF = new SlaveForm(DataTB.Text);
 
            //Показываем форму. В данном конкретном случае все равно как показывать: с помощью метода Show() либо ShowDialog()
            SF.Show();
        }
    }

Листинг 1.1.2. Дочерняя форма:

C#
1
2
3
4
5
6
7
8
9
10
    //Дочерняя форма
    public partial class SlaveForm : Form
    {
        public SlaveForm(String Data)
        {
            InitializeComponent();
            //Полученные в качестве аргумента данные напрямую пишем в свойство Text текстбокса
            InboxTB.Text = Data;
        }
    }

Думаю, все понятно и без дальнейших комментариев :-)

Реализация 2: Передать через public-переменную или свойство класса дочерней формы.
Способ чуть посложнее. Потребуется создать в классе дочерней формы дополнительную переменную или свойство (в данном случае — это не важно), и обработать событие Load дочерней формы.
Листинг 1.2.1. Основная форма:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    //Основная форма
    public partial class MasterForm : Form
    {
        public MasterForm()
        {
            InitializeComponent();
        }
 
        private void SendB_Click(object sender, EventArgs e)
        {
            //Создаем экземпляр формы
            SlaveForm SF = new SlaveForm();
 
            //Пишем в переменную InboxData данные.
            //ВНИМАНИЕ! Сначала нужно записать данные в переменную, а затем вызывать метод загрузки данных (Show()). 
            //В противном случае мы не получим данные в дочерней форме
            SF.InboxData = DataTB.Text;
 
            //Грузим дочернюю форму
            SF.Show();
        }
    }

Листинг 1.2.2. Дочерняя форма:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    //Дочерняя форма
    public partial class SlaveForm : Form
    {
        //Создаем переменную, доступную любому классу в пределах проекта
        public String InboxData = String.Empty;
        public SlaveForm()
        {
            InitializeComponent();
        }
 
        //Обрабатываем событие Load (загрузку формы), чтобы поместить полученный в переменную InboxData данные
        private void SlaveForm_Load(object sender, EventArgs e)
        {
            InboxTB.Text = InboxData;
        }
    }

Реализация 3: Передача данных через свойство/переменную статического класса.
Суть способа в следующем: использовать для временного буфера свойство или переменную статического класса. Данный способ несколько более универсальный. Хотя бы тем, что он не требует специализации класса дочерней формы, т.е. нам не придется добавлять в класс дочерней формы дополнительные свойства или переменные. Только обработать событие Load формы.

Листинг 1.3.1. Статический класс:

C#
1
2
3
4
5
6
//Статический класс, одна из переменных которого выступит в качестве буфера для данных
    public static class StaticData
    {
        //Буфер данных
        public static String DataBuffer = String.Empty;
    }

Листинг 1.3.2. Основная форма:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    //Основная форма
    public partial class MasterForm : Form
    {
        public MasterForm()
        {
            InitializeComponent();
        }
 
        private void SendB_Click(object sender, EventArgs e)
        {
            //При клике на кнопке обработчик события Click пишет строку из текстбокса в переменную статического класса,...
            StaticData.DataBuffer = DataTB.Text;
 
            //...создает экземпляр дочерней формы и отображает ее
            SlaveForm SF = new SlaveForm();
            SF.Show();
 
            //Опять же, для того, чтобы данные успешно отобразились в дочерней форме, запись их в переменную 
            //статического класса необходимо делать ДО вызова формы
        }
    }

Листинг 1.3.3. Дочерняя форма:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    //Дочерняя форма
    public partial class SlaveForm : Form
    {
        public SlaveForm()
        {
            InitializeComponent();
        }
 
        private void SlaveForm_Load(object sender, EventArgs e)
        {
            //При возникновении события Load обработчик считывает данные из статического буфера и отображает их в текстбоксе
            InboxTB.Text = StaticData.DataBuffer;
        }
    }

Реализация 4: Запись данных напрямую в TextBox дочерней формы через обработку события Load анонимным методом.
Не самый лучший вариант, попахивающий карри и индийскими слонами, но для полноты картины продемонстрирую и его. Суть способа в том, что в основной форме при обработке события Click на кнопке с помощью анонимного метода подписаться на событие Load дочерней формы и задать для этого события обработчик. А в обработчике уже производить присвоение свойству Text текстбокса дочерней формы каких-либо значений. Текстбоксу дочерней формы в этом случае должен быть присвоен модификатор public.
Листинг 1.4.1 Основная форма:

C#
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
    //Основная форма
    public partial class MasterForm : Form
    {
        public MasterForm()
        {
            InitializeComponent();
        }
 
        //Обработчик события Click
        private void SendB_Click(object sender, EventArgs e)
        {
            //Создаем экземпляр формы
            SlaveForm SF = new SlaveForm();
 
            //С помощью анонимного метода обрабатываем событие Load дочерней формы в основной форме
            SF.Load += (sender1, e1) =>
                {
                    //В свойство Text текстбокса на дочерней форме пишем данные из свойста Text текстбокса основной формы
                    SF.InboxTB.Text = DataTB.Text;
                };
 
            //После того, как подписались на событие Load, загружаем форму
            SF.Show();
        }
    }

Листинг 1.4.2. Дочерняя форма:

C#
1
2
3
4
5
6
7
8
9
    //Класс дочерней формы. Тут мы ничего не делаем, за исключением того, что меняем модификатор доступа для 
    //текстбокса с private на public в свойствах этого текстбокса
    public partial class SlaveForm : Form
    {
        public SlaveForm()
        {
            InitializeComponent();
        }
    }

Задача 2. Передать данные из дочерней формы в основную
Реализация 1. Через статический класс. Тут, в общем то, все достаточно просто и похоже на подобную реализацию выше. Но есть и пара нюансов.
Поскольку по умолчанию основная форма «не знает», когда из дочерней в переменную статического класса будет записано значение, встает проблема — обновить текстбокс основной формы именно тогда, когда в статический класс будут внесены данные. В самом первом приближении это возможно при выполнении следующего условия — дочерняя форма открыта как диалог (т.е. управление передается на дочернюю форму при ее закрытии), а обновление текстбокса основной формы происходит после метода открытия дочерней формы.
Листинг 2.1.1. Статический класс

C#
1
2
3
4
5
6
//Статический класс
    public static class StaticData
    {
        //Статическая переменная, выступающая как буфер данных
        public static String DataBuffer = String.Empty;
    }

Листинг 2.1.2. Основная форма

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Основная форма
    public partial class MasterForm : Form
    {
        public MasterForm()
        {
            InitializeComponent();
        }
 
        //Вызов дочерней формы как диалога
        private void CallB_Click(object sender, EventArgs e)
        {
            SlaveForm SF = new SlaveForm();
            SF.ShowDialog();
            //Обновление текстбокса после закрытия дочерней формы
            DataTB.Text = StaticData.DataBuffer;
        }
    }

Листинг 2.1.3. Дочерняя форма

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Дочерняя форма
    public partial class SlaveForm : Form
    {
        public SlaveForm()
        {
            InitializeComponent();
        }
 
        private void SendB_Click(object sender, EventArgs e)
        {
            //По клику заполняем статическую переменную 
            StaticData.DataBuffer = OutboxTB.Text;
        }
    }

Реализация 2. Через событие закрытия окна.
При вызове дочерней формы мы можем с помощью анонимного метода подписаться на события вызываемой формы. Например, если мы подпишемся на событие закрытия окна, то сможем выполнить некие действия, когда дочерняя форма инициирует это событие (т.е. начнет закрываться).

Листинг 2.2.1. Основная форма

C#
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
    //Основная форма
    public partial class MasterForm : Form
    {
        public MasterForm()
        {
            InitializeComponent();
        }
 
        //Открываем дочернюю форму и подписываемся на ее события
        private void CallB_Click(object sender, EventArgs e)
        {
            //Создаем экземпляр формы
            SlaveForm SF = new SlaveForm();
 
            //Создаем анонимный метод - обработчик события FormClosing дочерней формы (возникающего перед закрытием)
            //Подписаться на событие необходимо до открытия дочерней формы
            //Использовать событие FormClosed не стоит, так как оно возникает уже после закрытия формы, когда все переменные формы уже уничтожены
            SF.FormClosing += (sender1, e1) =>
                {
                    //Обновляем текстбокс основной формы
                    DataTB.Text = SF.DataBuffer;
                };
 
            //Открывает форму на просмотр
            SF.Show();
        }
    }

Листинг 2.2.2. Дочерняя форма

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    //Дочерняя форма
    public partial class SlaveForm : Form
    {
        //Переменная, выполняющая роль буфера для данных
        //Переменная должна быть public, иначе мы не получим к ней доступ из основной формы
        public String DataBuffer = String.Empty;
        public SlaveForm()
        {
            InitializeComponent();
        }
 
        //Обрабатываем клик по кнопке
        private void SendB_Click(object sender, EventArgs e)
        {
            //Локальная переменная получает значение из текстбокса
            DataBuffer = OutboxTB.Text;
        }
    }

Кроме событий формы, можно подписаться на события любых public-компонентов. Но не рекомендую этого делать — это, конечно, легко и просто, но… некрасиво, что ли.

Реализация 3. Через события статического класса.
Опять задействуем посредника в виде статического класса. Однако применим на этот раз иной подход. В основной форме подпишемся на событие ValueChanged статического свойства DataBuffer. Но, поскольку свойство это «из коробки» не имеет подобных событий, его придется создать.

Листинг 2.3.1. Статический класс

C#
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
    //Статический класс
    public static class StaticData
    {
        //Описание делегата - обработчика события
        public delegate void ValueChangedEventHandler(object sender, EventArgs e);
 
        //Событие 
        public static event ValueChangedEventHandler ValueChanged;
 
        //Изолированная переменная - хранилище данных, передаваемых в свойство DataBuffer
        private static String dataBuffer = String.Empty;
 
        //Свойство DataBuffer
        public static String DataBuffer
        {
            get
            {
                return dataBuffer;
            }
            set
            {
                dataBuffer = value;
 
                //При изменении данных свойства вызывается событие ValueChanged
                ValueChanged(null, EventArgs.Empty);
            }
        }
    }

Листинг 2.3.2. Основная форма

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    //Основная форма
    public partial class MasterForm : Form
    {
        public MasterForm()
        {
            InitializeComponent();
        }
 
        //Обработка клика по кнопке
        private void CallB_Click(object sender, EventArgs e)
        {
            //Подписываемся на событие ValueChanged статического класса
            StaticData.ValueChanged += (sender1, e1) =>
                {
                    DataTB.Text = StaticData.DataBuffer;
                };
 
            //Создаем экземпляр формы
            SlaveForm SF = new SlaveForm();
 
            //Вызываем форму
            SF.Show();
        }
    }

Листинг 2.3.3. Дочерняя форма

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    //Дочерняя форма
    public partial class SlaveForm : Form
    {
        public SlaveForm()
        {
            InitializeComponent();
        }
 
        private void SendB_Click(object sender, EventArgs e)
        {
            //По клику на кнопке пишем данные из текстбокса в свойство статического класса
            StaticData.DataBuffer = OutboxTB.Text;
        }
    }

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

На данный момент вроде как все. Скорее всего что-то забыл, поэтому к критике в комментариях буду прислушиваться особенно внимательно.
Best Regards, Aexx



6



Многие, кто начинал изучение WinForm приложений, сталкивались с проблемой обмена данными между окнами программы. Обмен данными между формами сводится к трем основным принципам: передача параметров в форму, получение данных из формы, изменение значений другой формы. Начнем по порядку.

1) Передача параметров в форму. Любой класс, должен иметь конструктор, и WinForm в этом случае не исключение. А следовательно очевидным является тот факт, что передача данных необходимых для инициализации формы необходимо проводить именно через конструктор формы. Приведем пример.

Создадим WinForm и перейдем к коду. Наблюдаем следующую картину:


public partial class Form1 : Form
    {
        public Form1() // <-- Конструктор формы по умолчанию
        {
            InitializeComponent();
        } }

Допустим на данной форме размещен элемент textBox в который мы хотим установить значение, при открытии нашей формы. Тогда модифицируем наш код следующим образом:


public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        } 
        public Form1(string text) // <-- Новый конструктор формы
        {
            InitializeComponent();
            textBox.Text = text;
        }
    }

Важно: Все действия, выполняемые с объектами формы должны быть произведены после функции InitializeComponent()!

2-3) По сути задача получения данных из формы и изменение данных в форме сводистя к одной задаче. Пусть у нас есть две формы Form1 и Form2. В первой форме у нас есть три кнопки: open, read, write . Первая откроет вторую форму, в которой есть элемент textBox, Вторая покажет сообщение с текстом, введенным в textBox формы номер два, третья очистит textBox из Form2. Имеем:


public partial class Form1 : Form
    {
        private Form2 F2 = new Form2();
 //<--Объявляем форму два как элемент класса формы один

        public Form1()
        {
            InitializeComponent();
        }
        private void open_Click(object sender, EventArgs e)
        {
            F2.ShowDialog();
        }
        private void read_Click(object sender, EventArgs e)
        {
            MessageBox.Show(F2.textBoxValue);
        }
        private void write_Click(object sender, EventArgs e)
        {
            F2.textBoxValue = String.Empty;
        }
    }

    public partial class Form2 : Form
    {
        public string textBoxValue
//<--Данная конструкция позволяет получить доступ
//к private элементам формы
        {
            get { return textBox.Text; }
            set { textBox.Text = value; }
        }
        public Form2()
        {
            InitializeComponent();
        }
    }

Вот и все. Оказалось не все так сложно 😉

In any but the smallest of WinForms apps, we’ll have multiple Forms interacting with one another. And while every app we write will be different, there’s really only a couple of ways for two Forms to pass data back and forth.

If you’d like to follow along while you read, the code in this article is available on GitHub.

Let’s take a closer look.

Child Form Pushes Data Back to Parent

When one Form displays another Form to collect user input, or to display a database record for editing, the changes a user makes need to make it back to the first Form or risk being lost.

One way to pass the data is to push it back to the parent Form from the child Form. A Form is just another class, and in order to call methods or change properties of any class, you need to have a reference to an instance of it. There’s some caveats to doing it this way, which I’ll point out in a minute.

Let’s assume we have a simple app with just two Forms — a parent and a child. Here’s the code behind ParentForm:

private void btnGetUserInput_Click(object sender, EventArgs e)
{
    using (var childForm = new ChildForm(this))
    {
        childForm.ShowDialog();
    }
}

public void SetName(string name)
{
    lblName.Text = name;
}

public int Age
{
    set { lblAge.Text = value.ToString(); }
}

It displays ChildForm on a button push, sending it a reference to itself. It also defines a method and a property, made public so the child Form can access them.

Now let’s look at the code behind ChildForm:

private readonly ParentForm parentForm;

public ChildForm(ParentForm form)
{
    InitializeComponent();
    parentForm = form;
}

private void btnSaveInput_Click(object sender, EventArgs e)
{
    parentForm.SetName(txtName.Text);

    parentForm.Age = int.TryParse(txtAge.Text, out var age) ? age : 0;
}

In the constructor, it accepts the reference from ParentForm and stores it. When the «Save Input» button is pushed, it uses that reference to call the method and property on ParentForm, passing the data back to it.

So what’s so bad about this code?

Problems With This Approach

I see two issues with this code, and the first is reusability. Imagine that next week, we want to use ChildForm from another, new Form. ChildForm will need to be reworked because it currently has a constructor that expects to be passed ParentForm. We’ll have to add more code and more complexity to ChildForm, so it’s not easily reusable.

The second issue is that ChildForm has knowledge it doesn’t need. There is no reason for ChildForm to know about other forms, user controls, class libraries, etc that could potentially use it. In general, a thing being called should know very little (or nothing) about the thing calling it.

Imagine as our app grows, we have two Forms calling ChildForm to get a name and age. One calls a new instance of ChildForm, passing an instance of itself, and has a public EmployeeName property that ChildForm can call:

private void btnGetUserInput_Click(object sender, EventArgs e)
{
    using (var childForm = new ChildForm(this))
    {
        childForm.ShowDialog();
    }
}

public string EmployeeName
{
    set { lblName.Text = value; }
}

The other does the same, except it has a public SetStudentName() method for ChildForm to call instead:

private void btnGetUserInput_Click(object sender, EventArgs e)
{
    using (var childForm = new ChildForm(this))
    {
        childForm.ShowDialog();
    }
}

public void SetStudentName(string name)
{
    lblStudentName.Text = name;
}

The logic in ChildForm increases in complexity with each new object calling it:

private readonly ParentForm1 parentForm1;
private readonly ParentForm2 parentForm2;

public ChildForm(ParentForm1 form)
{
    InitializeComponent();
    parentForm1 = form;
}

public ChildForm(ParentForm2 form)
{
    InitializeComponent();
    parentForm2 = form;
}

private void btnSaveInput_Click(object sender, EventArgs e)
{
    if (parentForm1 != null)
        parentForm1.EmployeeName = txtName.Text;
    else if (parentForm2 != null)
        parentForm2.SetStudentName(txtName.Text);
}

Now ChildForm needs to know about two potential callers, as well as which property or method to call, depending on how it was called. We could try to simplify things, perhaps by introducing an IParentForm interface, but no matter what, things are getting ugly.

Let’s look at a better way…

Parent Form Pulls Data from Child

If we make the data available from the second Form, then let the individual callers retrieve as much or as little of the data as they need, then ChildForm doesn’t need to change at all, no matter how many other objects are referencing it.

The easiest way to do this is to create public «getter» methods on ChildForm:

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

    public new string Name => txtName.Text;

    public int Age => int.TryParse(txtAge.Text, out int result) ? result : 0;
}

The two fields on ChildForm, to collect a name and age, are both made accessible to other forms, classes, etc that might need them. It doesn’t know for sure that a caller will need them, or anything else about potential callers – and that’s a good thing.

Here’s how one Form might call ChildForm, and it only cares about the name:

private void btnGetUserInput_Click(object sender, EventArgs e)
{
    using (var childForm = new ChildForm())
    {
        childForm.ShowDialog();

        lblEmployeeName.Text = childForm.Name;
    }
}

Then another Form calls ChildForm, this time keeping both name and age:

private void btnGetUserInput_Click(object sender, EventArgs e)
{
    using (var childForm = new ChildForm())
    {
        childForm.ShowDialog();

        lblStudentName.Text = childForm.Name;
        lblAge.Text = childForm.Age.ToString();
    }
}

The ChildForm is left cleaner. It requires no special knowledge of its callers, and has greater reusability and maintainability. The callers can grab as much or as little data as they need, or do nothing at all.

Final Thoughts

There’s two practical choices for passing data between two Forms:

  • While we’re still in Form2, push data back to Form1.
  • After we return to Form1, pull data from Form2.

The second option leads to easier code maintenance and greater usability.

I hope this helped clarify a few things. If it still seems unclear, or you see a possible error somewhere, leave a comment below and we’ll figure it out!

This article is aimed at providing some simple methods for passing data between forms in Windows applications

  • Download demo project — 51.01 KB

Sample Image - PassData.jpg

Passing Data Between Forms

Introduction

Some of you would have faced a scenario where you wanted to pass data from one form to another in WinForms. Honestly, I too had a similar problem (that’s why I am writing this article!).

There are so many methods (How many? I don’t know) to pass data between forms in Windows application. In this article, let me take four important (easiest) ways of accomplishing this:

  1. Using constructor
  2. Using objects
  3. Using properties
  4. Using delegates

Let us see all the above methods in detail in the following sections.

For data to be passed between forms using any of the above methods, we need two forms and some controls. Let us start by following the steps given below.

Step 1

Create a new project and select Windows application. This will create a default form as “Form1”. We can use this form for sending data.

Step 2

Add a textbox and a button to the form.

Step 3

Add another Windows Form for receiving the data and to display it. Right click the project and select Add->Add Windows Form. Enter a name or use the default name “Form2.cs” and click ok button.

Step 4

Add a label to the second form to display the text from form1.

The Constructor Approach

This could be the easiest method of all. A method is invoked whenever you instantiate an object. This method is called a constructor. Code a constructor for form2 class with one string parameter. In the constructor, assign the text to the label’s text property. Instantiate form2 class in form1’s button click event handler using the constructor with one string parameter and pass the textbox’s text to the constructor.

Follow the steps given below:

Step 1

Code a constructor for form2 class as below:

public Form2(string strTextBox)
{
  InitializeComponent(); 
  label1.Text=strTextBox;
}

Step 2

Instantiate form2 class in form1’s button click event handler as below:

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

The Object Approach

Objects are reference types, and are created on the heap, using the keyword new. Here we are going to pass data using objects. The approach is simple; in form2 we are going to instantiate form1 class.

Then instantiate form2 in the button click event handler of form1. After this we are going to pass form1 object to the form2 using form2’s form1 object. The last step is to invoke the form2 window by calling the form2’s show method.

Follow the below steps:

Step 1

Change the access modifier for textbox in form1 to public:

public class Form1 : System.Windows.Forms.Form
{  
 public System.Windows.Forms.TextBox textBox1;

Step 2

In the button click event-handler, add the following code:

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

Step 3

In form2.cs, instantiate form1 class:

public class Form2 : System.Windows.Forms.Form
{
     private System.Windows.Forms.Label label1;
     public Form1 frm1;

Step 4

In Form2’s Load method, type cast the object (frm1) of form1 to Form1 and access form1’s textbox and assign its text to label’s text.

private void Form2_Load(object sender, System.EventArgs e)
{
    label1.Text=((Form1)frm1).textBox1.Text;
}

The Properties Approach

Properties allow clients to access class state as if they were accessing member fields directly, while actually implementing that access through a class method. In this method, we are going to add one property to each form. In form1 we are going to use one property for retrieving value from the textbox and in form2, one property to set the label’s text property. Then, in form1’s button click event handler, we are going to instantiate form2 and use the form2’s property to set the label’s text.

Follow the below steps:

Step 1

Add a property in form1 to retrieve value from textbox:

public string _textBox1
{
    get{return textBox1.Text;}
}

Step 2

Add a property in form2 to set the labels’ text:

public string _textBox
{
   set{label1.Text=value;}
}

Step 3

In form1’s button click event handler, add the following code:

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

The Delegates Approach

Technically, a delegate is a reference type used to encapsulate a method with a specific signature and return type. You can encapsulate any matching method in that delegate. Here we are going to create a delegate with some signature and assign a function to the delegate to assign the text from textbox to label.

Follow the below steps:

Step 1

Add a delegate signature to form1 as below:

public delegate void delPassData(TextBox text);

Step 2

In form1’s button click event handler, instantiate form2 class and delegate. Assign a function in form2 to the delegate and call the delegate as below:

private void btnSend_Click(object sender, System.EventArgs e)
{
    Form2 frm= new Form2();
    delPassData del=new delPassData(frm.funData);
    del(this.textBox1);
    frm.Show();
}

Step 3

In form2, add a function to which the delegate should point to. This function will assign textbox’s text to the label:

public void funData(TextBox txtForm1)
{
    label1.Text = txtForm1.Text;
}

Conclusion

These four approaches are very simple in implementing data passing between forms. There are also other methods available in accomplishing the same. Source code for the methods I stated above is given at the top for download. It is time for you to put on your thinking cap and find other ways of doing this. Happy coding!!!

History

  • 16th May, 2006: Initial post

Introduction

Communication between multiple forms can be accomplished several ways, from exposing properties and/or events with public modifiers rather than the default of private modifiers which is okay for the occasional lobbyist while this is bad practice when writing professional solution in Visual Studio. The intent in this article is to provide an easy to use set of classes to allow information to be passed between two or more forms in a Visual Studio C# WinForm project.

Note
If the intent is to create more than simple communication, passing of strings or complex objects between form, instead require a more robust tool e.g. chat server, chat client look at using asynchronous sockets.

Requires

All code presented requires Visual Studio 2022 17.4 and higher. There is also code for classic .NET Framework for both C# and VB.NET which were the originals and upgrades in new projects to .NET Core.

Common types to pass between forms

Typically a developer will need to pass a string, date, numeric or an instance of a model/class. To achieve this create a base class responsible for sending information between forms.

The following class and interface are in a class project included in the supplied source code.

public class Dispatcher
{
    private readonly Collection<IMessageListener1> _listeners = new();

    /// <summary>
    /// Send message
    /// </summary>
    /// <param name="message">Message</param>
    /// <param name="sender"></param>
    /// <remarks></remarks>
    [DebuggerStepThrough()]
    public void Broadcast(string message, Form sender)
    {
        foreach (IMessageListener1 listener in _listeners)
        {
            listener.OnListen(message, sender);
        }
    }

    /// <summary>
    /// Send int
    /// </summary>
    /// <param name="value">value to sender</param>
    /// <param name="sender">form which sent the value</param>
    [DebuggerStepThrough()]
    public void Broadcast(int value, Form sender)
    {
        foreach (IMessageListener1 listener in _listeners)
        {
            listener.OnListen(value, sender);
        }
    }

    /// <summary>
    /// Send object
    /// </summary>
    /// <param name="value">value to sender</param>
    /// <param name="sender">form which sent the value</param>
    [DebuggerStepThrough()]
    public void Broadcast(object value, Form sender)
    {
        foreach (IMessageListener1 listener in _listeners)
        {
            listener.OnListen(value, sender);
        }
    }

    /// <summary>
    /// Add a Listener to the Collection of Listeners
    /// </summary>
    /// <param name="listener"></param>
    public void AddListener(IMessageListener1 listener)
    {
        _listeners.Add(listener);
    }
    /// <summary>
    /// Remove a Listener from the collection
    /// </summary>
    /// <param name="listener"></param>
    public void RemoveListener(IMessageListener1 listener)
    {

        for (int index = 0; index < _listeners.Count; index++)
        {
            if ( _listeners[index].Equals(listener) )
            {
                _listeners.Remove(_listeners[index]);
            }
        }
    }
}

Enter fullscreen mode

Exit fullscreen mode

Interface for above class

public interface IMessageListener1 
{
    void OnListen(string message, Form form);
    void OnListen(int value, Form form);
    void OnListen(object sender, Form form);
}

Enter fullscreen mode

Exit fullscreen mode

Implementation

For each form which will participate,

Add the following using

using static WinFormLibrary.Classes.Factory

Enter fullscreen mode

Exit fullscreen mode

implement the Interface.

public partial class Form1 : Form, IMessageListener1

Enter fullscreen mode

Exit fullscreen mode

In form load, add the form to the collection in the Dispatcher class.

Dispatcher().AddListener(this);

Enter fullscreen mode

Exit fullscreen mode

Next subscribe to form closing event, add the following code to remove the form from the collection.

Dispatcher().RemoveListener(this);

Enter fullscreen mode

Exit fullscreen mode

Example 1

There is a main form, one child form where if a int value exceeds a max value, change a picture box image from green to red and when the value is less than max value change the picture box image to green.

In the main form, when a NumericUpDown value changes its broadcasted to all listeners.

private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
    Dispatcher()
        .Broadcast((int)numericUpDown1.Value, this);
}

Enter fullscreen mode

Exit fullscreen mode

Each form which implemented IMessageListener1 will have an event.

public void OnListen(int value, Form form)
{
    if (form is Form1)
    {
        pictureBox1.Image = value > 10 ? 
            Properties.Resources.red : 
            Properties.Resources.green;
    }
}

Enter fullscreen mode

Exit fullscreen mode

In the code sample, start the project, click the button Show child form which opens two instances of the form. On the main form, change the value for the NumericUpDown to over ten then under 10 and all three forms picture boxes image changes.

Example 2

The objective is to pass a class instance between forms. Since the class project knows nothing about our classes there is the following.

void OnListen(object sender, Form form);

Enter fullscreen mode

Exit fullscreen mode

Which allows passing, in this case a person.

To keep things interesting Bogus library is used to create a Person instance.

ic class BogusOperations
{
    public static List<Person> People(int count = 1)
    {
        int identifier = 1;

        Faker<Person> fakePerson = new Faker<Person>()
                .CustomInstantiator(f => new Person(identifier++))
                .RuleFor(p => p.FirstName, f => 
                    f.Person.FirstName)
                .RuleFor(p => p.LastName, f => 
                    f.Person.LastName)
                .RuleFor(p => p.BirthDate, f => 
                    f.Date.Past(10))
            ;


        return fakePerson.Generate(count);

    }
}

Enter fullscreen mode

Exit fullscreen mode

To send a person to all forms listening (remember there are two instances of the same form which know nothing about each other).

private void BogusButton_Click(object sender, EventArgs e)
{
    Dispatcher()
        .Broadcast(BogusOperations.People()
            .FirstOrDefault()!, this);

}

Enter fullscreen mode

Exit fullscreen mode

Child form event

public void OnListen(object sender, Form form)
{
    if (sender is Person person)
    {
        MessageBox.Show(
            $"""
                    {Text}
                    {person.FirstName} {person.LastName}
                 """);
    }
}

Enter fullscreen mode

Exit fullscreen mode

Using code in your project

  • Add the project WinFormLibrary to a Visual Studio solution
  • Add a reference to WinFormLibrary to your project
    • Copy GlobalUsings.cs from the code sample to your project

Source code

Clone the following GitHub repository, do not download as a .zip file as once extracted forms will have mark of the web set and in turn Visual Studio will not compile until mark of the web is removed.

See also

Microsoft TechNet: original article using .NET Framework

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Звук стал как из бочки windows 10
  • Добавление удаление компонентов windows
  • Honor magicbook 16 драйвера windows 11
  • Hp ink tank 115 драйвер для windows 10
  • Как выделить объект windows