2017-08-31 112 views
0

我試圖從文件加載數據列表並立即在Winforms的Datagridview上顯示該數據。爲此,我使用Backgroundworker在另一個線程中進行了閱讀。問題是,它只更新一次,我不能讓它顯示更多的數據。不僅如此,單擊時,它會嘗試訪問具有-1索引的元素,這當然不存在,導致崩潰。Winforms Datagridview無法從委託刷新

通常,從我所看到的情況來看,只需將相同的數據添加到數據源dataGridView1.DataSource = samelist;即可使用,但在此情況下不起作用。

BackgroundWorker的工作

private void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    BackgroundWorker worker = sender as BackgroundWorker; 

    //lotsofCode... 
    while (readData != null) 
    { 
     fooLists.Add(readData); 
     //someCalculations... 
     worker.ReportProgress(); 
    } 
} 

的BackgroundWorker的progressChanged

private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    this.Invoke((MethodInvoker)delegate { UpdateGridView(); }); 
} 

UpdateGridView方法

private void UpdateGridView() 
{ 
    if (fooLists.GetListById(1).calculatedList != null) 
      dataGridView1.DataSource = fooLists.GetListById(1).calculatedList; 
} 

後來我讀過堆棧,其中一個用BindingSource所建議的一些線程「中間人「,所以現在我有dataGridView1.DataSource = MyBindingSource;我n組件初始化和tab1source.DataSource = fooLists.GetListById(1).calculatedList;而不是dataGridView1.DataSource。它確實有所幫助,因爲列表現在可以按照它的方式點擊,但列表中仍然只有少量記錄。

沒有dataGridView1.Refresh(),dataGridView1.RefreshEdit()dataGridView1.Update()幫助,雖然使列表加載稍微有點fancier(可能是由於延遲他們介紹:))。我試圖做一些「保護」(信號量,所以代理不會再次被調用,而工作;嘗試捕獲,雖然沒有例外拋出;重寫前的數據清除...),但「更好的版本「和這個一樣糟糕,只會使代碼變得暗淡。

我錯過了更新Datagridview控件的方法嗎?提前致謝。

+1

fooLists是什麼類型? – WithMetta

+0

@WithMetta這是一個列表清單的自定義類。該方法返回標準列表。 – Midas

+0

看看https://social.msdn.microsoft.com/Forums/en-US/18a9762e-ac67-48a7-a372-55307fe344f3/how-do-you-refresh-data-bound-to-a-datagridview ?forum = winformsdatacontrols 您可能需要調用父控件的刷新方法。另外我不確定表達式「dataGridView1.DataSource = fooLists.GetListById(1).calculateList;」有效地執行不止一次。想想看。您將數據源設置爲您已經設置的相同對象。如果是這樣的話,它可能會在內部檢查它並且不更新視圖。 – WithMetta

回答

0

儘管你沒有寫它,但我認爲你添加到你的數據源的項目被添加到一個集合的原因並沒有實現接口IBindingList。您可能使用一個簡單的列表來保存您的讀取數據。

如果您的'DataSource implements this interface, then after adding an item to your collection an event is raised. The class that holds the DataSource , whether it is a DataGridView or a BindingSource`得到通知列表中的變化並相應地更新其內容。

您的解決方案是將您的元素存儲在類System.ComponentModel.BindingList<T>的對象中。

假設你要顯示的項目是類MyReadData

class MyForm : Form 
{ 
    public MyForm() 
    { 
     InitializeComponents(); 
     this.myReadItems = new BindingList<MyReadData>(); 
     this.MyBindingSource.DataSource = this.myReadItems; 

     // if not already done in InitializeComponents 
     this.MyDataGridView.DataSource = this.MyBindingSource; 
    } 

    private readonly BindingList<MyReadData> myReadItems; 

    // whenever needed, start the BackGroundWorker. 

    private void OnButtonReadFile_Click(object send, EventArgs e) 
    { 
     // create and start the backgroundworker 
     BackGroundWorkdr worker = ... 
     MyBackGroundWorkerParams params = ... 
     worker.RunWorkerAsync(params); 
    } 

    private void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     // I am certain the sender is my BackGroundWorker: 
     BackgroundWorker worker = (BackGroundWorker)sender; 
     MyBackGroundWorkerParams params = (MyBackGroundWorkerParams)e.Argument; 

     // do some work using the params 

     while (readData != null) 
     { 
      // some data read. 
      // dont't add the data to the list, just report the data that must been added to the list: 
      // someCalculations... 
      int percentProgress = ... 
      MyReadData dataToAddToGrid = ... 
      worker.ReportProgress(percentProgress, dataToAddToGrid); 
     } 

     private void bw_progressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      // no need to call invoke, this is already the context of your forms thread 
      Debug.Assert(!This.InvokeReguired); 
      MyReadData dataToAdddToGrid = (MyReadData)e.UserState; 
      this.myReadItems.Add(dataToAddToGrid); 
     } 
} 

的主要區別是,你不應該讓你的BackgroundWorker將數據添加到顯示的數據列表。 BackGroundWorker的任務是讀取數據並向所有感興趣的人報告數據已被讀取。

由於MyForm顯示讀取數據的任務,因此MyForm決定要顯示哪些讀取數據以及以何種格式顯示。這增強了MyFormMyBackGroundWorker的複用:MyForm可以顯示已經以不同方式獲取的信息,並且MyBackGroundWorker可以用於通知他人而不是MyForm以通知關於讀取數據。

此外,進度更改事件處理程序的顯示上下文是'MyForm`的上下文,因此不需要調用。

您也可以將IBindingList直接分配給DataGridView,因此不需要使用BindingSource。保留BindingSource的唯一原因是如果你想訪問Current項目,或者如果你想自由填充你的DataGridView除了你的BindingList的內容以外的其他項目。

最後:解決方案最重要的部分是項目被添加到IBindingList。

System.Components.BindingList<T>是一個功能有限的類。如果你想訂購的行中的DataGridView,或只顯示匹配某些謂詞,或從多個來源結合項目到一個DataGridView的項目,可以考慮使用Equin.ApplicationFramework.BindingListView

using Equin.ApplicationFramework; 

public MyForm() 
{ 
    InitializeComponents(); 
    this.myReadItems = new BindingListView<MyReadData>(this.components); 
    this.MyBindingSource.DataSource = this.myReadItems; 
    this.MyDataGridView.DataSource = this.MyBindingSource; 
} 

private readonly BindingListView<MyReadData> myReadItems; 

private void bw_progressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    MyReadData dataToAdddToGrid = (MyReadData)e.UserState; 
    this.myReadItems.Add(dataToAddToGrid); 
    // finished updating the list, DataGridView can be updated: 
    this.myReadItems.Refresh(); 

    // this Refresh function allows you to change several items in the list 
    // without unnecessary intermediate updates of your BindingSource and DataGridView 
} 

轉眼間,這是所有:免費排序或您的點擊列標題列。考慮檢查他們的示例項目,瞭解過濾的工作原理以及如何使用多個源。

// Show only Brummies 
this.myReadData.ApplyFilter(person => person.Address.City == "Birmingham"); 

// Remove the filter, show everyone again 
this.myReadData.RemoveFilter();