从另一个线程写入文本框? [复制]

Posted

技术标签:

【中文标题】从另一个线程写入文本框? [复制]【英文标题】:Writing to a TextBox from another thread? [duplicate] 【发布时间】:2010-10-05 21:01:36 【问题描述】:

我不知道如何让 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. ";

我这样做完全错了吗?

更新

这是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);
        
    

【问题讨论】:

【参考方案1】:

在你的 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 进行静态引用,但这并不是真正推荐或不必要的,如果是这样,您可以让您的 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);
        
    

【讨论】:

我尝试了您的代码,但它对我来说不太适用。我很感激回答伙伴。 更具体地说,它会编译,但不会连续写入 TextBox。 在您的代码中,您取出 while(true) 将其放回您的示例函数中,您应该会摇摆不定。不过会写很多。如果您尝试测试响应能力,您可能需要放置 Thread.Sleep(1000) 1 秒 本德威,你这个摇滚伙伴!我可能花了 2 个小时把头撞在显示器上。【参考方案2】:

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

因此,在您的示例中的方法 AppendTextBox 中,您可以像这样将 Invoke 替换为 BeginInvoke

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 控件引用。 Here是SynchronizationContext上的一个小教程。

【讨论】:

谢谢!我不知道BeginInvokeInvoke 之间的区别。仅此一项就解决了我的问题(并且可能在未来帮助了我很多,因为BeginInvoke 不会导致等待) 我一直在尝试接受的答案,但没有结果,我的应用程序总是挂起。多亏了这个很好的解释,我终于可以再次入睡了【参考方案3】:

或者你可以这样做

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

【讨论】:

【参考方案4】:

很简单,不用担心委托:

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

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

【讨论】:

【参考方案5】:

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

这就是我在不添加太多代码噪音的情况下这样做的方式:

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

Invoke 重载是Lokad Shared Libraries 的简单扩展:

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

【讨论】:

【参考方案6】:

看看Control.BeginInvoke 方法。关键是永远不要从另一个线程更新 UI 控件。 BeginInvoke 会将调用分派给控件的 UI 线程(在您的情况下为 Form)。

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

【讨论】:

【参考方案7】:

更简单的是只使用 BackgroundWorker 控件...

【讨论】:

【参考方案8】:

这是我为避免CrossThreadException 并从另一个线程写入文本框而采取的措施。

这是我的Button.Click 函数- 我想生成随机数量的线程,然后在该工作线程中通过调用getID() 方法和TextBox 值来获取它们的IDs

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

【讨论】:

以上是关于从另一个线程写入文本框? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何从另一个线程更新 GUI 上的文本框 [重复]

如何从另一个表单刷新文本框? C#

如何从另一个类访问 Winform 文本框控件?

jquery 一个按钮点击,复制文本内容

将pyspark数据框写入文本文件

VB复制文本框文本问题