.NET 3.5 WinForms - DataGridView падает при обновлении (). Это ошибка? - PullRequest
2 голосов
/ 23 мая 2011

Это многопоточный сценарий.

  1. Основной поток обрабатывает события приложения и пользовательского интерфейса и запускает новый поток для выполнения некоторых фоновых операций.
  2. Фоновый поток загружает данные из файлов в таблицу данных строго типизированного набора данных.DataGridView привязывается к этому DataTable.
  3. Как только данные готовы, «фоновый» поток вызывает refresh() функцию DataGridView в форме.

Если строк больше, то что умещается на одном экране и должна появиться вертикальная полоса прокрутки: происходит сбой сетки. Всегда отображаются новые линии данных.Ошибка возникает только при наличии достаточного количества строк для отображения полосы прокрутки (см. Изображение ниже).

Sample image of DataGridView crash

Я использую .NET 3.5.В Windows XP вылетает целое приложение.На Win 7 (64 бит) только сетка перестает отвечать, но как только я изменяю размер окна, появляется полоса прокрутки, и все в порядке.

Соответствующие части кода прикреплены ниже.

Операция обновления сетки в файле .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 аналогичным образом, но из другого потока.

Но новые строки данных непоявляются, пока я, скажем, не изменю размер окна.

Что поднимает вопрос, как правильно обновить dataTable из другого потока?

Ответы [ 2 ]

4 голосов
/ 24 мая 2011

Я предполагаю, что проблема связана с тем, как WinForms работает внутри модели STA для многопоточности. По сути, DataTable, к которому вы обращаетесь, находится где-то , и это, вероятно, внутри формы, которую мы видим выше. Итак, когда вы обновляете DataTable из другого потока, какой поток получает события, необходимые для привязки? Вероятно, поток, из которого вы его обновляете, и поток формы не знает о внесенных изменениях. Итак, вам просто нужно вызывать любые вызовы DataTable для самой формы, чтобы она правильно принимала события:

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

Это кажется задом наперед, но имейте в виду, что в ситуации "предприятия" вы, вероятно, получите доступ к этому набору данных несколькими адаптерами. Таким образом, ваш поток обновления будет иметь адаптер для себя, и ваш графический интерфейс также будет иметь свой собственный. Другое решение было бы использовать BindingList, который, как мне кажется, имеет совместимость потоков для такого типа ситуаций, но не цитируйте меня по этому поводу.

Для дополнительной информации это также может объяснить вашу проблему до сбоя. Получая доступ к DataGridView из фонового потока, вы выполняли операции с несколькими потоками.

0 голосов
/ 24 мая 2011

Я бы не стал звонить:

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

Это будет занимать все больше и больше, поскольку набор данных становится все больше и больше.

Я написал приложение, которое использует DataGridView для отображения информации о файле mp3.Я устанавливаю DataSource из DataGridView в DataTable:

this.dataGridView.DataSource = this.dataTable;

, а затем просто добавляю новую информацию в DataTable:

this.dataTable.Rows.Add(row);

Это автоматическиобновляет DataGridView.

...