WinForms communicate between forms
Tags: dotnetcore, dotnet, codenewbie, programming
Published: 2023-04-09 19:20:13

## 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](https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/sockets/sockets-overview).

## 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](https://github.com/karenpayneoregon/BroadcastWinForms/tree/master/WinFormLibrary).

```csharp
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

```csharp
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 

```csharp
using static WinFormLibrary.Classes.Factory
```

implement the Interface.


```csharp
public partial class Form1 : Form, IMessageListener1
```

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

```csharp
Dispatcher().AddListener(this);
```
Next subscribe to form closing event, add the following code to remove the form from the collection.

```csharp
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.

![main form screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4xocjggffbzsiww0blmc.png)

In the main form, when a NumericUpDown value changes its broadcasted to all listeners.

```csharp
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
    Dispatcher()
        .Broadcast((int)numericUpDown1.Value, this);
}
```

Each form which implemented IMessageListener1 will have an event.

```csharp
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.

```csharp
void OnListen(object sender, Form form);
```
Which allows passing, in this case a [person](https://github.com/karenpayneoregon/BroadcastWinForms/blob/master/BroadcastCoreApp/Models/Person.cs).

To keep things interesting [Bogus](https://www.nuget.org/packages/Bogus) library is used to create a Person instance.

```csharp
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).

```csharp
private void BogusButton_Click(object sender, EventArgs e)
{
    Dispatcher()
        .Broadcast(BogusOperations.People()
            .FirstOrDefault()!, this);
    
}
```

Child form event

```csharp
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](https://github.com/karenpayneoregon/BroadcastWinForms), 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](https://social.technet.microsoft.com/wiki/contents/articles/53527.winforms-communicate-between-forms-c.aspx) using .NET Framework