Communicate between two windows forms in C#

后端 未结 12 1761
温柔的废话
温柔的废话 2020-11-21 04:40

I have two forms, one is the main form and the other is an options form. So say for example that the user clicks on my menu on the main form: Tools -> Options

12条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-11-21 05:22

    In the comments to the accepted answer, Neeraj Gulia writes:

    This leads to tight coupling of the forms Form1 and Form2, I guess instead one should use custom events for such kind of scenarios.

    The comment is exactly right. The accepted answer is not bad; for simple programs, and especially for people just learning programming and trying to get basic scenarios to work, it's a very useful example of how a pair of forms can interact.

    However, it's true that the coupling that example causes can and should be avoided, and that in the particular example, an event would accomplish the same thing in a general-purpose, decoupled way.

    Here's an example, using the accepted answer's code as the baseline:

    Form1.cs:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            Form2 frm = new Form2();
    
            frm.Button1Click += (sender, e) => Lbl.Text = ((Form2)sender).Message;
    
            frm.Show();
        }
    }
    

    The above code creates a new instance of Form2, and then before showing it, adds an event handler to that form's Button1Click event.

    Note that the expression (sender, e) => Lbl.Text = ((Form2)sender).Message is converted automatically by the compiler to a method that looks something similar to (but definitely not exactly like) this:

    private void frm_Message(object sender, EventArgs e)
    {
        Lbl.Text = ((Form2)sender).Message;
    }
    

    There are actually lots of ways/syntaxes to implement and subscribe the event handler. For example, using an anonymous method as the above, you don't really need to cast the sender parameter; instead you can just use the frm local variable directly: (sender, e) => Lbl.Text = frm.Message.

    Going the other way, you don't need to use an anonymous method. You could in fact just declare a regular method just like the compiler-generated one I show above, and then subscribe that method to the event: frm.Button1Click += frm_Message; (where you have of course used the name frm_Message for the method, just as in my example above).

    Regardless of how you do it, of course you will need for Form2 to actually implement that Button1Click event. That's very simple…

    Form2.cs:

    public partial class Form2 : Form
    {
        public event EventHandler Button1Click;
    
        public string Message { get { return txtMessage.Text; } }
    
        public Form2()
        {
            InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            EventHandler handler = Button1Click;
    
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
    

    In addition to the event, I've also declared a property Message that exposes the Text property (and only the Text property, and only as read-only in fact) of the txtMessage control. This allows the subscriber to the event to get the value and do whatever it needs to with it.

    Note that all that the event does is to alert the subscriber that the button has in fact been clicked. It's up to the subscriber to decide how to interpret or react to that event (e.g. by retrieving the value of the Message property and assigning it to something).

    Alternatively, you could in fact deliver the text along with the event itself, by declaring a new EventArgs sub-class and using that for the event instead:

    public class MessageEventArgs : EventArgs
    {
        public string Message { get; private set; }
    
        public MessageEventArgs(string message)
        {
            Message = message;
        }
    }
    
    public partial class Form2 : Form
    {
        public event EventHandler Button1Click;
    
        public Form2()
        {
            InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            EventHandler handler = Button1Click;
    
            if (handler != null)
            {
                handler(this, new MessageEventArgs(txtMessage.Text));
            }
        }
    }
    

    Then the subscriber can just retrieve the message value directly from the event object:

    frm.Button1Click += (sender, e) => Lbl.Text = e.Message;
    


    The important thing note in all of the above variations is that at no point does the class Form2 need to know anything about Form1. Having Form1 know about Form2 is unavoidable; after all, that's the object that will create a new Form2 instance and use it. But the relationship can be asymmetrical, with Form2 being usable by any object that needs the features it offers. By exposing the functionality as an event (and optionally with a property), it makes itself useful without limiting its usefulness to only the Form1 class.

提交回复
热议问题