Как правильно обновлять представление данных с привязкой к данным из фонового потока? - PullRequest
8 голосов
/ 18 января 2009

У меня есть пользовательский объект, который реализует INotifyPropertyChanged. У меня есть коллекция этих объектов, где коллекция основана на BindingList Я создал источник привязки для коллекции и установил источники данных для bindingsource и datagridview.

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

BindingSource не может быть своим собственным источником данных. Не устанавливайте в свойствах DataSource и DataMember значения, ссылающиеся на BindingSource

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

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/3566f7c7-eb47-422e-ab09-9549a18da360/

Я создал и инициализировал переменные операций для каждого поста в своем бизнес-объекте, а затем поместил две функции событий в свой класс коллекции. Это правильно скомпилировано, но без исключения зависает при запуске.

Я видел много сообщений, в которых говорится об использовании Invoke / Begin Invoke, но я не вызываю какие-либо функции в пользовательском интерфейсе, я просто обновляю бизнес-объекты, поэтому я не уверен, куда я помещу вызовы invoke.

Одно ограничение: я хочу, чтобы бизнес-объект не знал, кто его отображает (так как имеется несколько потребителей), поэтому отправка ссылок GUI в бизнес-объект, чтобы позже я мог вызывать invoke с использованием этих ссылок, не является вариант.

Ответы [ 3 ]

14 голосов
/ 18 января 2009

Я нашел этот класс на форуме, который работает. Просто используйте это вместо BindingList

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;
using System.Threading;

namespace Utility
{
    public class ThreadedBindingList<T> : BindingList<T>
    {
        SynchronizationContext ctx = SynchronizationContext.Current;

        protected override void OnAddingNew(AddingNewEventArgs e)
        {

            if (ctx == null)
            {
                BaseAddingNew(e);
            }
            else
            {
                ctx.Send(delegate
                {
                    BaseAddingNew(e);
                }, null);
            }
        }
        void BaseAddingNew(AddingNewEventArgs e)
        {
            base.OnAddingNew(e);
        }
        protected override void OnListChanged(ListChangedEventArgs e)
        {
           // SynchronizationContext ctx = SynchronizationContext.Current;
            if (ctx == null)
            {
                BaseListChanged(e);
            }
            else
            {
                ctx.Send(delegate
                {
                    BaseListChanged(e);
                }, null);
            }
        }
        void BaseListChanged(ListChangedEventArgs e)
        {
            base.OnListChanged(e);
        }
    } 
}
1 голос
/ 09 апреля 2009

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

using System.ComponentModel; 
using System.Threading;

namespace Utility 
{
  public class ThreadedBindingList : BindingList 
  {
    SynchronizationContext ctx = SynchronizationContext.Current;
    protected override void OnAddingNew(AddingNewEventArgs e)
    {
      if (ctx == null)
      {
        BaseAddingNew(e);
      }
      else
      {
        ctx.Send(delegate { BaseAddingNew(e); }, null);
      }
    }

    void BaseAddingNew(AddingNewEventArgs e)
    {
      base.OnAddingNew(e);
    }

    protected override void OnListChanged(ListChangedEventArgs e)
    {
      // SynchronizationContext ctx = SynchronizationContext.Current;
      if (ctx == null)
      {
        BaseListChanged(e);
      }
      else
      {
        ctx.Send(delegate { BaseListChanged(e); }, null);
      }
    }

    void BaseListChanged(ListChangedEventArgs e)
    {
      base.OnListChanged(e);
    }
  }
}
0 голосов
/ 14 октября 2014

Не совсем поточнобезопасно, но это небольшое изменение в приведенных выше ответах может оказать большое влияние, если ваш фоновый поток изменяет свойства объекта быстрее, чем они могут отображаться;

protected override void OnListChanged(ListChangedEventArgs e)
{
  // SynchronizationContext ctx = SynchronizationContext.Current;
  if (ctx == null)
  {
    BaseListChanged(e);
  }
  else if(e.ListChangedType == ListChangedType.ItemChanged)
  {
    ctx.Post(delegate { BaseListChanged(e); }, null);
  }
  else
  {
    ctx.Send(delegate { BaseListChanged(e); }, null);
  }
}

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

...