Winforms Events Architecture

Viewed 94

Trying to build a simple app in winforms but I'm noticing a gap in my understanding of events inside the winforms model. I know I'm fairly close to the solution but there are a few things that I dont know how to do the "winforms way"

Here is the UI layout of what I am trying to accomplish. Form1 is generated via the VS2019 template, but both LeftSide and RightSide are two separate classes that I have built.

*****Form1************************
|                                |
| **LeftSide***   **RightSide**  |
| |           |   |           |  |
| | _TextBox  |   | _TextBox  |  |
| | _Button   |   | _Button   |  |
| |           |   |           |  |
| |           |   |           |  |
| *************   *************  |
|                                |
**********************************

Both LeftSide and RightSide:

  • return a Panel with a textbox and button
  • have 'child' controls that are not exposed (ie, the class itself is responsible for the UI layout and the data validation)
  • require communication to each other and Form1
  • are not inherited from anything; they are used in Form1 via a namespace
  • are instantiated inside Form1

Here is (an incomplete) Form1, idk what to add here

namespace myapp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

Here is both LeftSide and RightSide classes (note that they are the same, except substitute the word "left" with "right")

namespace myapp.LeftSide
{
     class LeftSide
     {
            private Panel LeftSide_Panel = new Panel();
            private Textbox LeftSide_TextBox = new Textbox();
            
            private Button LeftSide_Button = new Button();

            private string LeftSide_Data; //store TextBox.text here

            public ReturnPanel()
            {
             //return a Panel that Form1 can use
             LeftSide_Panel.Controls.Add(LeftSide_Button);
             LeftSide_Panel.Controls.Add(LeftSide_TextBox);
             return this.LeftSide_Panel;
            }
      }
}

I understand that I can make a custom event handler for LeftSide_Button by creating a function inside LeftSide like such:

private void LeftSide_Button_Click(object sender, EventArgs e)
{
 //read the textbox, assume it is valid and store the string in our LeftSide_Data
 this.LeftSide_Data = this.LeftSide_TextBox.Text;
}

The above works if I wanted to keep the data local to the class, but I'm at a loss of how to expose LeftSide_Button_Click to, primarily, Form1 and sometimes to RightSide.

My issues are:

  • if LeftSide owns the data that is inside its textbox (and stored in LeftSide_Data) and LeftSide_Button_Click is my custom validation event (validation is not shown above in order to simplify) how does Form1 become not only notified that the button has been clicked but is also sent the string that is inside LeftSide_Data?
  • how do I expose the LeftSide event to anyone who wants to subscribe? Is it with the 'delegate' keyword or the 'event' keyword? or something else? there are too many event based keywords and thus the main source of confusion.
  • should private void LeftSide_Button_Click(object sender, EventArgs e) be changed from returning void to private Eventhandler LeftSide_Button_Click(object sender, EventArgs e) and if so, what am I returning... what is an EventHandler "made of"?

EDIT: I found the following diagram, and even though it does not pass data along with the event, is implementing this a step in the right direction? enter image description here

1 Answers

You can declare a delegate that represents the signature of your custom event, providing any number of parameters that you desire to pass out. Then you simply declare an event of that delegate type. Anyone can subscribe to your custom event to be notified when it occurs.

Here's a simple example with the delegate called dlgLeftSideData, and the event called LeftSideData. It passes out the sender (instance of LeftSide producing the event) as well as the string data. There is also a public read-only property that you could access via the sender if you wanted to. I've wired up the click event of the button to do "validation" and then it raises the custom event if there are any subscribers:

class LeftSide
{

    public event dlgLeftSideData LeftSideData;
    public delegate void dlgLeftSideData(LeftSide sender, string data);
    
    private Panel LeftSide_Panel = new Panel();
    private TextBox LeftSide_TextBox = new TextBox();
    private Button LeftSide_Button = new Button();

    public LeftSide()
    {
        LeftSide_TextBox.Location = new Point(5, 5);
        LeftSide_Button.Location = new Point(5, 25);            
        LeftSide_Panel.Controls.Add(LeftSide_TextBox);
        LeftSide_Panel.Controls.Add(LeftSide_Button);

        LeftSide_Button.Click += LeftSide_Button_Click;
    }

    private void LeftSide_Button_Click(object sender, EventArgs e)
    {
        if (true) // some kind of validation
        {
            LeftSideData?.Invoke(this, this.Data); // raise the event if there are any subscribers
        }
    }

    public string Data
    {
        get
        {
            return LeftSide_TextBox.Text;
        }
    }
    
    public Panel LeftSidePanel
    {
        get {
            return this.LeftSide_Panel;
        }                        
    }

}

Here is example code in Form1 that subscribes to the custom event and does something with the received data:

public partial class Form1 : Form
{

    LeftSide ls = new LeftSide();

    public Form1()
    {
        InitializeComponent();
        this.Controls.Add(ls.LeftSidePanel);
        ls.LeftSideData += Ls_LeftSideData;
    }

    private void Ls_LeftSideData(LeftSide sender, string data)
    {
        label1.Text = data;
        // or we could use "sender" somehow as well
        label1.Text = sender.Data; // get value from the read-only property
    }

}

You can make a property or method in your classes that receives a reference to the other LeftSide/RightSide and subscribes to the events internally.

Related