Hi,
I spent some time investigating the issue...
When you call the EndEdit method, each of the Cell's Content property is pushed to your business object (in your case, a DataRowView). Later on, when an exception is caught, you call CancelEdit. In our implementation of CancelEdit, we make sure that the source collection sill contains the business object. This is done by calling the Contains method on the ItemsCollection.
I traced into the Framework's code and found out that the ItemsCollection Contains method will call the source's collection Contains method (in your case, your DataView casted as an IList.).
Everything up to here is fine. However, when digging deeper, it seems that the problem lies within the DataView's implementation of the IList interface Contains method.
When a DataRowView is being edited (it's IEditableObject interface's BeginEdit was called) and any of its field is modified with the same or a different value (which is the case in your app since it pushed back the first cell's content when calling EndEdit), the DataView's IList's Contains method returns False even if the passed DataRowView is truly within the DataRowView !!!
Since the source collection returns saying that you are calling CancelEdit for an item which is not part of the ItemsCollection, we throw the InvalidOperationException that results in your application crashing.
Here is some repro-code that clearly shows the issue... as you will see, using Object.Equals between the DataView's first DataRowView and a kept reference of the very same DataRowView returns True, however the Contains method returns False when the DataRowView is still in edition and was flagged as modified.
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
m_dataTable = new DataTable();
m_dataTable.Columns.Add( new DataColumn( "ID", typeof( int ) ) );
m_dataTable.Columns.Add( new DataColumn( "QuantityInStock", typeof( int ) ) );
m_dataTable.Columns.Add( new DataColumn( "QuantityOnOrder", typeof( int ) ) );
m_dataTable.Rows.Add( new object[] { 1, 10, 30 } );
m_dataRowViewAtPosition0 = this.DataView[ 0 ];
}
private void Button_Click( object sender, RoutedEventArgs e )
{
this.LogDataView();
m_dataRowViewAtPosition0.BeginEdit();
System.Diagnostics.Debug.WriteLine( "Changing dataRowView's value..." );
m_dataRowViewAtPosition0[ "QuantityInStock" ] = 60;
this.LogDataView();
m_dataRowViewAtPosition0.EndEdit();
this.LogDataView();
}
private void LogDataView()
{
System.Diagnostics.Debug.WriteLine(
string.Format( "DataRowView in edition: {0} \n DataView row count: {1} \n Object.Equals( Kept_Reference_Of_DataRowView_At_Position 0, DataView[ 0 ] ): {2} \n ( ( IList )DataView ).Contains( Kept_Reference_Of_DataRowView_At_Position 0 ): {3} \n ( ( IList )DataView ).Contains( DataView[ 0 ] ): {4} \n",
new object[]
{
m_dataRowViewAtPosition0.IsEdit,
this.DataView.Count,
Object.Equals( m_dataRowViewAtPosition0, this.DataView[ 0 ] ),
( ( IList )this.DataView ).Contains( m_dataRowViewAtPosition0 ),
( ( IList )this.DataView ).Contains( this.DataView[ 0 ] )
}
) );
}
public DataView DataView
{
get
{
return m_dataTable.DefaultView;
}
}
private DataTable m_dataTable;
public DataRowView DataRowViewAtPosition0
{
get
{
return m_dataRowViewAtPosition0;
}
}
private DataRowView m_dataRowViewAtPosition0;
}
Note that if using a DataGridCollectionView as your DataGridControl's source instead of a BindingListCollectionView, you would not have this issue since the DataGridCollectionView does use Object.Equals instead of the Contains call.
Ok, so now you are wondering what should be done...
Well, we plan on including a workaround in our very code so that when dealing with a DataView, we use a For loop of Object.Equals instead of the Contains method.
In the meantime though, I would strongly suggest using a DataGridCollectionView instead of a BindingListCollecitonView... it does not just provide you with a workaround for the issue you found, but it will also improve your grid's performance when grouping and sorting.
In the vast majority of cases, there's really only upsides to using the DataGridCollectionView.
Regards,
Pierre-Luc Ledoux
Software Developer
Xceed Software Inc.