Che*_*ung 4 c# multithreading winforms
我有一个用.NET Winforms编码的项目.我需要实现数据挖掘操作,将文本打印到TextBox并更新进度.
我尝试使用BackgroundWorker来做,但它抛出一个InvalidOperationException(跨线程操作无效:控件'xxxxx'从一个线程访问,而不是它创建的线程)
为了缩小问题的潜在原因,我开始了一个新项目,包括以下内容:按钮 - 启动BackgroundWorker标签 - 打印文本.和ProgressBar.
但是,结果是一样的.我搜索了SOF,并被告知要使用代表,但我不熟悉它.
这是抛出错误的代码示例:
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace TestProject
{
public partial class Form1 : Form
{
private readonly BackgroundWorker _bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
_bw.DoWork += RosterWork;
_bw.ProgressChanged += BwProgressChanged;
_bw.RunWorkerCompleted += BwRunWorkerCompleted;
_bw.WorkerReportsProgress = true;
_bw.WorkerSupportsCancellation = false;
}
private void RosterWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
for (int i = 0; i < 1000; i++)
{
label1.Text = i.ToString();
_bw.ReportProgress(Convert.ToInt32((i * (100 / 1000))));
}
}
private void BwProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void btnStart_Click(object sender, EventArgs e)
{
progressBar1.Show();
_bw.RunWorkerAsync();
}
private void BwRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Hide();
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新: 我遵循Jon Skeet的回答,它确实适用于我的测试项目,但回到我的真实项目,
我的表格布局:
表单 - TabControl - Tab1 -Tab1Panel -TextBox1
到达此行时:
TextBox txtbox1 = new TextBox();
Tab1Panel.Controls.Add(txtbox1);
Run Code Online (Sandbox Code Playgroud)
当我以编程方式将Textbox添加到Panel Control时,仍会出现错误.
最后,我替换为:
if (Tab1Panel.InvokeRequired)
Tab1Panel.Invoke((MethodInvoker)delegate { Tab1Panel.Controls.Add(txtbox1); });
else
Tab1Panel.Controls.Add(txtbox1);
Run Code Online (Sandbox Code Playgroud)
一切都很有效.如何确定控件是InvokeRequired,是否指定了控件?
Jon*_*eet 11
这就是问题:
label1.Text = i.ToString();
Run Code Online (Sandbox Code Playgroud)
您正在尝试更改BackgroundWorker未在UI线程上运行的标签文本.关键BackgroundWorker是要在那里完成所有非UI工作,使用ReportProgress定期"返回"UI线程并使用您正在进行的进度更新UI.
所以,要么你需要改变label1.Text的BwProgressChanged为好,或者你需要使用Control.Invoke/ BeginInvoke就像你从任何其他后台线程将:
// Don't capture a loop variable in a lambda expression...
int copy = i;
Action updateLabel = () => label1.Text = copy.ToString();
label1.BeginInvoke(updateLabel);
Run Code Online (Sandbox Code Playgroud)
有关复制部分的更多信息,请参阅Eric Lippert的博客文章"关闭循环变量被认为有害".在这种特殊情况下,这只是一个问题因为我正在使用BeginInvoke.这可以改为:
Action updateLabel = () => label1.Text = i.ToString();
label1.Invoke(updateLabel);
Run Code Online (Sandbox Code Playgroud)
...但是现在后台工作人员总是会等待UI继续前进,这在现实生活中通常不是你想要的.我一般喜欢BeginInvoke了Invoke.
小智 5
使用此代码 iw 将工作
BeginInvoke((MethodInvoker)delegate
{
TextBox1.Text += "your text here";
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10822 次 |
| 最近记录: |