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]);
}
}
}
}
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);
}
Implementation
For each form which will participate,
Add the following using
using static WinFormLibrary.Classes.Factory
implement the Interface.
public partial class Form1 : Form, IMessageListener1
In form load, add the form to the collection in the Dispatcher class.
Dispatcher().AddListener(this);
Next subscribe to form closing event, add the following code to remove the form from the collection.
Dispatcher().RemoveListener(this);
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);
}
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;
}
}
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);
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);
}
}
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);
}
Child form event
public void OnListen(object sender, Form form)
{
if (sender is Person person)
{
MessageBox.Show(
$"""
{Text}
{person.FirstName} {person.LastName}
""");
}
}
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