Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# Windows Forms
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.98/65: Рейтинг темы: голосов - 65, средняя оценка - 4.98
Aexx
17 / 17 / 0
Регистрация: 04.04.2012
Сообщений: 31
1

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

06.04.2012, 05:44. Просмотров 12582. Ответов 3
Метки нет (Все метки)

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
3
Вложения
Тип файла: zip AexxBlog_SendData_p1a1.zip (50.0 Кб, 57 просмотров)
Тип файла: zip AexxBlog_SendData_p1a2.zip (50.7 Кб, 38 просмотров)
Тип файла: zip AexxBlog_SendData_p1a3.zip (50.5 Кб, 43 просмотров)
Тип файла: zip AexxBlog_SendData_p1a4.zip (50.6 Кб, 38 просмотров)
Тип файла: zip AexxBlog_SendData_p2a1.zip (61.0 Кб, 47 просмотров)
Тип файла: zip AexxBlog_SendData_p2a2.zip (56.1 Кб, 45 просмотров)
Тип файла: zip AexxBlog_SendData_p2a3.zip (70.6 Кб, 58 просмотров)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
06.04.2012, 05:44
Ответы с готовыми решениями:

Как передать данные между формами
Еще раз всем привет. У меня на Form1 есть 2 radioButton (radioButton1 и...

Как передать данные между формами, без Show/ShowDialog
Есть 3 формы: 1. MainForm, которая является меню и родительской формой, на...

передать данные между формами
Задание такое: на основной форме нажимаем кнопку "ввести данные"- открывается...

Как передавать данные из одной формы в другую?
помогите сделать через location кузнечика типа как в Кумире. не получается...

Как передавать данные из TextBox в ListBox из другой формы?
Подскажите как передавать данные из textBox в listBox из другой формы? Прочитал...

3
turbanoff
Эксперт Java
4024 / 3759 / 742
Регистрация: 18.05.2010
Сообщений: 9,330
Записей в блоге: 11
Завершенные тесты: 1
06.04.2012, 08:26 2
Так вроде уже есть тема:
Ответы на 7 самых частых вопросов по WinForms
Вопрос номер 2
0
Aexx
17 / 17 / 0
Регистрация: 04.04.2012
Сообщений: 31
06.04.2012, 08:28  [ТС] 3
Хм, действительно. А слона то я и не приметил. Можно удалить тему, опубликую в блоге
0
Win-ni
11 / 11 / 1
Регистрация: 16.08.2011
Сообщений: 70
30.07.2012, 18:01 4
Здравствуйте !

А можно ли как-то решить эту задачу, если данными является массив (матрица) и размеры её определяются при работе программы? Сейчас ещё загляну в эти 7 вопросов, может там есть.
- Однако, нет там этой проблемы. Вероятно, сделаю статический класс, а размеры матрицы - с запасом.
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
30.07.2012, 18:01

Как переключаться между несколькими формами?
у меня в приложении несколько форм, как мне между ними переключаться?

Как осуществить переход между формами
Можно ли как то запомнить с какой формы был совершен переход. у меня есть 3...

Как реализовать переход между формами?
Нашёл лишь такую реализацию перехода меж формами, но она далеко не лучшая, т.к....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru