在C#中的两个窗体之间进行通信

我有两种forms,一种是主要forms,另一种是选项forms。 所以说,例如,用户点击我的主窗体上的菜单: Tools -> Options ,这将导致我的选项窗体显示。

我的问题是如何从我的选项表单发回数据回到我的主窗体? 我知道我可以使用属性,但我有很多select,这似乎是一个单调乏味的事情。

那么最好的方法是什么?

Form1触发Form2打开。 Form2重载了构造函数,它将调用窗体作为参数,并提供对Form2成员的引用。 这解决了通信问题。 例如,我已经在Form1中公开了Form1中的Label属性,并在Form2中进行了修改。

用这种方法你可以用不同的方式进行交stream。

示例项目的下载链接

// 你的Form1

 public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Form2 frm = new Form2(this); frm.Show(); } public string LabelText { get { return Lbl.Text; } set { Lbl.Text = value; } } } 

// 你的Form2

 public partial class Form2 : Form { public Form2() { InitializeComponent(); } private Form1 mainForm = null; public Form2(Form callingForm) { mainForm = callingForm as Form1; InitializeComponent(); } private void Form2_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { this.mainForm.LabelText = txtMessage.Text; } } 

替代文字files/frm1.png

替代文字files/frm2.png

Neeraj Gulia在对接受的答复的评论中写道:

这导致formsForm1和Form2的紧密耦合,我猜是应该使用自定义事件来处理这种types的场景。

评论是完全正确的。 被接受的答案并不坏, 对于简单的程序,特别是对于只是学习编程和试图获得基本scheme的人来说,这是一对非常有用的例子,表明一对表单可以相互作用。

然而,这个例子所引起的耦合是可以而且应该被避免的,在这个例子中,一个事件会以通用的,分离的方式完成同样的事情。

下面是一个例子,使用接受的答案的代码作为基准:

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

上面的代码创build一个Form2的新实例,然后在显示它之前,向该窗体的Button1Click事件添加一个事件处理程序。

请注意,expression式(sender, e) => Lbl.Text = ((Form2)sender).Message由编译器自动转换为类似于(但绝对不完全一样)的方法:

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

实际上有很多方法/语法来实现和订阅事件处理程序。 例如,使用上面的匿名方法,您并不需要强制使用sender参数。 而是直接使用frm局部variables: (sender, e) => Lbl.Text = frm.Message

换一种方式,你不需要使用匿名方法。 事实上,你可以像上面展示的编译器生成的一样声明一个常规方法,然后将该方法订阅到事件: frm.Button1Click += frm_Message; (当然你也可以使用frm_Message作为这个方法,就像我上面的例子一样)。

不pipe你如何做,当然你需要Form2来实际的实现Button1Click事件。 这很简单…

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

除了事件之外,我还声明了一个属性Message ,该属性暴露了txtMessage控件的Text属性(并且只是 Text属性,实际上只是作为只读)。 这使用户可以通过事件来获得价值,并随时随地做任何事情。

请注意,事件所做的一切就是提醒用户该button实际上已被点击。 由用户决定如何解释或响应该事件(例如,通过检索Message属性的值并将其分配给某个事件)。

另外,事实上,你可以通过声明一个新的EventArgs子类并将其用于该事件,而将其与事件本身一起传递:

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

然后用户可以直接从事件对象中检索消息值:

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

上述所有变化中重要的一点是, Form2类不需要知道Form1任何信息。 Form1知道Form2是不可避免的; 毕竟,这是将创build一个新的Form2实例并使用它的对象。 但是这种关系可能是不对称的, Form2可以被任何需要它提供的特性的对象所使用。 通过将function作为事件(以及可选的属性)进行公开,它使其本身非常有用,而不会将其用途仅限于Form1类。

在这种情况下,最好的办法是使用一些可通过IServiceProvider访问的OptionsService类/接口。

只需在事件发生变化时添加一个事件,其余的应用程序就可以响应。

属性是一个选项,共享静态类 – 另一个选项,事件 – 另一个选项…

你可以尝试AutoMapper 。 将您的选项保存在一个单独的类中,然后使用AutoMapper在类和表单之间传递数据。

创build一个类,并将所有的属性放在类中。在父类中创build一个属性,并从您的子(选项)窗体中设置它

你可以在表格B中有一个函数,如下所示:

 public SettingsResults GetNewSettings() { if(this.ShowDialog() == DialogResult.Ok) { return new SettingsResult { ... }; } else { return null; } } 

你可以这样称呼它:

 ... using(var fb = new FormB()) { var s = fb.GetNewSettings(); ... // Notify other parts of the application that settings have changed. } 

MVC,MVP,MVVM – 对于有人坦承说他们需要教程的微小的矫枉过正。 这些理论都有完整的课程。

正如已经发布的,传递一个对象可能是最简单的。 如果把一个类作为一个对象(在这个意义上是可以互换的)是新的,那么你可能需要再花2-4个星期来弄清楚属性和构造函数等等。

我不是一个C#高手,但是这些概念需要非常具体,如果你想远远超过两种forms(也是类/对象本身)传递值。 不要在这里意味着什么,这听起来像是你从VB6(或任何带有全局variables的语言)转向更加结构化的东西。

最终,它会点击。

这可能会绕过你的问题,但我的设置对话框使用应用程序设置结构。 http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

我找不到一个很好的例子,它类似于我(它实际上有一个实际的类+对象),但是这覆盖了另一种方式:

读取C#中的默认应用程序设置

一个表格就像其他类一样是一个类。 添加一些公共variables到你的表单类,并设置它们,当他们点击buttonclosures表单(技术上,他们只是隐藏它)。

一个VB.NET的例子,但你会明白 –

在你的OptionsForm类中:

 Public Option1 as String = "" 

当他们点击“确定”button时设置它们。

所以在你的主表单中,当他们点击“选项”button时,你创build你的选项表单:

 OptionsForm.ShowDialog() 

当它退出时,您将从表单上的公共variables中获取选项设置:

 option1 = OptionsForm.Option1 

等等