当列出数据源 adds/removes 项时,绑定源不反映 new/removed 行
Binding Source NOT reflecting new/removed Row when List datasource adds/removes item
所以我尝试同时使用 BindingList 和 BindingSource,但是两者的问题都是一样的。
只是为了一些背景:
我有一个应用程序,可以从 api 接收交易对象的更新。
我从 api 实时接收更新(可以是 add/update/remove updateType)并将它们处理到存储库 class 中,该存储库列出了每个相应的对象类型,这些对象类型都继承自父 class 调用 DSO(对于 DataSourceObject)。
此存储库在另一个名为 DataSource 的 class 中有一个实例(我稍后会引用它。)
所以我的存储库中有多个列表,它们位于 DataSource 和所有操作 (add/remove/update) 中,到目前为止这些列表都可以正常工作。
现在,在我的 UI 上,我有一个 frmDashboard 表单,它调用一个 frmDataWindow 表单。
这个 frmDataWindow 有一个 DataGridView,我想在其中显示我的各种 DSO childClass 对象(3 个示例是 DSOPortfolio、DSOInstrument、DSOTrade)。
这是我遇到问题的地方,我尝试了不同的方法,但我目前使用的是以下方法:
我声明了一个 frmDataWindow 的新实例,在一个单独的方法中我将 DataSource 的引用(或者我认为是引用,因为我的理解是 c# 默认通过引用传递所有内容)传递给实例的 frmDataWindow。此 DataSource 已有一个存储库,其中包含我的 BusinessObjects 的已加载列表。
然后,我将要绑定到 DataGridView 的对象类型通过枚举(我们称之为 DSOType)传递给 frmDataWindow 实例。
然后我 运行 一个 switch 语句,将 DSO 对象列表分配给绑定源,同时将其转换为正确类型的 DSO 子 class(因此所有属性都显示在 DataGridView 上).
请注意,我已经在我的所有 DSO 对象中实现了 INotify属性更改。
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 中更新。
但是,当我 add/remove 我的存储库中的列表中的一个对象时,可以说是一个新的 DSOTrade,而 window 是打开的,我希望该更改反映在我的 bindingSource 中,其中一个新行应该添加或一行应该消失,具体取决于在绑定到 BindingSource 的列表中采取的操作。
这不会发生。
当我执行任一操作时,保留的行数相同。
我采取了额外的测试步骤(甚至只是添加了一次点击),这样我就可以添加一个断点并比较我的 bindingSource/Datagridview/ 并列出对象的来源。绑定列表似乎没有改变它的计数以反映 new/removed 项。
假设原来有 3 行,现在我在列表中添加了一行。
然后我 运行 通过触发该单击事件进行测试,我看到列表(位于存储库中)已正确更新并且现在计数为 4,而 BindingSource(当然还有 DataGridView)仍然有 3 个计数。
如果我要删除一个项目(假设计数再次为 3)。我 运行 同样的测试,List 的计数为 2,而 BindingSource 的计数仍然为 3。
还有一件需要注意的重要事情是我的 DSO 有一个 属性 说明最后的更新类型。当我要从列表中删除一个项目时,属性 的 UpdateType 更改为 'DELETE'。此更改实际上反映在我的 DataGridView 中,它告诉我 属性 上的更改仍在通过 BidnginSOurce 进行,但项目的 add/removal 未通过 BindingSource 进行。
有人有什么想法吗?把我的头发弄得乱七八糟。
如果我需要 post 更多信息,请告诉我。
谢谢。
为回应 Marc Gravell 的问题而编辑:
在我的存储库中,我目前正在使用 System.Collections.Generic.List,
对于我的每个列表,它们都是一个列表(我的对象的父 class)。
我目前没有在我的方法中使用 BindingList,我直接将 My List 分配给一个新的 BindingSource,如上所示,但是我也尝试过 BindingList 并得到相同的结果,在此编辑之后我将进行另一次编辑在我用 BindingList 重新测试后 post 我的代码。
我目前正在按以下方式处理我的 ListChanged 事件。
我想在 ListChangedType.ItemAdded 或 ListChangedType.ItemDeleted 上刷新我的 DataGridView (dgvMain)(即使刷新也无济于事,我通过在按钮单击事件中刷新进行了测试。),但是,该事件似乎总是触发关闭为 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.List
1[Pharaoh_Dashboard.DSOTrade]
e.ListChangedType = 项目已更改
发件人是= 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.List
1[Pharaoh_Dashboard.DSOTrade]
e.ListChangedType = 项目已更改
发件人是= 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.List
1[Pharaoh_Dashboard.DSOTrade]
e.ListChangedType = 项目已更改
我实现INotify属性按以下方式更改。
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;
}
}
不确定您使用的是哪个 .net 框架。但是如果你能够使用 ObservableCollection 这将使你的生活更轻松,因为这种类型已经实现了 INotifyPropetyChanged。
看看
https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx
List 没有实现 INotifyPropertyChanged 并且没有内部机制来传播有关其内部列表更改的信息。你可以查看下面的link来验证。
https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx
好的,终于找到了解决我的问题的方法。
Mahmoud 感谢您的建议,但我最终遇到了同样的问题。
在我的代码中的某处,当我从主列表引用绑定源时,我认为某些东西(我不知道到底是什么)丢失了。也许这是我正在使用的通用列表的事实(但即使是 ObservableCollection 也会导致同样的问题。我从这个 link 中得到了更好的理解,其中 Marc Gravell 回答了另一个类似的问题(见他的编辑他的回答)。
C# Inherited class BindingList<T> doesn't update controls
所以我最终使用了 ThreadedBindingList,正如 Marc 在此 link 中推荐的那样。
补充说明。即使在使用他的 ThreadedBindingList 时,也会在 base.OnListChanged(e);
处遇到跨线程异常。这是因为当创建 ThreadedBindingList 的线程不是 UI 线程时,SynchronizationContext 始终为 null。
看:
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);
}
}
我现在在我的存储库 class.
中实现了 'DSO' 的每个具体子 Class 的 ThreadedBindingLists
我在我的 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;
}
}
所以我尝试同时使用 BindingList 和 BindingSource,但是两者的问题都是一样的。
只是为了一些背景: 我有一个应用程序,可以从 api 接收交易对象的更新。 我从 api 实时接收更新(可以是 add/update/remove updateType)并将它们处理到存储库 class 中,该存储库列出了每个相应的对象类型,这些对象类型都继承自父 class 调用 DSO(对于 DataSourceObject)。
此存储库在另一个名为 DataSource 的 class 中有一个实例(我稍后会引用它。)
所以我的存储库中有多个列表,它们位于 DataSource 和所有操作 (add/remove/update) 中,到目前为止这些列表都可以正常工作。
现在,在我的 UI 上,我有一个 frmDashboard 表单,它调用一个 frmDataWindow 表单。
这个 frmDataWindow 有一个 DataGridView,我想在其中显示我的各种 DSO childClass 对象(3 个示例是 DSOPortfolio、DSOInstrument、DSOTrade)。
这是我遇到问题的地方,我尝试了不同的方法,但我目前使用的是以下方法:
我声明了一个 frmDataWindow 的新实例,在一个单独的方法中我将 DataSource 的引用(或者我认为是引用,因为我的理解是 c# 默认通过引用传递所有内容)传递给实例的 frmDataWindow。此 DataSource 已有一个存储库,其中包含我的 BusinessObjects 的已加载列表。
然后,我将要绑定到 DataGridView 的对象类型通过枚举(我们称之为 DSOType)传递给 frmDataWindow 实例。
然后我 运行 一个 switch 语句,将 DSO 对象列表分配给绑定源,同时将其转换为正确类型的 DSO 子 class(因此所有属性都显示在 DataGridView 上).
请注意,我已经在我的所有 DSO 对象中实现了 INotify属性更改。
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 中更新。 但是,当我 add/remove 我的存储库中的列表中的一个对象时,可以说是一个新的 DSOTrade,而 window 是打开的,我希望该更改反映在我的 bindingSource 中,其中一个新行应该添加或一行应该消失,具体取决于在绑定到 BindingSource 的列表中采取的操作。
这不会发生。
当我执行任一操作时,保留的行数相同。
我采取了额外的测试步骤(甚至只是添加了一次点击),这样我就可以添加一个断点并比较我的 bindingSource/Datagridview/ 并列出对象的来源。绑定列表似乎没有改变它的计数以反映 new/removed 项。
假设原来有 3 行,现在我在列表中添加了一行。 然后我 运行 通过触发该单击事件进行测试,我看到列表(位于存储库中)已正确更新并且现在计数为 4,而 BindingSource(当然还有 DataGridView)仍然有 3 个计数。
如果我要删除一个项目(假设计数再次为 3)。我 运行 同样的测试,List 的计数为 2,而 BindingSource 的计数仍然为 3。
还有一件需要注意的重要事情是我的 DSO 有一个 属性 说明最后的更新类型。当我要从列表中删除一个项目时,属性 的 UpdateType 更改为 'DELETE'。此更改实际上反映在我的 DataGridView 中,它告诉我 属性 上的更改仍在通过 BidnginSOurce 进行,但项目的 add/removal 未通过 BindingSource 进行。
有人有什么想法吗?把我的头发弄得乱七八糟。
如果我需要 post 更多信息,请告诉我。
谢谢。
为回应 Marc Gravell 的问题而编辑: 在我的存储库中,我目前正在使用 System.Collections.Generic.List, 对于我的每个列表,它们都是一个列表(我的对象的父 class)。
我目前没有在我的方法中使用 BindingList,我直接将 My List 分配给一个新的 BindingSource,如上所示,但是我也尝试过 BindingList 并得到相同的结果,在此编辑之后我将进行另一次编辑在我用 BindingList 重新测试后 post 我的代码。
我目前正在按以下方式处理我的 ListChanged 事件。 我想在 ListChangedType.ItemAdded 或 ListChangedType.ItemDeleted 上刷新我的 DataGridView (dgvMain)(即使刷新也无济于事,我通过在按钮单击事件中刷新进行了测试。),但是,该事件似乎总是触发关闭为 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.List
1[Pharaoh_Dashboard.DSOTrade]
e.ListChangedType = 项目已更改
发件人是= 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.List
1[Pharaoh_Dashboard.DSOTrade]
e.ListChangedType = 项目已更改
发件人是= 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.List
1[Pharaoh_Dashboard.DSOTrade]
e.ListChangedType = 项目已更改
我实现INotify属性按以下方式更改。
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;
}
}
不确定您使用的是哪个 .net 框架。但是如果你能够使用 ObservableCollection 这将使你的生活更轻松,因为这种类型已经实现了 INotifyPropetyChanged。
看看 https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx
List 没有实现 INotifyPropertyChanged 并且没有内部机制来传播有关其内部列表更改的信息。你可以查看下面的link来验证。 https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx
好的,终于找到了解决我的问题的方法。
Mahmoud 感谢您的建议,但我最终遇到了同样的问题。
在我的代码中的某处,当我从主列表引用绑定源时,我认为某些东西(我不知道到底是什么)丢失了。也许这是我正在使用的通用列表的事实(但即使是 ObservableCollection 也会导致同样的问题。我从这个 link 中得到了更好的理解,其中 Marc Gravell 回答了另一个类似的问题(见他的编辑他的回答)。
C# Inherited class BindingList<T> doesn't update controls
所以我最终使用了 ThreadedBindingList,正如 Marc 在此 link 中推荐的那样。
补充说明。即使在使用他的 ThreadedBindingList 时,也会在 base.OnListChanged(e);
处遇到跨线程异常。这是因为当创建 ThreadedBindingList 的线程不是 UI 线程时,SynchronizationContext 始终为 null。
看:
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);
}
}
我现在在我的存储库 class.
中实现了 'DSO' 的每个具体子 Class 的 ThreadedBindingLists我在我的 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;
}
}