从另一个线程写入 TextBox?

我不知道如何让一个 C # Windows 窗体应用程序从一个线程写入文本框。例如,在 Program.cs 中,我们有绘制表单的标准 main () :

static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}

然后我们在表格1.cs 中有:

public Form1()
{
InitializeComponent();


new Thread(SampleFunction).Start();
}


public static void SampleFunction()
{
while(true)
WindowsFormsApplication1.Form1.ActiveForm.Text += "hi. ";
}

我是不是完全搞错了?

更新

下面是 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);
}
}
}
181690 次浏览

在 MainForm 上创建一个函数来设置文本框,该函数将选中 InvokeRefied

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的静态引用,但这并不是真正推荐或必要的,你能不能只是让你的 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);
}
}
}

看看 控件. BeginInvoke方法。关键是永远不要从另一个线程更新 UI 控件。BeginInvoke 将调用控件的 UI 线程(在您的示例中是 Form)。

要获取表单,请从示例函数中删除静态修饰符并使用。BeginInvoke () ,如 MSDN 中的示例所示。

或者你可以

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

更简单的方法是使用 BackoundWorker 控件..。

您需要从拥有该控件的线程执行操作。

这就是我如何在不增加太多代码噪音的情况下做到这一点:

control.Invoke(() => textBox1.Text += "hi");

其中调用重载是 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);
}
}

我会尽可能频繁地使用 BeginInvoke而不是 Invoke,除非您确实需要等待直到您的控件被更新(在您的示例中并非如此)。BeginInvoke将委托发布到 WinForms 消息队列上,并让调用代码立即进行(在您的例子中是 SampleFunction中的 for 循环)。Invoke不仅发布委托,而且等待委托完成。

所以在你的例子中的方法 AppendTextBox中,你可以用 BeginInvoke代替 Invoke,就像这样:

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上的一个小教程。

很简单,不用担心代表们:

if(textBox1.InvokeRequired == true)
textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "Invoke was needed";});


else
textBox1.Text = "Invoke was NOT needed";

下面是我所做的,以避免 CrossThreadException和写到文本框从另一个线程。

这是我的 Button.Click函数-我想生成一个随机数量的线程,然后得到它们的 IDs通过调用 getID()方法和 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;
});
}