How to call function from another form
Categories:
How to Call a Function from Another Form in C# WinForms

Learn various techniques to call methods and access controls across different forms in C# Windows Forms applications, ensuring proper communication and data flow.
In C# Windows Forms development, it's a common requirement for one form to interact with another. This interaction often involves calling a public method on a different form instance or accessing/modifying controls on another form. This article explores several robust and recommended patterns for achieving inter-form communication, focusing on maintainability, encapsulation, and avoiding common pitfalls.
Understanding the Challenge: Form Instances
The primary challenge in calling a function from another form lies in managing form instances. Each form you create and show in your application is typically a separate object in memory. To call a method on FormB
from FormA
, FormA
needs a reference to the specific instance of FormB
that it intends to interact with. Without this reference, FormA
wouldn't know which FormB
to communicate with, or it might inadvertently create a new, unintended instance of FormB
.
flowchart TD A[FormA] --> B{Needs to call function on FormB} B -- Has reference to FormB instance --> C[FormB.MyPublicMethod()] B -- Creates new FormB instance --> D[New FormB.MyPublicMethod()] C -- Executes on existing FormB --> E[Desired Outcome] D -- Executes on new, hidden FormB --> F[Undesired Outcome]
The importance of referencing an existing form instance.
Method 1: Passing Form References (Constructor/Property)
One of the most straightforward and widely used methods is to pass a reference of the calling form to the called form. This can be done either through the constructor of the child form or by setting a public property after the child form has been instantiated. This allows the child form to directly invoke public methods or properties on its parent or owner form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdateLabel(string message)
{
this.label1.Text = message;
}
private void button1_Click(object sender, EventArgs e)
{
Form2 childForm = new Form2(this); // Pass reference via constructor
childForm.Show();
}
}
public partial class Form2 : Form
{
private Form1 _parentForm;
public Form2(Form1 parentForm)
{
InitializeComponent();
_parentForm = parentForm;
}
private void button1_Click(object sender, EventArgs e)
{
_parentForm.UpdateLabel("Message from Form2!"); // Call method on parent
this.Close();
}
}
Method 2: Using Events for Decoupled Communication
Events provide a powerful and decoupled way for forms to communicate. The child form can define an event, and the parent form can subscribe to this event. When the child form needs to notify the parent or trigger an action, it raises the event. This pattern is particularly useful when the child form doesn't need to know the specifics of the parent form, only that something needs to be communicated.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form3 childForm = new Form3();
childForm.DataUpdated += ChildForm_DataUpdated; // Subscribe to event
childForm.Show();
}
private void ChildForm_DataUpdated(object sender, string data)
{
this.label1.Text = $"Data from Form3: {data}";
}
}
public partial class Form3 : Form
{
// Define a delegate for the event handler
public delegate void DataUpdatedEventHandler(object sender, string data);
// Define the event
public event DataUpdatedEventHandler DataUpdated;
public Form3()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Raise the event when data is updated
DataUpdated?.Invoke(this, textBox1.Text);
this.Close();
}
}
sequenceDiagram participant FormA participant FormB FormA->>FormB: Show FormB FormA->>FormA: Subscribe to FormB.DataUpdated event FormB->>FormB: User enters data FormB->>FormB: User clicks 'Save' FormB-->>FormA: Raise DataUpdated event (with data) FormA->>FormA: Handle DataUpdated event (update UI) FormB->>FormB: Close FormB
Sequence diagram for event-based communication between forms.
Method 3: Using a Static Instance (Singleton Pattern)
For scenarios where you need a single, globally accessible instance of a form (e.g., a main application window or a logging window), the Singleton pattern can be applied. This ensures that only one instance of the form exists throughout the application's lifetime, and other forms can access it directly via a static property.
public partial class MainForm : Form
{
private static MainForm _instance;
public static MainForm Instance
{
get
{
if (_instance == null || _instance.IsDisposed)
{
_instance = new MainForm();
}
return _instance;
}
}
private MainForm()
{
InitializeComponent();
}
public void LogMessage(string message)
{
this.textBoxLog.AppendText(message + Environment.NewLine);
}
private void buttonOpenChild_Click(object sender, EventArgs e)
{
ChildForm child = new ChildForm();
child.Show();
}
}
public partial class ChildForm : Form
{
public ChildForm()
{
InitializeComponent();
}
private void buttonSendMessage_Click(object sender, EventArgs e)
{
MainForm.Instance.LogMessage("Message from ChildForm: " + DateTime.Now.ToShortTimeString());
}
}
Accessing Controls on Another Form
Directly accessing controls on another form is generally discouraged as it breaks encapsulation and makes your code harder to maintain. Instead, expose public methods or properties on the target form that encapsulate the control's functionality. For example, instead of formB.textBox1.Text = "Hello";
, create a method formB.SetTextBoxText("Hello");
.
public partial class FormA : Form
{
public FormA()
{
InitializeComponent();
}
public void SetStatusText(string status)
{
// Ensure thread safety if FormA's UI is updated from a different thread
if (this.InvokeRequired)
{
this.Invoke(new Action<string>(SetStatusText), status);
}
else
{
this.labelStatus.Text = status;
}
}
private void buttonOpenFormB_Click(object sender, EventArgs e)
{
FormB formB = new FormB(this);
formB.Show();
}
}
public partial class FormB : Form
{
private FormA _ownerForm;
public FormB(FormA ownerForm)
{
InitializeComponent();
_ownerForm = ownerForm;
}
private void buttonUpdateStatus_Click(object sender, EventArgs e)
{
_ownerForm.SetStatusText("Status updated from FormB!");
}
}
Invoke
or BeginInvoke
to marshal the call to the UI thread. Failure to do so will result in a System.InvalidOperationException
.Choosing the right method depends on the specific requirements of your application. For simple parent-child relationships, passing references might suffice. For more complex, decoupled interactions, events are generally preferred. The Singleton pattern is best reserved for truly unique, application-wide forms.