Методичка з робочими прикладами на C# (Windows Forms)
📌 Ключова ідея: Форма в C# — це звичайний клас, похідний від System.Windows.Forms.Form.
Тому об'єкт форми можна передавати в методи інших форм як параметр, зберігати посилання та використовувати для доступу до публічних членів.
1. Чому передавати форму як параметр?
Існує кілька сценаріїв, коли це корисно:
🔸 Потрібно з однієї форми керувати елементами іншої форми
🔸 Необхідно оновлювати дані в головній формі з дочірньої
🔸 Реалізація патерну "Observer" або "Mediator" між формами
🔸 Створення складних інтерфейсів з багатосторонньою взаємодією
💡 Важливо: При передачі форми передається саме посилання на об'єкт, тому всі зміни, зроблені через це посилання, впливають на оригінальну форму.
Жива демонстрація: як працює передача форми
Form1 — Головна форма
×
Статус: Form2 закрита
Поточний колір: LightBlue
Form2 — Дочірня форма
×
Отримано посилання на Form1
Форма відкрита
Поточний колір: LightGreen
2. Базовий приклад: передача Form1 в конструктор Form2
Найпростіший спосіб — передати посилання на форму через конструктор дочірньої форми.
📁 Form2.cs (конструктор з параметром)
using System.Windows.Forms;
namespaceПередачаФорми
{
public partial classForm2 : Form
{
// Зберігаємо посилання на головну формуprivate Form1 _mainForm;
// Конструктор, який приймає посилання на Form1publicForm2(Form1 mainForm)
{
InitializeComponent();
_mainForm = mainForm;
// Тепер через _mainForm можна звертатись до публічних членів Form1this.Text = "Дочірня форма для " + _mainForm.Text;
}
}
}
Створимо дві форми, які можуть змінювати колір одна одної через передані посилання.
Повний робочий проект
📁 Program.cs (точка входу)
using System;
using System.Windows.Forms;
namespaceПередачаФорми
{
internal static classProgram
{
[STAThread]
static voidMain()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(newForm1());
}
}
}
📁 Form1.cs (головна форма)
using System;
using System.Drawing;
using System.Windows.Forms;
namespaceПередачаФорми
{
public partial classForm1 : Form
{
private Button btnOpenForm2;
private Button btnChangeColor;
private Label lblStatus;
// Посилання на Form2 (якщо вона відкрита)private Form2 _form2;
publicForm1()
{
InitializeComponent();
SetupForm();
}
private voidSetupForm()
{
this.Text = "Головна форма";
this.Size = new Size(400, 300);
this.StartPosition = FormStartPosition.CenterScreen;
this.BackColor = Color.LightBlue;
btnOpenForm2 = new Button
{
Text = "Відкрити Form2",
Location = new Point(120, 50),
Size = new Size(150, 35)
};
btnOpenForm2.Click += BtnOpenForm2_Click;
btnChangeColor = new Button
{
Text = "Змінити колір Form2",
Location = new Point(120, 100),
Size = new Size(150, 35),
Enabled = false// Активується після відкриття Form2
};
btnChangeColor.Click += BtnChangeColor_Click;
lblStatus = new Label
{
Text = "Статус: Form2 закрита",
Location = new Point(120, 150),
Size = new Size(200, 30)
};
this.Controls.AddRange(new Control[] { btnOpenForm2, btnChangeColor, lblStatus });
}
private voidBtnOpenForm2_Click(object sender, EventArgs e)
{
if (_form2 == null || _form2.IsDisposed)
{
// Передаємо посилання на поточну форму (this)
_form2 = newForm2(this);
_form2.Show();
_form2.FormClosed += (s, args) =>
{
btnChangeColor.Enabled = false;
lblStatus.Text = "Статус: Form2 закрита";
};
btnChangeColor.Enabled = true;
lblStatus.Text = "Статус: Form2 відкрита";
}
else
{
_form2.Focus();
}
}
private voidBtnChangeColor_Click(object sender, EventArgs e)
{
if (_form2 != null && !_form2.IsDisposed)
{
// Змінюємо колір Form2 через публічний метод
_form2.ChangeBackColor(Color.Pink);
}
}
// Публічний метод, який може викликати Form2 для зміни кольору Form1public voidChangeBackColor(Color color)
{
this.BackColor = color;
}
}
}
📁 Form2.cs (дочірня форма)
using System;
using System.Drawing;
using System.Windows.Forms;
namespaceПередачаФорми
{
public partial classForm2 : Form
{
// Зберігаємо посилання на головну формуprivate Form1 _mainForm;
private Button btnChangeMainColor;
private Label lblInfo;
// Конструктор отримує посилання на Form1publicForm2(Form1 mainForm)
{
InitializeComponent();
_mainForm = mainForm;
SetupForm();
}
private voidSetupForm()
{
this.Text = "Дочірня форма";
this.Size = new Size(350, 250);
this.StartPosition = FormStartPosition.CenterScreen;
this.BackColor = Color.LightGreen;
lblInfo = new Label
{
Text = "Ця форма отримала посилання на Form1",
Location = new Point(30, 30),
Size = new Size(250, 40),
TextAlign = ContentAlignment.MiddleCenter
};
btnChangeMainColor = new Button
{
Text = "Змінити колір головної форми",
Location = new Point(70, 100),
Size = new Size(180, 40)
};
btnChangeMainColor.Click += BtnChangeMainColor_Click;
this.Controls.AddRange(new Control[] { lblInfo, btnChangeMainColor });
}
private voidBtnChangeMainColor_Click(object sender, EventArgs e)
{
// Викликаємо публічний метод головної форми через збережене посиланняif (_mainForm != null && !_mainForm.IsDisposed)
{
_mainForm.ChangeBackColor(Color.Orange);
}
}
// Публічний метод для зміни кольору цієї форми (викликається з Form1)public voidChangeBackColor(Color color)
{
this.BackColor = color;
}
}
}
4. Альтернативний спосіб: передача через властивість
Іноді зручніше створити публічну властивість і встановити її після створення форми.
📁 Form3.cs (передача через властивість)
public partial classForm3 : Form
{
// Публічна властивість для зберігання посилання на головну формуpublic Form1 MainForm { get; set; }
publicForm3()
{
InitializeComponent();
}
private voidForm3_Load(object sender, EventArgs e)
{
if (MainForm != null)
{
this.Text = "Підключено до " + MainForm.Text;
}
}
}
// У Form1 створення:// Form3 f3 = new Form3();// f3.MainForm = this;// f3.Show();
5. Переваги та недоліки підходу
✅ Переваги
Прямий доступ до публічних членів іншої форми
Можливість двосторонньої комунікації
Простота реалізації
Немає потреби в глобальних статичних класах
Кожен екземпляр форми має своє посилання
⚠️ Застереження
Потрібно перевіряти чи форма не знищена (IsDisposed)
Можливе створення циклічних посилань
При закритті головної форми дочірні можуть залишитися "висячими"
Тісне зв'язування форм (вони знають одна про одну)
6. Патерн "Посередник" (Mediator) з передачею форм
Можна створити окремий клас-посередник, який зберігає посилання на всі форми.
📁 FormMediator.cs
using System.Collections.Generic;
using System.Windows.Forms;
namespaceПередачаФорми
{
// Клас-посередник для керування формамиpublic static classFormMediator
{
private static List<Form> _forms = new List<Form>();
public static voidRegister(Form form)
{
if (!_forms.Contains(form))
{
_forms.Add(form);
form.FormClosed += (s, e) => _forms.Remove(form);
}
}
public static voidSendMessageToAll(string message)
{
foreach (var form in _forms)
{
if (form is IMessageReceiver receiver)
{
receiver.ReceiveMessage(message);
}
}
}
}
// Інтерфейс для форм, які можуть отримувати повідомленняpublic interfaceIMessageReceiver
{
voidReceiveMessage(string message);
}
}
7. Покрокова інструкція для лабораторної роботи
📁 Створіть новий проект Windows Forms
➕ Додайте другу форму (Form2)
✏️ У Form2 створіть поле для зберігання посилання на Form1: private Form1 _mainForm;
✏️ Створіть конструктор Form2, який приймає Form1: public Form2(Form1 mainForm)
✏️ У Form1 при створенні Form2 передайте this: new Form2(this)
🔌 Додайте публічні методи в обидві форми для взаємодії
🔄 Використовуйте збережені посилання для виклику методів
⚠️ Не забувайте перевіряти !IsDisposed перед використанням
🎯 Практичне завдання: Модифікуйте приклад з кольорами так, щоб:
Form1 могла змінювати текст в Form2
Form2 могла переміщувати Form1 в інше місце екрану
Додайте третю форму, яка отримує посилання на обидві попередні
8. Поширені помилки та їх вирішення
Помилка
Рішення
"ObjectDisposedException"
Перевіряйте if (form != null && !form.IsDisposed) перед використанням