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

Поэтому я попытался использовать BindingList и BindingSource, но проблема одинакова для обоих.

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

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

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

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

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

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

Я объявляю новый экземпляр frmDataWindow, в отдельном методе я передаю ссылку на DataSource (или, как я считаю, ссылку, потому что я понимаю, что C# передает все по ссылке как значение по умолчанию) экземпляру frmDataWindow. Этот источник данных уже имеет репозиторий с загруженными списками моих 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, пока окно открыто, я ожидал бы, что это изменение будет отражено в моем bindingSource, где должна быть добавлена ​​новая строка или строка должна исчезают, в зависимости от действия, выполненного в списке, который связан с BindingSource.

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

Когда я выполняю какое-либо действие, остается одинаковое количество строк.

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

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

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

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

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

Дайте мне знать, если мне нужно больше информации.

Благодарю.

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

В настоящее время я НЕ использую BindingList в своем подходе, я непосредственно назначаю Мой список новому 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.List1[Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is= System.Windows.Forms.BindingSource bs.Datasource= System.Collections.Generic.List1 [Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is = System.Windows.Forms.BindingSource bs.Datasource = System.Collections.Generic.List1[Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is= System.Windows.Forms.BindingSource bs.Datasource= System.Collections.Generic.List1 [Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is = System.Windows.Forms.BindingSource bs.Datasource = System.Collections.Generic.List1[Pharaoh_Dashboard.DSOTrade] e.ListChangedType = ItemChanged sender is= System.Windows.Forms.BindingSource bs.Datasource= System.Collections.Generic.List1 [Pharaoh_Dashboard.DSOTrade] e.ListChangedType = 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;
        }
    }

2 ответа

Решение

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

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

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

C# Унаследованный класс BindingList не обновляет элементы управления

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

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

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

Я справился с этим, создав свойство для 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 каждого конкретного дочернего класса 'DSO' в моем классе репозитория.

Я назначаю 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;
                }
        }

Не уверен, какой.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

Другие вопросы по тегам