Nano Hash - криптовалюты, майнинг, программирование

Winforms Datagridview не может быть обновлен из делегата

Я пытаюсь загрузить данные из файла в список и сразу показать эти данные в Winforms Datagridview. Для этого я сделал чтение в другом потоке, используя Backgroundworker. Проблема в том, что он обновляется только один раз, и я не могу заставить его отображать больше данных. Мало того, что при нажатии он пытается получить доступ к элементу с индексом -1, который, конечно, не существует, что приводит к сбою.

Обычно, судя по тому, что я видел, простое добавление тех же данных в источник данных dataGridView1.DataSource = samelist; должно работать, но не в этом случае.

Справочная информация

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

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

Справочная информация Прогресс работы Изменено

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; в инициализации компонента и tab1source.DataSource = fooLists.GetListById(1).calculatedList; вместо dataGridView1.DataSource. Это, безусловно, помогло, так как список теперь можно щелкнуть, как и должно быть, но все же в списке всего несколько записей.

Ни один из dataGridView1.Refresh(), dataGridView1.RefreshEdit() или dataGridView1.Update() не помог, хотя сделал загрузку списка немного сложнее (вероятно, из-за введенной задержки :)).

Я попытался сделать некоторые «защиты» (семафоры, чтобы делегат не вызывается снова во время работы; попытки перехватить, хотя там никаких исключений не возникает; очистка данных перед повторной записью ...), но сработала «лучшая версия» такой же убогий, как этот, и это только затемняло код.

Мне не хватает способа обновить элемент управления Datagridview? Заранее спасибо.


  • Что такое fooLists? 01.09.2017
  • @WithMetta Это настраиваемый класс со списком списков. Метод возвращает стандартный список ‹T›. 01.09.2017
  • Взгляните на social.msdn.microsoft.com/Forums/en-US/ Может потребоваться вызвать метод обновления родительского элемента управления. Также я не уверен, что выражение dataGridView1.DataSource = fooLists.GetListById (1) .calculatedList; эффективно выполняется более одного раза. Подумай об этом. Вы устанавливаете источник данных для того же объекта, который вы его уже установили. Возможно, внутренне он проверяет это и не обновляет представление, если это так. 01.09.2017
  • Это может ответить на ваш вопрос. stackoverflow .com / questions / 11404453 / 01.09.2017
  • @WithMetta Спасибо, помогла замена List на BindingList. Не уверен, что могу принять ответ в комментарии: P. 01.09.2017

Ответы:


1

Хотя вы этого не писали, но я думаю, причина в том, что элементы, которые вы добавляете в свой источник данных, добавляются в коллекцию, которая не реализует интерфейс IBindingList. Вы, вероятно, используете простой список для хранения ваших прочитанных данных.

Если ваш 'DataSourceimplements this interface, then after adding an item to your collection an event is raised. The class that holds theDataSource, whether it is aDataGridViewor aBindingSource` получите уведомление об изменениях в списке и обновите их содержимое соответствующим образом.

Решением будет хранение ваших элементов в объекте класса 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 решает, какие считанные данные отображать и в каком формате. Это расширяет возможности повторного использования как MyForm, так и MyBackGroundWorker: MyForm может отображать то, что было извлечено другим способом, а MyBackGroundWorker можно использовать для информирования других, а не MyForm, для уведомления о прочитанных данных.

Кроме того, контекст отображения обработчика события изменения хода выполнения является контекстом MyForm, поэтому вызов не требуется.

Вы также можете назначить IBindingList непосредственно DataGridView без использования BindingSource. Единственная причина сохранить BindingSource - это если вам нужен доступ к элементу Current или если вы хотите, чтобы ваш DataGridView мог свободно заполнять 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
 }

Presto, вот и все: бесплатная сортировка или ваши столбцы, щелкнув заголовок столбца. Рассмотрите возможность изучения их примера проекта, чтобы увидеть, как работает фильтрация и как использовать несколько источников.

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

// Remove the filter, show everyone again
this.myReadData.RemoveFilter();
01.09.2017
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

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