簡體   English   中英

.NET 3.5 WinForms - DataGridView 在 refresh() 時崩潰。 這是一個錯誤嗎?

[英].NET 3.5 WinForms - DataGridView crashes on refresh(). Is it a bug?

這是一個多線程場景。

  1. 主線程處理應用程序和 UI 事件,並啟動一個新線程來執行一些后台操作。
  2. “后台”線程將文件中的數據加載到強類型數據集的數據表中。 DataGridView綁定到該DataTable
  3. 數據准備好后,“后台”線程調用窗體上DataGridViewrefresh() function。

如果有更多的行,那么適合一個屏幕的內容和垂直滾動條就會出現:網格崩潰。 始終顯示新的數據線。 只有當有足夠的行來顯示滾動條時才會發生錯誤(見下圖)。

DataGridView 崩潰的示例圖像

我使用 .NET 3.5。 在 Windows XP 中,它會使整個應用程序崩潰。 在 Win 7(64 位)上,只有網格變得無響應,但是一旦我調整 window 的大小,滾動條就會出現,一切都很好。

代碼的相關部分附在下面。

表單的.cs文件中的網格刷新操作:

    public void ThreadSafeRebindGrids()
    {
        SimpleCallBack callBackHandler = new SimpleCallBack(RebindGrids);
        this.BeginInvoke(callBackHandler);
    }
    public void RebindGrids()
    {
        gridCurrentResults.Refresh(); // The problematic DataGridView refresh()
        gridAllResults.Refresh();
    }
    public delegate void SimpleCallBack();

“后台”線程中的更新部分:

    void Maestro32_SampleFinished(object sender, MeasurementEvents.SampleFinishedEventArgs e)
    {
        //--- Read new results
        ParentForm.ThreadSafeSetStatusInfo("Processing results for sample no. " + e.SampleNo.ToString() + "...");
        CurrentMeasurement.ReadSpeResults();   // Updating the DataTable in the strongly typed DataSet (see below)
        ParentForm.ThreadSafeRebindGrids();    // Refresh the DataGridView
        ParentForm.ThreadSafeRefreshNumbers();
    }

與“后台”線程相關的對象直接引用了DataSet ( UiDataSource )。 DataTable ( CurrentSamples ) 以下列方式更新:

    /// <summary>
    /// Adds a new sample to the CurrentSamples table of the UiDataSet.
    /// </summary>
    /// <param name="sample">The new sample to be added to the table.</param>
    /// <param name="serial">The serial number of the sample being added</param>
    private void AddSampleToCurrentResults(SampleData sample, int serial)
    {
        UiDataSource.CurrentSamples.AddCurrentSamplesRow(serial,
                                                       sample.MeasurementDate,
                                                       (uint)Math.Round(sample.SampleCountSum),
                                                       true, //--- Set the checkbox checked
                                                       sample.LiveTime,
                                                       sample.RealTime);
    }

DataGridView選項:

        // 
        // gridCurrentResults (generated)
        // 
        this.gridCurrentResults.AllowUserToAddRows = false;
        this.gridCurrentResults.AllowUserToDeleteRows = false;
        this.gridCurrentResults.AllowUserToOrderColumns = true;
        this.gridCurrentResults.AllowUserToResizeRows = false;
        this.gridCurrentResults.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
        | System.Windows.Forms.AnchorStyles.Left) 
        | System.Windows.Forms.AnchorStyles.Right)));
        this.gridCurrentResults.AutoGenerateColumns = false;
        this.gridCurrentResults.CausesValidation = false;
        this.gridCurrentResults.ColumnHeadersHeight = 25;
        this.gridCurrentResults.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
        this.selectedCol,
        this.SampleNoCol,
        this.MeasuredValueCol,
        this.liveTimeCol,
        this.realTimeDataGridViewTextBoxColumn,
        this.AtTimeCol});
        this.gridCurrentResults.DataMember = "CurrentSamples";
        this.gridCurrentResults.DataSource = this.uiDataSource;
        this.gridCurrentResults.Location = new System.Drawing.Point(11, 24);
        this.gridCurrentResults.Margin = new System.Windows.Forms.Padding(8);
        this.gridCurrentResults.Name = "gridCurrentResults";
        this.gridCurrentResults.RowHeadersVisible = false;
        this.gridCurrentResults.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
        this.gridCurrentResults.ShowEditingIcon = false;
        this.gridCurrentResults.Size = new System.Drawing.Size(534, 264);
        this.gridCurrentResults.TabIndex = 0;
        this.gridCurrentResults.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridCurrentResults_CellContentClick);

如果我在某個地方犯了錯誤,請指出給我。

@ChrisF:

我嘗試刪除refresh()語句,因為我所做的與你建議的幾乎相同。 唯一的區別是數據綁定,它看起來像:

this.dataGridView.DataSource = this.dataSet;
this.dataGridView.DataMember = "dataTable";

我以類似的方式更新dataTable ,但來自另一個線程。

但是新的數據線直到我調整 window 的大小時才會出現。

這引發了我如何從另一個線程正確更新dataTable的問題?

我猜這個問題與 WinForms 在 STA model 中的線程工作方式有關。 基本上,您正在訪問的 DataTable 位於某個地方,這可能在我們上面看到的表格中。 那么,當您從另一個線程更新 DataTable 時,哪個線程獲取綁定所需的事件? 很可能是您從中更新它的線程,並且表單的線程不知道正在進行的更改。 因此,您只需在表單本身上調用對 DataTable 的任何調用,它就可以正確接收事件:

this.Invoke(() => {
   // any calls involving DataTable
});

這似乎是倒退的,但請記住,在“企業”情況下,您可能會通過多個適配器訪問該數據集。 因此,您的更新線程本身將有一個適配器,您的 GUI 也將有自己的。 另一種解決方案是使用BindingList ,我相信它對這種情況具有線程兼容性,但不要引用我的話。

對於額外的功勞,這也可以在崩潰之前解釋您的問題。 通過從后台線程訪問DataGridView ,您可以進行跨線程操作。

我不會打電話:

    gridCurrentResults.Refresh(); // The problematic DataGridView refresh()
    gridAllResults.Refresh();

隨着數據集變得越來越大,這些將花費越來越長的時間。

我編寫了一個使用DataGridView來顯示 mp3 文件信息的應用程序。 我將DataGridViewDataSource設置為DataTable

this.dataGridView.DataSource = this.dataTable;

然后只需將新信息添加到DataTable

this.dataTable.Rows.Add(row);

這會自動更新DataGridView

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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