[英]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.