2015-05-24 6 views
0

Итак, я попытался использовать оба BindingList и BindingSource, но проблема одинакова с обоими.Источник привязки НЕ отражает новый/удаленный ряд, когда источник данных списка добавляет/удаляет элемент

Просто для фона: У меня есть приложение, в котором я получаю обновления на торговых объектах из api. Я получаю в реальном времени обновления от api (может быть add/update/remove updateType) и обрабатывать их в классе репозитория, который имеет списки каждого соответствующего типа объекта, которые все наследуют от родительского класса вызова DSO (для DataSourceObject).

Это хранилище имеет экземпляр в другой класс называется DataSource (я ссылаться на это позже.)

Так у меня есть несколько списков в моем Repository, который сидит в DataSource и все операции (добавление/удаление/обновление), работать в этих списках до этого момента.

Теперь, в моем пользовательском интерфейсе, у меня есть форма frmDashboard, которая вызывает форму frmDataWindow.

В этом frmDataWindow есть DataGridView, где я хочу показать свои различные объекты DSO childClass (3 примера - DSOPorfolio, DSOInstrument, DSOTrade).

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

я объявляю новый экземпляр frmDataWindow, в отдельном методе я передать ссылку на источник данных (или то, что я считаю ссылкой, потому что я понимаю, что C# передает все по ссылке в качестве значения по умолчанию) в экземпляр frmDataWindow. У этого DataSource уже есть репозиторий с загруженными списками моих BusinessObjects.

В экземпляр frmDataWindow я передаю тип объекта через enum (назовем его DSOType), который я хочу связать с DataGridView.

Затем я запускаю оператор switch, который присваивает список объектов DSO источнику привязки, преобразовывая его в соответствующий тип дочернего класса DSO (так что все свойства отображаются в DataGridView).

Просто заметьте, что я уже реализовал INotifyPropertyChanged во всех моих объектах DSO.

DataSource _ds; 
    BindingSource bs; 

    public void AssignDataSource(DataSource ds) 
    { 
     _ds = ds; 
    } 
    public void AssignDSO(DSOType type) 
    { 
     try 
     { 
      _dsoType = type; 
      dgvMain.Rows.Clear(); 
      dgvTotal.Rows.Clear(); 


       switch (_dsoType) 
       { 
        case DSOType.Portfolio: 
         { 
          bs = new BindingSource(_ds.DSORepository.GetDSOList(type).ConvertAll(x => (DSOPortfolio)x), null); 

          break; 
         } 
        case DSOType.Instrument: 
         { 
          bs = new BindingSource(_ds.DSORepository.GetDSOList(type).ConvertAll(x => (DSOInstrument)x), null); 
          break; 
         } 
        case DSOType.Trade: 
         { 
          bs = new BindingSource(_ds.DSORepository.GetDSOList(type).ConvertAll(x => (DSOTrade)x), null); 
          break; 
         } 
        case DSOType.ClosedTrade: 
         { 
          bs = new BindingSource(_ds.DSORepository.GetDSOList(type).ConvertAll(x => (DSOClosedTrade)x), null); 
          break; 
         } 
        case DSOType.Order: 
         { 
          bs = new BindingSource(_ds.DSORepository.GetDSOList(type).ConvertAll(x => (DSOOrder)x), null); 
          break; 
         } 
        case DSOType.Position: 
         { 
          bs = new BindingSource(_ds.DSORepository.GetDSOList(type).ConvertAll(x => (DSOPosition)x), null); 
          break; 
         } 
        default: 
         { 
          bs = null; 
          break; 
         } 
       } 


      string text = text = _ds.DSORepository.GetDSOList(type)[0].ObjectType.ToString(); 

      dgvMain.DataSource = bs; 

      this.Text = text; 

      bs.ListChanged += new ListChangedEventHandler(bs_ListChanged); 


      settingsFullPath = settingsDirectory + @"\" + "DataGridView-" + this.Text + ".xml"; 


      dgvMain.ReadOnly = true; 

    } 

Таким образом, в этот момент, когда я запускаю мое приложение, я могу получить DataGridView населенного с соответствующими дочерними объектами DSO И когда происходит изменение в какой-то объект, он отражает должным образом и обновлениями в DataGridView. ОДНАКО, когда я добавляю/удаляю объект из своего списка в моем репозитории, скажем, новый DSOTrade, в то время как окно открыто, я ожидаю, что это изменение будет отражено в моем bindSource, где должна быть добавлена ​​новая строка или строка должен исчезнуть в зависимости от действия, предпринятого в списке, который привязан к BindingSource.

Этого не происходит.

Когда я беру любое действие, остается такое же количество строк.

Я выполнил дополнительный этап тестирования (просто добавил клик) и чтобы я мог добавить точку останова и сравнить мои привязкиSource/Datagridview/и список, из которого происходят объекты. Кажется, что список Binding не изменяет его количество, чтобы отразить новый/удаленный элемент.

Допустим, что изначально было 3 строки, и теперь я добавил один в свой список. Затем я запускаю свой тест, установив это событие click, я вижу, что список (который находится в репозитории) правильно обновлен и теперь имеет счет 4, а BindingSource (и, конечно, DataGridView) все еще имеет кол-во 3.

Если бы я должен был удалить товар (скажем, счет 3 снова). Я запускаю тот же тест, и у List есть счет 2, а BindingSource по-прежнему имеет значение 3.

Еще одна важная вещь, которую следует отметить, заключается в том, что мои DSO имеют свойство, в котором указывается последний тип обновления. Когда я собираюсь удалить элемент из списка, UpdateType для этого свойства изменится на «DELETE». Это изменение действительно отражено в моем DataGridView, который говорит мне, что изменение свойства все еще проходит через BidnginSOurce, но добавление/удаление элемента не происходит через BindingSource.

У кого-нибудь есть мысли? Отрывая мои волосы от этого.

Сообщите мне, если мне нужно разместить дополнительную информацию.

Спасибо.

Edited в ответ на вопросы Марка Gravell в: В моем хранилище настоящее время я использую System.Collections.Generic.List, Для каждого из моих списков, они являются List (родительский класс для своих объектов).

Я в настоящее время не использую BindingList в своем подходе, я непосредственно назначаю My List новому BindingSource, как показано выше, однако я также попробовал BindingList и получал те же результаты, после этого редактирования я сделаю другое редактирование после Я повторяю с BindingList и выставляю свой код.

В настоящее время я обрабатываю событие ListChanged следующим образом. Я хотел обновить свой DataGridView (dgvMain) на ListChangedType.ItemAdded или ListChangedType.ItemDeleted (хотя обновление также не помогает, я тестировал обновление при событии с нажатием кнопки), однако событие всегда, кажется, срабатывает как ListChangedType .ItemChanged.

Когда я добавляю или удаляю элемент, НИЧЕГО не срабатывает в списке, измененном событии.

Ниже вы видите код обработки событий - это тики обновлений с сервера API, которые в настоящее время работают и работают в течение новой недели.

void bs_ListChanged(object sender, ListChangedEventArgs e) 
     { 
      Debug.WriteLine("sender is= " + sender.ToString()); 
      Debug.WriteLine("bs.Datasource= " + bs.DataSource); 
      Debug.WriteLine("e.ListChangedType = " + e.ListChangedType); 
      if (e.ListChangedType == ListChangedType.ItemAdded || e.ListChangedType == ListChangedType.ItemDeleted) 
      { 
       SystemControlInvoker.InvokeControl(dgvMain, RefreshDGV); 
      } 
     } 

отправитель = System.Windows.Forms.BindingSource bs.Datasource = System.Collections.Generic.List 1[Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is= System.Windows.Forms.BindingSource bs.Datasource= System.Collections.Generic.List 1 [Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged отправитель = System.Windows. Forms.BindingSource bs.Datasource = System.Collections.Generic.List 1[Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is= System.Windows.Forms.BindingSource bs.Datasource= System.Collections.Generic.List 1 [Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged отправитель = System.Windows.Forms.BindingSource bs.Datasource = System.Collections.Generic. Список 1[Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is= System.Windows.Forms.BindingSource bs.Datasource= System.Collections.Generic.List 1 [Pharaoh_Dashboard.DSOTrade] e.ListChangedTy pe = ItemChanged

Я реализую INotifyPropertyChanged следующим образом.

public abstract class DSO : IDisposable, INotifyPropertyChanged 
    { 
     protected bool _isCreationComplete; 
     string _dataSourceObjectID; 
     string _dataSourceID; 
     protected string _portfolioName; 
     int _dsoInstance = -1; 

     protected DSOType _objectType; 
     protected DSOUpdateType _updateType; 

     public string DataSourceObjectID 
     { 
      get { return _dataSourceObjectID; } 
      set 
      { 
       _dataSourceObjectID = value; 
       NotifyPropertyChanged("DataSourceObjectID"); 
      } 
     } 
     public string DataSourceID 
     { 
      get { return _dataSourceID; } 
      set 
      { 
       _dataSourceID = value; 
       NotifyPropertyChanged("DataSourceID"); 
      } 
     } 
     public string PortfolioName 
     { 
      get { return _portfolioName; } 
      set 
      { 
       _portfolioName = value; 
       NotifyPropertyChanged("PortfolioName"); 
      } 
     } 
     public DSOType ObjectType 
     { 
      get { return _objectType; } 
      set 
      { 
       _objectType = value; 
       NotifyPropertyChanged("ObjectType"); 
      } 
     } 
     public DSOUpdateType UpdateType 
     { 
      get { return _updateType; } 
      set 
      { 
       _updateType = value; 
       NotifyPropertyChanged("UpdateType"); 
      } 
     } 
     public bool CreationIsComplete 
     { 
      get { return _isCreationComplete; } 
      set 
      { 
       _isCreationComplete = value; 
       NotifyPropertyChanged("CreationIsComplete"); 
      } 
     } 

     public DSO() 
     { 
      _isCreationComplete = false; 
     } 

     public void SetDSOInstance(int dsoInstance) 
     { 
      //do this so it can only be assigned once 
      if (_dsoInstance == -1) 
      { 
       _dsoInstance = dsoInstance; 
       _dataSourceObjectID = _dataSourceID + "-" + _objectType + "-" + _dsoInstance; 
      } 
     } 

     public void Dispose() 
     { 
      //throw new NotImplementedException(); 
     } 
     protected void NotifyPropertyChanged(String propertyName = "") 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
     public event PropertyChangedEventHandler PropertyChanged; 
    } 



    public class DSOTrade : DSO 
    { 
     int _amount; 
     string _buySell; 
     string _instrumentID; 
     decimal _openRate; 
     DateTime _openTime; 
     decimal _commission; 
     decimal _rolloverInterest; 
     string _tradeID; 
     decimal _usedMargin; 
     decimal _close; 
     decimal _grossPnL; 
     decimal _netPnL; 
     decimal _limit; 
     decimal _pnl; 
     decimal _stop; 
     string _instrument; 
     bool _isChangeFromInstrumentTick; 

     TimeSpan _tradeTimeLength; 

     public string TradeID 
     { 
      get { return _tradeID; } 
      set 
      { 
       if (value != _tradeID) 
       { 
        _tradeID = value; 
        NotifyPropertyChanged("TradeID"); 
       } 
      } 
     } 
     public string BuySell 
     { 
      get { return _buySell; } 
      set 
      { 
       if (value != _buySell) 
       { 
        _buySell = value; 
        NotifyPropertyChanged("BuySell"); 
       } 
      } 
     } 
     public string InstrumentID 
     { 
      get { return _instrumentID; } 
      set 
      { 
       if (value != _instrumentID) 
       { 
        _instrumentID = value; 
        NotifyPropertyChanged("InstrumentID"); 
       } 
      } 
     } 
     public int Amount 
     { 
      get { return _amount; } 
      set 
      { 
       if (value != _amount) 
       { 
        _amount = value; 
        NotifyPropertyChanged("Amount"); 
       } 
      } 
     } 
     public decimal OpenRate 
     { 
      get { return _openRate; } 
      set 
      { 
       if (value != _openRate) 
       { 
        _openRate = value; 
        NotifyPropertyChanged("OpenRate"); 
       } 
      } 
     } 
     public decimal Commission 
     { 
      get { return _commission; } 
      set 
      { 
       if (value != _commission) 
       { 
        _commission = value; 
        NotifyPropertyChanged("Commission"); 
       } 
      } 
     } 
     public decimal RolloverInterest 
     { 
      get { return _rolloverInterest; } 
      set 
      { 
       if (value != _rolloverInterest) 
       { 
        _rolloverInterest = value; 
        NotifyPropertyChanged("RolloverInterest"); 
       } 
      } 
     } 
     public decimal UsedMargin 
     { 
      get { return _usedMargin; } 
      set 
      { 
       if (value != _usedMargin) 
       { 
        _usedMargin = value; 
        NotifyPropertyChanged("UsedMargin"); 
       } 
      } 
     } 
     public DateTime OpenTime 
     { 
      get { return _openTime; } 
      set 
      { 
       if (value != _openTime) 
       { 
        _openTime = value; 
        NotifyPropertyChanged("OpenTime"); 
       } 
      } 
     } 

     //Calculated 
     public decimal Close 
     { 
      get { return _close; } 
      set 
      { 
       if (value != _close) 
       { 
        _close = value; 
        NotifyPropertyChanged("Close"); 
       } 
      } 
     } 
     public decimal PnL 
     { 
      get { return _pnl; } 
      set 
      { 
       if (value != _pnl) 
       { 
        _pnl = value; 
        NotifyPropertyChanged("PnL"); 
       } 
      } 
     } 
     public decimal GrossPnL 
     { 
      get { return _grossPnL; } 
      set 
      { 
       if (value != _grossPnL) 
       { 
        _grossPnL = value; 
        NotifyPropertyChanged("GrossPnL"); 
       } 
      } 
     } 
     public decimal NetPnL 
     { 
      get { return _netPnL; } 
      set 
      { 
       if (value != _netPnL) 
       { 
        _netPnL = value; 
        NotifyPropertyChanged("NetPnL"); 
       } 
      } 
     } 
     public decimal Limit 
     { 
      get { return _limit; } 
      set 
      { 
       if (value != _limit) 
       { 
        _limit = value; 
        NotifyPropertyChanged("Limit"); 
       } 
      } 
     } 
     public decimal Stop 
     { 
      get { return _stop; } 
      set 
      { 
       if (value != _stop) 
       { 
        _stop = value; 
        NotifyPropertyChanged("Stop"); 
       } 
      } 
     } 
     public string Instrument 
     { 
      get { return _instrument; } 
      set 
      { 
       if (value != _instrument) 
       { 
        _instrument = value; 
        NotifyPropertyChanged("Instrument"); 
       } 
      } 
     } 
     public TimeSpan TradeTimeLength 
     { 
      get { return _tradeTimeLength; } 
      set 
      { 
       if (value != _tradeTimeLength) 
       { 
        _tradeTimeLength = value; 
        NotifyPropertyChanged("TradeTimeLength"); 
       } 
      } 
     } 
     public bool IsChangeFromInstrumentTick 
     { 
      get { return _isChangeFromInstrumentTick; } 
      set 
      { 
       if (value != _isChangeFromInstrumentTick) 
       { 
        _isChangeFromInstrumentTick = value; 
        NotifyPropertyChanged("IsChangeFromInstrumentTick"); 
       } 
      } 
     } 

     public DSOTrade() 
     { 
      _objectType = DSOType.Trade; 
     } 
    } 
+0

INotifyPropertyChanged не используется для списков; какой у вас тип списка? Он реализует IBindingList? И добавлен ли ListChanged для ваших изменений? –

+0

@Marc Gravell: Marc I отредактировал мое сообщение, чтобы ответить на ваши вопросы. Пожалуйста, дайте мне знать, если это не ясно или вам нужна дополнительная информация. – xSquared

ответ

1

ОК, наконец-то нашли решение моей проблемы.

Махмуд благодарит вас за предложение, но у меня возникла такая же проблема.

Где-то в моем кодексе я думаю, что что-то (я не знаю точно, что) теряется, когда я ссылаюсь на основной список на источник привязки. Возможно, это был тот факт, что это был общий список, который я использовал (но даже ObservableCollection вызывал ту же проблему. Я получил немного лучшее понимание из этой ссылки, где Марк Гравелл отвечает на другой аналогичный вопрос (см. Его редактирование на свой ответ) .

C# Inherited class BindingList<T> doesn't update controls

так что я в конечном итоге, используя ThreadedBindingList как и Марк рекомендуемую в этой ссылке.

https://groups.google.com/forum/#!msg/microsoft.public.dotnet.languages.csharp/IU5ViEsW9Nk/Bn9WgFk8KvEJ

Просто дополнительное примечание. Если бы ударил Exception Cross-Thread в base.OnListChanged(e); даже при использовании h это ThreadedBindingList. Это произошло потому, что SynchronizationContext всегда был нулевым, когда поток, на котором был создан ThreadedBindingList, не был потоком пользовательского интерфейса. См: Why is SynchronizationContext.Current null?

Я получил вокруг этого, создав свойство для SynchronizationContext в ThreadedBindingList, и назначение его перед назначением ThreadedBindingList моей DataGridView. Версия, которую я сейчас использую, выглядит следующим образом.

public class ThreadedBindingList<T> : BindingList<T> 
    { 

     public SynchronizationContext SynchronizationContext 
     { 
      get { return _ctx; } 
      set { _ctx = value; } 
     } 

     SynchronizationContext _ctx; 
     protected override void OnAddingNew(AddingNewEventArgs e) 
     { 
      if (_ctx == null) 
      { 
       BaseAddingNew(e); 
      } 
      else 
      { 
       SynchronizationContext.Current.Send(delegate 
       { 
        BaseAddingNew(e); 
       }, null); 
      } 
     } 
     void BaseAddingNew(AddingNewEventArgs e) 
     { 
      base.OnAddingNew(e); 
     } 
     protected override void OnListChanged(ListChangedEventArgs e) 
     { 
      if (_ctx == null) 
      { 
       BaseListChanged(e); 
      } 
      else 
      { 
       _ctx.Send(delegate { BaseListChanged(e); }, null); 
      } 
     } 
     void BaseListChanged(ListChangedEventArgs e) 
     { 
      base.OnListChanged(e); 
     } 
    } 

я теперь реализовать ThreadedBindingLists каждого конкретного ребенка класса «ОКО» в моем Repository классе.

Я назначаю DataSource следующим образом в своей форме frmDataWindow.

 //Must set Synchonization Context of the current UI thread otherswise system will throw CrossThread-Exception when tryin gto add/remove a record from the BindingList 
     switch (_dsoType) 
     { 
      case DSOType.Portfolio: 
       { 
        ThreadedBindingList<DSOPortfolio> list = _ds.DSORepository.PortfolioBindingList; 
        list.SynchronizationContext = SynchronizationContext.Current; 
        dgvMain.DataSource = list; 
        list.ListChanged += new ListChangedEventHandler(list_ListChanged); 
        break; 
       } 
      case DSOType.Instrument: 
       { 
        ThreadedBindingList<DSOInstrument> list = _ds.DSORepository.InstrumentBindingList; 
        list.SynchronizationContext = SynchronizationContext.Current; 
        dgvMain.DataSource = list; 
        list.ListChanged += new ListChangedEventHandler(list_ListChanged); 
        break; 
       } 
      case DSOType.Trade: 
       { 
        ThreadedBindingList<DSOTrade> list = _ds.DSORepository.TradeBindingList; 
        list.SynchronizationContext = SynchronizationContext.Current; 
        dgvMain.DataSource = list; 
        list.ListChanged +=new ListChangedEventHandler(list_ListChanged); 
        break; 
       } 
      case DSOType.ClosedTrade: 
       { 
        ThreadedBindingList<DSOClosedTrade> list = _ds.DSORepository.ClosedTradeBindingList; 
        list.SynchronizationContext = SynchronizationContext.Current; 
        dgvMain.DataSource = list; 
        list.ListChanged += new ListChangedEventHandler(list_ListChanged); 
        break; 
       } 
      case DSOType.Order: 
       { 
        ThreadedBindingList<DSOOrder> list = _ds.DSORepository.OrderBindingList; 
        list.SynchronizationContext = SynchronizationContext.Current; 
        dgvMain.DataSource = list; 
        list.ListChanged += new ListChangedEventHandler(list_ListChanged); 
        break; 
       } 
      case DSOType.Position: 
       { 
        ThreadedBindingList<DSOPosition> list = _ds.DSORepository.PositionBindingList; 
        list.SynchronizationContext = SynchronizationContext.Current; 
        dgvMain.DataSource = list; 
        list.ListChanged += new ListChangedEventHandler(list_ListChanged); 
        break; 
       } 
      default: 
       { 
        break; 
       } 
     } 
0

Не знаете, какой .net framework вы используете. Но если вы можете использовать ObservableCollection, это упростит вашу жизнь, потому что этот тип уже реализует INotifyPropetyChanged.

Посмотрите на https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx

Список не реализует INotifyPropertyChanged и не имеет внутренний механизм для распространения информации о ее внутреннем изменении списка. Вы можете проверить приведенную ниже ссылку для проверки. https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx

+0

Хорошо, я думаю, что я понимаю вас и Марка лучше сейчас ... Я постараюсь выполнить ObservableCollection завтра и позволит вам, ребята, узнать, разрешает ли это проблема. Благодарю. – xSquared