在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
等等