C#中两个窗体之间的通信
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1665533/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Communicate between two windows forms in C#
提问by Bob Dylan
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
, this would cause my options form to be shown.
我有两种表格,一种是主表格,另一种是选项表格。例如,假设用户单击主窗体上的菜单:Tools -> Options
,这将导致显示我的选项窗体。
My question is how can I send data from my options form back to my main form? I know I could use properties, but I have a lot of options and this seems like an tediousodd thing to do.
我的问题是如何将选项表单中的数据发送回主表单?我知道我可以使用属性,但我有很多选择,这似乎是一件乏味而奇怪的事情。
So what is the best way?
那么最好的方法是什么?
采纳答案by this. __curious_geek
Form1 triggers Form2 to open. Form2 has overloaded constructor which takes calling form as argument and provides its reference to Form2 members. This solves the communication problem. For example I've exposed Label Property as public in Form1 which is modified in Form2.
Form1 触发 Form2 打开。Form2 具有重载构造函数,它将调用 form 作为参数并提供对 Form2 成员的引用。这解决了通信问题。例如,我在 Form1 中将 Label 属性公开为 public,在 Form2 中进行了修改。
With this approach you can do communication in different ways.
通过这种方法,您可以以不同的方式进行交流。
Download Link for Sample Project
//Your Form1
//你的 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; }
}
}
//Your Form2
//你的 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;
}
}
(source: ruchitsurati.net)
(来源:ruchitsurati.net)
(source: ruchitsurati.net)
(来源:ruchitsurati.net)
回答by Dani
Properties is one option, shared static class - another option, events - another option...
属性是一个选项,共享静态类 - 另一个选项,事件 - 另一个选项......
回答by leppie
The best in this case would be to have some OptionsService
class/interface that is accessible via IServiceProvider
.
在这种情况下,最好的方法是拥有一些OptionsService
可通过IServiceProvider
.
Just add an event when something changes, and the rest of the app can respond to it.
只需在发生变化时添加一个事件,应用程序的其余部分就可以响应它。
回答by Andy S
You might try AutoMapper. Keep your options in a separate class and then use AutoMapper to shuttle the data between the class and the form.
您可以尝试AutoMapper。将您的选项放在一个单独的类中,然后使用 AutoMapper 在类和表单之间传输数据。
回答by Zuhaib
Create a Class and put all your properties inside the class .. Create a Property in the parent class and set it from your child (options) form
创建一个类并将所有属性放在类中.. 在父类中创建一个属性并从您的子类(选项)表单中设置它
回答by John Gietzen
You can have a function in Form B like so:
您可以在 Form B 中使用一个函数,如下所示:
public SettingsResults GetNewSettings()
{
if(this.ShowDialog() == DialogResult.Ok)
{
return new SettingsResult { ... };
}
else
{
return null;
}
}
And you can call it like this:
你可以这样称呼它:
...
using(var fb = new FormB())
{
var s = fb.GetNewSettings();
...
// Notify other parts of the application that settings have changed.
}
回答by Oren Mazor
This is probably sidestepping your problem a little bit, but my settings dialog uses the Application Settings construct. http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx
这可能稍微回避了您的问题,但我的设置对话框使用了应用程序设置构造。http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx
I can't find a good example that's similar to how I do it (which is actually having an actual class+object), but this covers another way of doing it:
我找不到与我的做法类似的好例子(实际上有一个实际的类+对象),但这涵盖了另一种做法:
回答by Ron Savage
A form is a class, just like any other class. Add some public variables to your form class and set them when they click the button to close the form (technically they are just hiding it).
表单是一个类,就像任何其他类一样。将一些公共变量添加到您的表单类中,并在他们单击按钮关闭表单时设置它们(从技术上讲,他们只是隐藏了它)。
A VB.NET example, but you'll get the idea -
一个 VB.NET 示例,但您会明白-
In your OptionsForm class:
在您的 OptionsForm 类中:
Public Option1 as String = ""
etc. Set them when they hit the "Ok" button.
等。当他们点击“确定”按钮时设置它们。
So in your main form, when they hit the "options" button - you create your options form:
因此,在您的主表单中,当他们点击“选项”按钮时 - 您创建了您的选项表单:
OptionsForm.ShowDialog()
when it exits, you harvest your option settings from the public variables on the form:
当它退出时,您从表单上的公共变量中获取您的选项设置:
option1 = OptionsForm.Option1
etc.
等等。
回答by Sarkazein
MVC, MVP, MVVM -- slight overkill for someone admittedly saying they want tutorials. Those are theories that have entire courses dedicated to them.
MVC、MVP、MVVM——对于承认说他们想要教程的人来说有点矫枉过正。这些是整个课程专门针对它们的理论。
As already posted, passing an object around is probably easiest. If treating a class as an object (interchangeable in this sense) is new, then you may want to spend another 2-4 weeks figuring out properties and constructors and such.
正如已经发布的,传递一个对象可能是最简单的。如果将类视为对象(在这个意义上是可互换的)是新的,那么您可能需要再花 2-4 周来弄清楚属性和构造函数等。
I'm not a C# master by any means, but these concepts need to be pretty concrete if you want to go much further beyond passing values between two forms (also classes/objects in their own right). Not trying to be mean here at all, it just sounds like you're moving from something like VB6 (or any language with globals) to something far more structured.
我无论如何都不是 C# 高手,但是如果您想在两个表单(也包括类/对象本身)之间传递值更进一步,那么这些概念需要非常具体。在这里根本不想刻薄,这听起来像是您正在从诸如 VB6(或任何具有全局变量的语言)之类的东西转移到更加结构化的东西。
Eventually, it will click.
最终,它会点击。
回答by Peter Duniho
In the comments to the accepted answer, Neeraj Gulia writes:
在对已接受答案的评论中,Neeraj Gulia 写道:
This leads to tight coupling of the forms Form1 and Form2, I guess instead one should use custom events for such kind of scenarios.
这导致表单 Form1 和 Form2 的紧密耦合,我想应该为此类场景使用自定义事件。
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:
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.
上面的代码创建了 的一个新实例Form2
,然后在显示它之前,将一个事件处理程序添加到该表单的Button1Click
事件中。
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:
请注意,表达式(sender, e) => Lbl.Text = ((Form2)sender).Message
会被编译器自动转换为类似于(但绝对不完全像)以下内容的方法:
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
.
实际上有很多方法/语法来实现和订阅事件处理程序。例如,使用上面的匿名方法,您实际上不需要强制转换sender
参数;相反,您可以直接使用frm
局部变量:(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).
反过来说,您不需要使用匿名方法。实际上,您可以像上面展示的编译器生成的方法一样声明一个常规方法,然后将该方法订阅到事件:(frm.Button1Click += frm_Message;
当然,您在这里使用了frm_Message
该方法的名称,就像我上面的示例一样)。
Regardless of how you do it, of course you will need for Form2
to actually implement that Button1Click
event. That's very simple…
不管你怎么做,当然你需要Form2
实际实现那个Button1Click
事件。这很简单……
Form2.cs:
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 onlythe 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.
除了事件,我也声明的属性Message
暴露的Text
属性(只的Text
财产,只能作为只读其实)的的txtMessage
控制。这允许事件的订阅者获取值并对其进行任何需要的操作。
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).
请注意,该事件所做的只是提醒订阅者按钮实际上已被单击。由订阅者决定如何解释或响应该事件(例如通过检索Message
属性的值并将其分配给某物)。
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:
或者,您实际上可以通过声明一个新的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));
}
}
}
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 anyobject 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.
在上述所有变体中Form2
需要注意的重要一点是,类在任何时候都不需要了解Form1
. 有Form1
知道Form2
是不可避免的; 毕竟,这是将创建新Form2
实例并使用它的对象。但是这种关系可能是不对称的,任何需要它提供的功能的对象Form2
都可以使用。通过将功能公开为事件(以及可选的属性),它使自己变得有用,而不会将其用途仅限于类。Form1