WinForms communicate between forms

Karen Payne - Apr 9 '23 - - Dev Community

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]);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

Implementation

For each form which will participate,

Add the following using

using static WinFormLibrary.Classes.Factory
Enter fullscreen mode Exit fullscreen mode

implement the Interface.

public partial class Form1 : Form, IMessageListener1
Enter fullscreen mode Exit fullscreen mode

In form load, add the form to the collection in the Dispatcher class.

Dispatcher().AddListener(this);
Enter fullscreen mode Exit fullscreen mode

Next subscribe to form closing event, add the following code to remove the form from the collection.

Dispatcher().RemoveListener(this);
Enter fullscreen mode Exit fullscreen mode

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.

main form screenshot

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);

    }
}
Enter fullscreen mode Exit fullscreen mode

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);

}
Enter fullscreen mode Exit fullscreen mode

Child form event

public void OnListen(object sender, Form form)
{
    if (sender is Person person)
    {
        MessageBox.Show(
            $"""
                    {Text}
                    {person.FirstName} {person.LastName}
                 """);
    }
}
Enter fullscreen mode Exit fullscreen mode

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .