我无法弄清楚如何使C#Windows窗体应用程序从一个线程写入文本框.例如,在Program.cs中,我们有标准的main()来绘制表单:
static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
然后我们在Form1.cs中:
public Form1() { InitializeComponent(); new Thread(SampleFunction).Start(); } public static void SampleFunction() { while(true) WindowsFormsApplication1.Form1.ActiveForm.Text += "hi. "; }
我完全错了吗?
UPDATE
以下是bendewey提供的工作代码示例:
public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread(SampleFunction).Start(); } public void AppendTextBox(string value) { if (InvokeRequired) { this.Invoke(new Action(AppendTextBox), new object[] {value}); return; } textBox1.Text += value; } void SampleFunction() { // Gets executed on a seperate thread and // doesn't block the UI while sleeping for(int i = 0; i<5; i++) { AppendTextBox("hi. "); Thread.Sleep(1000); } } }
bendewey.. 63
在您的MainForm上创建一个函数来设置文本框检查InvokeRequired
public void AppendTextBox(string value) { if (InvokeRequired) { this.Invoke(new Action(AppendTextBox), new object[] {value}); return; } ActiveForm.Text += value; }
虽然在静态方法中你不能只是打电话.
WindowsFormsApplication1.Form1.AppendTextBox("hi. ");
你必须在某个地方对Form1进行静态引用,但这不是真正推荐或必要的,你是否只是让你的SampleFunction不是静态的,那么你可以调用
AppendTextBox("hi. ");
如果需要,它将附加在不同的线程上并使用Invoke调用编组到UI.
完整样本
public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread(SampleFunction).Start(); } public void AppendTextBox(string value) { if (InvokeRequired) { this.Invoke(new Action(AppendTextBox), new object[] {value}); return; } textBox1.Text += value; } void SampleFunction() { // Gets executed on a seperate thread and // doesn't block the UI while sleeping for(int i = 0; i<5; i++) { AppendTextBox("hi. "); Thread.Sleep(1000); } } }
小智.. 14
或者你可以这样做
public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread( SampleFunction ).Start(); } void SampleFunction() { // Gets executed on a seperate thread and // doesn't block the UI while sleeping for ( int i = 0; i < 5; i++ ) { this.Invoke( ( MethodInvoker )delegate() { textBox1.Text += "hi"; } ); Thread.Sleep( 1000 ); } } }
Mike.. 14
我会用BeginInvoke
,而不是Invoke
尽可能多的,除非你真的需要等待,直到你的控制已经更新(在您的例子并非如此).BeginInvoke
在WinForms消息队列中发布委托,并让调用代码立即进行(在您的情况下为for循环SampleFunction
).Invoke
不仅发布代表,还等待它完成.
所以在AppendTextBox
你的例子的方法中,你将替换Invoke
为BeginInvoke
:
public void AppendTextBox(string value) { if (InvokeRequired) { this.BeginInvoke(new Action(AppendTextBox), new object[] {value}); return; } textBox1.Text += value; }
好吧,如果你想得到更多的幻想,还有一个SynchronizationContext
类,它可以让你基本上做同样的Control.Invoke/Control.BeginInvoke
,但具有不需要WinForms控件引用的优势.这是一个小教程SynchronizationContext
.
在您的MainForm上创建一个函数来设置文本框检查InvokeRequired
public void AppendTextBox(string value) { if (InvokeRequired) { this.Invoke(new Action(AppendTextBox), new object[] {value}); return; } ActiveForm.Text += value; }
虽然在静态方法中你不能只是打电话.
WindowsFormsApplication1.Form1.AppendTextBox("hi. ");
你必须在某个地方对Form1进行静态引用,但这不是真正推荐或必要的,你是否只是让你的SampleFunction不是静态的,那么你可以调用
AppendTextBox("hi. ");
如果需要,它将附加在不同的线程上并使用Invoke调用编组到UI.
完整样本
public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread(SampleFunction).Start(); } public void AppendTextBox(string value) { if (InvokeRequired) { this.Invoke(new Action(AppendTextBox), new object[] {value}); return; } textBox1.Text += value; } void SampleFunction() { // Gets executed on a seperate thread and // doesn't block the UI while sleeping for(int i = 0; i<5; i++) { AppendTextBox("hi. "); Thread.Sleep(1000); } } }
或者你可以这样做
public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread( SampleFunction ).Start(); } void SampleFunction() { // Gets executed on a seperate thread and // doesn't block the UI while sleeping for ( int i = 0; i < 5; i++ ) { this.Invoke( ( MethodInvoker )delegate() { textBox1.Text += "hi"; } ); Thread.Sleep( 1000 ); } } }
我会用BeginInvoke
,而不是Invoke
尽可能多的,除非你真的需要等待,直到你的控制已经更新(在您的例子并非如此).BeginInvoke
在WinForms消息队列中发布委托,并让调用代码立即进行(在您的情况下为for循环SampleFunction
).Invoke
不仅发布代表,还等待它完成.
所以在AppendTextBox
你的例子的方法中,你将替换Invoke
为BeginInvoke
:
public void AppendTextBox(string value) { if (InvokeRequired) { this.BeginInvoke(new Action(AppendTextBox), new object[] {value}); return; } textBox1.Text += value; }
好吧,如果你想得到更多的幻想,还有一个SynchronizationContext
类,它可以让你基本上做同样的Control.Invoke/Control.BeginInvoke
,但具有不需要WinForms控件引用的优势.这是一个小教程SynchronizationContext
.
您需要从拥有该控件的线程执行操作.
这就是我在不增加太多代码噪音的情况下这样做的方式:
control.Invoke(() => textBox1.Text += "hi");
Invoke重载是Lokad共享库的一个简单扩展:
////// Invokes the specified ///on the thread that owns /// the . type of the control to work with /// The control to execute action against. /// The action to on the thread of the control. public static void Invoke(this TControl control, Action action) where TControl : Control { if (!control.InvokeRequired) { action(); } else { control.Invoke(action); } }