Using a Controller and Events to decouple the forms
The correct way to do this kind of thing is to decouple the two forms from each other by introducing a Controller
class, and using events
to signal state changes.
Here's an example.
Firstly, create a new default Windows Forms app called WindowsFormsApplication1
and add two forms, form1
and form2
.
Then add to form1
a button called "button1" and a label called "label1".
Then add to form2
a checkbox called "checkbox1".
In the form1
designer, double-click the button to add a click handler for it.
In the form2
designer, double-click the checkbox to add a change handler for it.
Now add a new class called "Controller" and add to it the following code:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
internal sealed class Controller
{
public void RunForm1()
{
_form1 = new Form1();
// The next line defines our response to the button being pressed in Form1
_form1.ButtonClicked += (sender, args) => showForm2();
Application.Run(_form1);
}
private void showForm2()
{
var form2 = new Form2();
// The next line defines our response to the checkbox changing in Form2.
form2.CheckBoxChanged +=
(sender, args) =>
_form1.SetLabel("Checkbox = " + ((CheckBox)sender).Checked);
form2.ShowDialog(_form1);
}
private Form1 _form1 ;
}
}
Now change Form1.cs
to this:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1: Form
{
// Here's where we announce our event handler to the world:
public event EventHandler ButtonClicked;
public Form1()
{
InitializeComponent();
}
public void SetLabel(string text)
{
label1.Text = text;
}
private void button1_Click(object sender, EventArgs e)
{
// We make a copy of ButtonClicked before checking it for null because
// in a multithreaded environment some other thread could change it to null
// just after we checked it for nullness but before we call it, which would
// cause a null reference exception.
// A copy cannot be changed by another thread, so that's safe to use:
var handler = ButtonClicked;
if (handler != null)
handler(sender, e);
}
}
}
And change Form2.cs
to this:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form2: Form
{
// Here's the event handler for the check box:
public event EventHandler CheckBoxChanged;
public Form2()
{
InitializeComponent();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
var handler = CheckBoxChanged;
if (handler != null)
handler(sender, e);
}
}
}
Finally, change Program.cs
to this:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Controller controller = new Controller();
controller.RunForm1();
}
}
}
Now run the program and click the button, then click the checkbox a few times. You will see the label in Form1 changing.
In this way, you have completely decoupled Form1
from Form2
and placed the control logic into a separate Controller class.