简体   繁体   中英

Best way to communicate between forms?

I almost never used (advanced, or at all) graphical interfaces, or one simple form with simple controls... but this time I've got something a little more complex, and I don't have much experience with GUI. I have one main form (and possibly more in the future) from which other sub-forms open (and they might have sub-forms of themselves) and I wonder what is, in your opinion, the best way to communicate between them?

I thought of passing the main form as a parameter to the constructors of the sub-forms, but it doesn't seem like a good way, especially if I'm going to need to communicate between other, distinct, sub-forms, not to mention I have to double check the input, or make a few methods, but it seems more like functional programming than object oriented programming...

Perhaps I can:

  • Create a static class (or Properties.Settings) for global settings. Cons: every change of data is needed to be copied to the class, I'm looking for something a bit more comfortable and elegant.
  • Use the ugly way of accessing the controls from Application.OpenForms - fixes the problem of passing the main form as parameter. Cons: not very stable.
  • Do something else I haven't thought of. Suggestions? Cons: don't know what it is yet.

Your constructor idea is probably the most sound method of communication back to the main form. Your sub form would do something like the following:

public class SubForm : Form
{
    public SubForm(MainForm parentForm)
    {
        _parentForm = parentForm;
    }

    private MainForm _parentForm;

    private void btn_UpdateClientName_Click(object sender, EventArgs e)
    {
        _parentForm.UpdateClientName(txt_ClientName.Text);
    }
}

And then you expose public methods on your MainForm :

public class MainForm : Form
{
    public void UpdateClientName(string clientName)
    {
        txt_MainClientName.Text = clientName;
    }
}

Alternatively, you can go the other way around and subscribe to events from your SubForms:

public class MainForm : Form
{
    private SubForm1 _subForm1;
    private SubForm2 _subForm2;

    public MainForm()
    {
        _subForm1 = new SubForm1();
        _subForm2 = new SubForm2();

        _subForm1.ClientUpdated += new EventHandler(_subForm1_ClientUpdated);
        _subForm2.ClientUpdated += new EventHandler(_subForm2_ProductUpdated);
    }

    private void _subForm1_ClientUpdated(object sender, EventArgs e)  
    {
        txt_ClientName.Text = _subForm1.ClientName; // Expose a public property
    }

    private void _subForm2_ProductUpdated(object sender, EventArgs e)  
    {
        txt_ProductName.Text = _subForm2.ProductName; // Expose a public property
    }
}

A good way is to declare delegates in the form that want to start the communication. You need a delegate and a callback function:

public delegate void SetValueDelegate(string value);
public SetValueDelegate SetValueCallback;

Another form can then attach to this delegate. At that moment both forms have to know each other, but not after that moment:

firstForm.SetValueCallback += new SetValueDelegate(secondForm.SetValueFunction);

The second form has to declare a function that matches the delegate definition:

public void SetValueFunction(string value)
{
    // do something
}

Now the first form can use the delegate to use the function of the second form (and all other forms or classes that were attached to the delegate:

SetValueCallback(txtParam.Text);

Edit: made an complete example

using System;

namespace DelegateTest
{
    public delegate void SetValueDelegate(string value);

    public class Class1
    {
        public SetValueDelegate SetValueCallBack;

        public void Test()
        {
            if(SetValueCallBack != null)
            {
                SetValueCallBack("Hello World!");
            }
        }
    }

    public class Class2
    {
        public void SetValueFunction(string value)
        {
            Console.WriteLine(value);
        }
    }

    public class Launcher
    {
        public static void Main(string[] args)
        {
            Class1 c1 = new Class1();
            Class2 c2 = new Class2();
            c1.SetValueCallBack += new SetValueDelegate(c2.SetValueFunction);
            c1.Test();
        }
    }
}

You can use the built in Tag property of the form which is an "object" class.

public Form1() { (ComplicatedDataStructure)Tag = new ComplicatedDataStracture(); } . . form1 = new Form1(); . . form2 = new Form2(); . . form2.Tag = form1.Tag;

so form2.Tag is equals to "ComplicatedDataStracture" object;

The most flexible, scalable (and IMHO the most professional) way to do it is to use CAB (Composite Application Block) . In simple terms CAB is a set of 2-3 assemblies that implement a lot of plumbing required to make complex UI applications the right way and it exposes this plumbing to the user of the library in a nice way. Among others it has a very nice event and command (as in command pattern) system.

The downside: requires some time to learn and not very trivial to grasp.

Here is a comprehensive (but easy to understand) tutorial that will help you make the learning easier.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM