[英]C# Invoking ListView not working (Background Thread)
我正在使用 WinForms。 我想從后台線程填充 ListView,但是當我調用 listview 時,我的程序停止並顯示錯誤。 錯誤是“無法訪問已處置的 object。Object 名稱為:ListView。” 當我把這個方法
lvValidate.Invoke((Action)delegate
{
lvValidate.Items.Add(listitem);
});
在 try-catch 塊中,我的程序開始滯后。 我不知道問題出在哪里,但我的 Invoke 方法是:
static class Intercept
{
internal static void Invoke(this Control control, Action action)
{
control.Invoke(action);
}
}
該錯誤僅在我關閉表單並打開另一個表單(在同一程序中)時顯示。 在包含 ListView 的表單中,數據不可讀,並且似乎加載了數千次。
這是我的 DoWork,ProgressChanged,RunWorkerCompleted 事件的作用。
private void bgwLoad_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string commandText = "SELECT * FROM works";
MySqlCommand command = new MySqlCommand(commandText, connection);
MySqlDataAdapter da = new MySqlDataAdapter(command);
connection.Close();
connection.Open();
reader = command.ExecuteReader();
connection.Close();
DataTable dt = new DataTable();
da.Fill(dt);
for (int i = 0; i < dt.Rows.Count; i++)
{
DataRow dr = dt.Rows[i];
ListViewItem listitem = new ListViewItem(dr["ID"].ToString(), dr["Date"].ToString());
listitem.SubItems.Add(dr["Date"].ToString());
listitem.SubItems.Add(dr["Name"].ToString());
listitem.SubItems.Add(dr["WorkNumber"].ToString());
listitem.SubItems.Add(dr["WorkCode"].ToString());
listitem.SubItems.Add(dr["CoreThread"].ToString());
listitem.SubItems.Add(dr["Tech"].ToString());
listitem.SubItems.Add(dr["From"].ToString());
listitem.SubItems.Add(dr["To"].ToString());
listitem.SubItems.Add(dr["Validate"].ToString());
listitem.SubItems.Add(dr["Validate2"].ToString());
lvValidate.Items.Add(listitem);
}
}
private void bgwLoad_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
}
private void bgwLoad_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
picLoading.Visible = false;
}
嘗試以下操作:
Dispatcher.CurrentDispatcher.Invoke(() => {
lvValidate.Items.Add(listitem);
});
編輯:
或者試試這個:
public static void AddItem(ListItem listitem)
{
if (lvValidate.InvokeRequired)
{
AddItemDelegate d = new AddItemDelegate (AddItem);
lvValidate.Invoke(d, new object[] { listitem });
}
else
{
lvValidate.Invoke(new Action(() =>
{
lvValidate.Items.Add(listitem);
}));
}
}
delegate void AddItemDelegate(ListItem listitem);
然后調用:
AddItem(listitem);
根據您的評論,您正在嘗試使用 DataAdapter 從數據庫中檢索數據,然后將數據零碎地分發。 這不起作用有幾個原因:
數據適配器
DataAdapter 類的共同點是它們只工作,而 DBConnection 是主動打開的。 這就是為什么您會得到“無法訪問已處置的對象”的原因。 因為當您嘗試使用它時,它的連接已經被釋放。 處置不是你應該拖延的事情。 或者根本分手。 在 DoWork() 中保持使用(你希望得到的)正確的位置。
因此,您始終必須將 DataAdapter 的數據復制到非適配器集合中。 真的任何舊清單都可以。 這將暫時使 Memory 負載加倍,並且可能會產生一些供 GC 清理的東西,但實際上它們只是一種建議的方式。
僅在已完成時批量寫入
雖然理論上您可以通過進度報告分發部分讀取/處理結果,但不建議嘗試。 編寫 GUI 需要大量開銷。 我第一次也是唯一一次這樣做,我最終用寫入和繪制操作鎖定了我的 GUI 線程。 看起來我從來沒有做過多任務處理。 更新進度條的成本幾乎足夠低,不會真正引起問題。
默認模式是僅在獲得所有數據后才覆蓋相關數量的數據。
如果您遇到異常或取消,該模式是假定所有數據都是錯誤的並且沒有在 UI 上。
我喜歡將 BackgroundWorker 稱為“多任務/線程訓練輪”。 他們教你所有這些東西。 第一部分,通過分發 akward。 第二部分,如果您嘗試在無效情況下使用 Result,則實際拋出異常。
你可能收回了太多
數據庫最常見的錯誤可能是嘗試檢索大量數據,然后在客戶端中進行處理或過濾。 一個常見的錯誤,所以避免它。
用戶可以處理多少您定義為 1 頁的數據是有限制的。 我的建議是一次不要超過 100 個數據字段。 如果您需要過濾、分頁或排序,請始終在查詢中進行。 將這些東西移動到客戶端會通過網絡移動大量不必要的數據,然后處理/過濾的速度會比數據庫更慢。
示例代碼
這實際上是我的第一個 BGW 項目。 多年來我對其進行了一些更新,但其中大部分仍然有效:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.