【问题标题】:Binding Source NOT reflecting new/removed Row when List datasource adds/removes item当列表数据源添加/删除项目时,绑定源不反映新/删除的行
【发布时间】:2015-08-06 03:55:51
【问题描述】:

所以我尝试同时使用 BindingList 和 BindingSource,但两者的问题是一样的。

只是为了一些背景: 我有一个应用程序,我从 api 接收交易对象的更新。 我从 api 接收实时更新(可以是添加/更新/删除 updateType)并将它们处理到一个存储库类中,该存储库类列出了每个相应的对象类型,这些对象类型都继承自父类调用 DSO(对于 DataSourceObject)。

这个 Repository 在另一个名为 DataSource 的类中有一个实例(我稍后会引用它。)

所以我的存储库中有多个列表,这些列表位于数据源中,所有操作(添加/删除/更新)在这些列表上都可以正常工作。

现在,在我的 UI 上,我有一个 frmDashboard 表单,它调用了一个 frmDataWindow 表单。

这个 frmDataWindow 有一个 DataGridView,我想在其中显示我的各种 DSO 子类对象(3 个示例是 DSOPorfolio、DSOInstrument、DSOTrade)。

这是我遇到问题的地方,我尝试了不同的方法,但我目前正在使用以下方法:

我声明了一个新的 frmDataWindow 实例,在一个单独的方法中我将一个 DataSource 的引用(或者我认为是一个引用,因为我的理解是 c# 默认通过引用传递所有内容)传递给 frmDataWindow 的实例.此 DataSource 已经有一个存储库,其中包含我的 BusinessObjects 加载列表。

到 frmDataWindow 实例,然后我通过枚举(我们称之为 DSOType)传递我想要绑定到 DataGridView 的对象类型。

然后我运行一个 switch 语句,将 DSO 对象列表分配给绑定源,同时将其转换为 DSO 子类的正确类型(因此所有属性都显示在 DataGridView 上)。

请注意,我已经在所有 DSO 对象中实现了 INotifyPropertyChanged。

   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;

    }

因此,此时,当我运行我的应用程序时,我可以获得一个填充了正确 DSO 子对象的 DatagridView,并且当某个对象发生更改时,它会正确反映并在 DataGridView 中更新。 但是,当我从存储库中的列表中添加/删除一个对象时,假设一个新的 DSOTrade,当窗口打开时,我希望该更改反映在我的 bindingSource 中应该添加新行或行的地方消失,具体取决于在绑定到 BindingSource 的列表中执行的操作。

这不会发生。

当我采取任一操作时,保留的行数相同。

我采取了一个额外的测试步骤(甚至添加了一个点击),以便我可以添加一个断点并比较我的 bindingSource/Datagridview/ 和对象来自哪里的列表。绑定列表似乎没有改变它的计数来反映新/删除的项目。

假设最初有 3 行,现在我在列表中添加了 1 行。 然后我通过关闭该单击事件来运行我的测试,我看到列表(位于存储库中)已正确更新,现在计数为 4,而 BindingSource(当然还有 DataGridView)仍然有计数3 个。

如果我要删除一个项目(假设计数再次为 3)。我运行相同的测试,List 的计数为 2,而 BindingSource 的计数仍为 3。

需要注意的更重要的一点是,我的 DSO 有一个属性,它表明最后的更新类型。当我要从列表中删除一个项目时,该属性的 UpdateType 更改为“DELETE”。这种变化实际上反映在我的 DataGridView 中,它告诉我属性上的变化仍然是通过 BidnginSource 来的,但是项目的添加/删除不是通过 BindingSource 来的。

有人有什么想法吗?扯掉我的头发。

如果我需要发布更多信息,请告诉我。

谢谢。

针对 Marc Gravell 的问题进行了编辑: 在我的存储库中,我目前正在使用 System.Collections.Generic.List, 对于我的每个列表,它们都是一个 List(我的对象的父类)。

我目前没有在我的方法中使用 BindingList,我直接将我的列表分配给一个新的 BindingSource,如上所示,但是我也尝试过 BindingList 并且得到了相同的结果,在此编辑之后我将在我之后进行另一个编辑使用 BindingList 重新测试并发布我的代码。

我目前正在通过以下方式处理我的 ListChanged 事件。 我想在 ListChangedType.ItemAdded 或 ListChangedType.ItemDeleted 上刷新我的 DataGridView (dgvMain)(即使刷新也无济于事,我通过在按钮单击事件中刷新进行了测试。),但是,该事件似乎总是作为 ListChangedType 触发。项目已更改。

当我添加或删除一个项目时,列表更改事件中没有触发任何内容。

您在此事件处理代码下方看到的是来自 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 发件人是= 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 发件人是= 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;
        }
    }

【问题讨论】:

  • INotifyPropertyChanged 不用于列表;你的列表类型是什么?它是否实现 IBindingList ? ListChanged 是否针对您的更改提出?
  • @Marc Gravell:Marc 我编辑了我的帖子来回答你的问题。如果不清楚或者您需要更多信息,请告诉我。

标签: c# datagridview bindingsource bindinglist


【解决方案1】:

好的,终于找到解决我问题的方法了。

Mahmoud 感谢您的建议,但我最终遇到了同样的问题。

当我从主列表引用绑定源时,我认为在我的代码中某处(我不知道具体是什么)丢失了。也许这是我正在使用的通用列表的事实(但即使是 ObservableCollection 也导致了同样的问题。我从这个链接中得到了更好的理解,Marc Gravell 回答了另一个类似的问题(参见他对他的回答的编辑) .

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

所以我最终使用了 Marc 在此链接中推荐的 ThreadedBindingList。

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

只是一个附加说明。即使在使用他的 ThreadedBindingList 时,在base.OnListChanged(e); 上也遇到了跨线程异常。这是因为当创建 ThreadedBindingList 的线程不是 UI 线程时,SynchronizationContext 始终为空。 看: Why is SynchronizationContext.Current null?

我通过在 ThreadedBindingList 中为 SynchronizationContext 创建一个属性并在将 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);
        }
    }

我现在在我的 Repository 类中实现“DSO”的每个具体子类的 ThreadedBindingLists。

我在 frmDataWindow 窗体中按以下方式分配 DataSource。

     //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;
                }
        }

【讨论】:

    【解决方案2】:

    不确定您使用的是哪个 .net 框架。但是,如果您能够使用 ObservableCollection,这将使您的生活更轻松,因为这种类型已经实现了 INotifyPropetyChanged。

    看看 https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx

    List 没有实现 INotifyPropertyChanged,也没有内部机制来传播有关其内部列表更改的信息。您可以查看以下链接进行验证。 https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx

    【讨论】:

    • 好吧,我想我现在更了解你和 Marc... 我明天会尝试实施 ObservableCollection,如果这能解决问题,我会告诉你们。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    相关资源
    最近更新 更多