winforms 正在从另一个线程写入TextBox?[duplicate]

t1rydlwq  于 2022-12-14  发布在  其他
关注(0)|答案(8)|浏览(119)

此问题在此处已有答案

How do I update the GUI from another thread?(47个答案)
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on(22个答案)
去年关闭了。
我不知道如何让C# Windows Form应用程序从执行绪写入文字方块。例如,在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);
        }
    }
}
2cmtqfgy

2cmtqfgy1#

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

7vhp5slm2#

我会尽可能经常地使用BeginInvoke而不是Invoke,除非您确实需要等到控件更新后再使用BeginInvoke将委托发布到WinForms消息队列上,并让调用代码立即继续执行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控件引用。HereSynchronizationContext的一个小教程。

ukqbszuj

ukqbszuj3#

或者你可以这样做

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

pvcm50d14#

非常简单,无需担心委派和操作:

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

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

InvokeRequired检查代码是在UI线程上运行还是在其他线程上运行。只允许UI线程执行UI操作(如更改控件的内容)。如果代码不在UI线程上运行,则Invoke会将操作临时传递给UI线程。
这可能会降低性能并减少多线程的好处。在这种情况下,可以使用带有事件的队列。后台线程将更改放在队列中并继续。UI更改在事件中异步执行。

3zwtqj6y

3zwtqj6y5#

您需要从拥有控件的执行绪执行动作。
这就是我如何在不增加过多代码噪音的情况下实现的:

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

xxhby3vn6#

看一下Control.BeginInvoke方法。要点是永远不要从另一个线程更新UI控件。BeginInvoke会将调用调度到控件的UI线程(在您的示例中是Form)。
要获取表单,请从示例函数中删除static修饰符,并使用this.BeginInvoke(),如MSDN中的示例所示。

myzjeezk

myzjeezk7#

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

vql8enpb

vql8enpb8#

下面是我为避免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(工作线程)代码:

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

相关问题