从另一个线程写入文本框?
我无法弄清楚如何使一个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<string>(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); } } }
在你的MainForm中设置一个函数来设置文本框的InvokeRequired
public void AppendTextBox(string value) { if (InvokeRequired) { this.Invoke(new Action<string>(AppendTextBox), new object[] {value}); return; } ActiveForm.Text += value; }
虽然在你的静态方法,你不能只是打电话。
WindowsFormsApplication1.Form1.AppendTextBox("hi. ");
你必须有一个对Form1的静态引用,但这并不是真正的build议和必要的,你可以让你的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<string>(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消息队列中,并让调用代码立即进行(在你的情况下, SampleFunction
的for循环)。 Invoke
不仅仅是发布委托,还要等到它完成。
所以在你的例子中的方法AppendTextBox
你会像这样用BeginInvoke
replaceInvoke
:
public void AppendTextBox(string value) { if (InvokeRequired) { this.BeginInvoke(new Action<string>(AppendTextBox), new object[] {value}); return; } textBox1.Text += value; }
那么,如果你想变得更花哨,那么还有SynchronizationContext
类,它可以让你基本上像Control.Invoke/Control.BeginInvoke
,但是不需要知道WinForms控件引用的优点。 这是一个关于SynchronizationContext
的小教程。
您需要从拥有该控件的线程执行该操作。
这就是我所做的,而不会增加太多的代码噪音:
control.Invoke(() => textBox1.Text += "hi");
Invoke过载是Lokad共享库的简单扩展:
/// <summary> /// Invokes the specified <paramref name="action"/> on the thread that owns /// the <paramref name="control"/>.</summary> /// <typeparam name="TControl">type of the control to work with</typeparam> /// <param name="control">The control to execute action against.</param> /// <param name="action">The action to on the thread of the control.</param> public static void Invoke<TControl>(this TControl control, Action action) where TControl : Control { if (!control.InvokeRequired) { action(); } else { control.Invoke(action); } }
最简单的,不关心代表
if(textBox1.InvokeRequired == true) textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "Invoke was needed";}); else textBox1.Text = "Invoke was NOT needed";
更简单的是只使用BackgroundWorker控件…
这是我做了什么来避免CrossThreadException并从另一个线程写入文本框。
这里是我的button点击function – 我想要生成一个随机数的线程,然后通过调用getID()获得他们的ID。 方法和该工作线程中的textBox值。
private void btnAppend_Click(object sender, EventArgs e) { Random n = new Random(); for (int i = 0; i < n.Next(1,5); i++) { label2.Text = "UI Id" + ((Thread.CurrentThread.ManagedThreadId).ToString()); Thread t = new Thread(getId); t.Start(); } }
这里是getId(workerThread)代码
public void getId() { int id = Thread.CurrentThread.ManagedThreadId; //Note that, I have collected threadId just before calling //*this.Invoke* //method else it would be same as of UI thread inside the below code //block this.Invoke((MethodInvoker)delegate () { inpTxt.Text += "My id is" +"--"+id+Environment.NewLine; });
看看Control.BeginInvoke方法。 重点是永远不要从另一个线程更新UI控件。 BeginInvoke将调用调用到控件的UI线程(在你的情况下,表单)。
要获取表单,请从示例函数中删除静态修饰符,并使用this.BeginInvoke(),如MSDN示例中所示。