簡體   English   中英

C# 調用 ListView 不起作用(后台線程)

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM