[英]C# winform backgroundworker
我目前正在为自己做一个家庭项目。 该程序使用Winforms以C#编写。
我当前遇到的问题如下:
我在名为lvwGames的主窗体中有一个列表视图,当我运行该程序而不进行调试时,它运行良好。 但是,当我开始调试时,出现错误。 这与后台工作线程有关。
请允许我发布一些代码来帮助我。
private void MainViewLoad(object sender, EventArgs e)
{
RefreshGamesListView();
}
这里没什么特别的。 我之所以调用RefreshGamesListView()是因为我不得不多次刷新。
被调用的方法如下所示。
public void RefreshGamesListView()
{
pbRefreshGamesList.Value = 0;
bgwRefreshList.RunWorkerAsync();
}
因此,在调用该方法时,将调用后台工作程序并运行dowork方法。 这个很大。
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
lvwGames.Items.Add(li);
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
当我在调试中运行代码时,代码在lvwGames.Items.Add(li);上。 它给了我以下错误:
Cross-thread operation not valid: Control 'lvwGames' accessed from a thread other than the thread it was created on.
我完全不知道为什么。 我认为这是特定于代码的。 但这也可能意味着我没有完全了解后台工作人员,尤其是何时正确使用它。
我之所以使用它,是因为我正在从数据库中加载一个大列表,我想在加载列表时保持UI的响应性,并使用进度条通知用户它有多远。
如果缺少任何代码,或者您实际上了解了发生这种情况的原因,请向我解释为什么在这种情况下会导致错误。 您不需要为我修复它。 我只想知道原因。
感谢您抽出宝贵的时间阅读这篇文章。 我希望能够尽快继续使用调试器。 :)
从后台线程访问可视控件时,需要调用Conrol.Invoke 。
if (_lvwGames.IsHandleCreated) {
Action addGameToList = () => {
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
....
_lvwGames.Items.Add(li);
};
if (_lvwGames.InvokeRequired) {
_lvwGames.Invoke(addGameToList);
} else {
addGameToList();
}
}
从线程控制
...例如,您可能调用一种方法,以响应线程执行的操作来禁用按钮或更新表单上的显示。 .NET Framework提供了可以从任何线程安全调用的方法,以调用与其他线程拥有的控件进行交互的方法。 Control.Invoke方法允许在控件上同步执行方法...
这是因为您试图从后台线程访问UI控件( lvwGames
)。 使其工作的方法要求您将信息封送回主UI线程,然后从那里更新控件:
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
// This is the new line you need:
lvwGames.Invoke(new MethodInvoker(delegate { lvwGames.Items.Add(item) }));
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
通常,您将首先检查InvokeRequired
属性,如其他答案中所述,但是如果始终从后台线程调用它,则实际上并不需要。 您的DoWork
方法将始终需要调用调用,因此您最好继续进行编写。
如果您在Studio中以调试模式运行,则后台工作程序无法正常工作。 如果您有使用Windows句柄检索消息的呼叫,则它们将失败。 例如,如果您有一个progressChanged事件处理程序,这将更改文本框中的文本,这可能会失败。
我有这种情况:具有后台工作人员的窗体。 如果我只是在不首先打开对话框的情况下启动工作程序,那么它就可以正常工作。 如果显示对话框,然后启动后台工作程序,则它将失败。 当我正常运行程序时,它不会失败。 调试环境以某种方式破坏了事件和前台窗口之间的链接。 我已经更改了代码以使用invoke,现在所有版本都可以在发行版中运行和调试时使用。
这是一个链接,说明如何使程序线程安全。 http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
我没有对Microsoft进行相同的示例。 我做了代表,分配了我需要运行的功能。 并调用了它们。
样本伪代码:
class MyClassWithDelegates
{
public delegate void ProgressDelegate( int progress );
public ProgressDelegate myProgress;
public void MyProgress(int progress)
{
myTextbox.Text = ..... ; // this is code that must be run in the GUI thread.
}
public MyClassWithDelegates()
{
myProgress = new ProgressDelegate(MyProgress);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Invoke( myProgress, e.ProgressPercentage );
}
}
为了安全起见,必须调用可能必须在应用程序的GUI线程中运行的所有代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.