Welcome to the Xceed Community | Help
Community Search  

Inline Editing Exception - An attempt was made to edit an item that is not part of the specified context

Sort Posts: Previous Next
  •  08-12-2008, 8:28 AM Post no. 14010

    Inline Editing Exception - An attempt was made to edit an item that is not part of the specified context

    Hi,

    I am using Xceed Datagrids for WPF 2.0 in my application and I have also provided support for in-line editing of data grids. Please find below the details for the problem that I am facing currently.

     Consider there are 3 columns in a grid - ID, FromRegion and ToRegion. All 3 are editable and editor is a textbox.  

    1. Click on ‘FromRegion’ cell.
    2. Modify the value so that there is no validation error, say 20.
    3. Now click on any cell of the same row, in this case “ToRegion”. Points to take care of in this step:
      1. After editing the cell in step 2, do not press ‘Enter’ key or do not click on any other row in the grid.
      2. This is because as soon as we do the above, the modified value of step 2 will get committed to the underlying data source. We do not want this.
    4. After clicking in the different cell of the same row, i.e. “ToRegion”, modify its value in such a manner that it will cause a validation error. For e.g. enter -10.  

    The following code is called on pressing of EndEdit button.

         protected void btnEndEdit_Click(object sender, RoutedEventArgs args) {

             try{

                wfhGrid.EndEdit();

             }

             catch (DataGridException e1)      {

                if (wfhGrid.HasValidationError)   {

                   MessageBox.Show("Exception after Validation called. CancelEdit() to be called and application is about to crash. Exception Reason - '" + e1.Message + "'");

                   wfhGrid.CancelEdit();

                }

                return ;

             }

             catch (Exception e)

             {

                MessageBox.Show("Exception. Validation already called. Exception Reason - '" + e.Message + "'");

                return;

             }

            

          }

     Case 1:

    1. After modifying “ToRegion” to -10, don’t click anywhere outside the cell. Directly click on “Call EndEdit” button.
    2. On call of “wfhGrid.EndEdit();” function, valid exception is thrown (since there is a validation error). But then on call of “wfhGrid.CancelEdit();” the following exception is being thrown.

    Exception Type: “InvalidOperationException”

    Exception Message: “An attempt was made to edit an item that is not part of the specified context.”

     Case 2:

    1. After modifying “ToRegion” to -10, click anywhere outside the cell so that the cell gets painted with a red border.
    2. Now click on “Call EndEdit” button.
    3. This time the exception is thrown on call of “wfhGrid.EndEdit();” function itself.

    Exception Type: “InvalidOperationException”

    Exception Message: “An attempt was made to edit an item that is not part of the specified context.”

     Can you please let me know why this is happening and how do I resolve this problem.

     

    I am attaching the source code for reference:

     

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Data;

    using Xceed.Wpf.DataGrid;

    using Xceed.Wpf.DataGrid.Markup;

    using Xceed.Wpf.DataGrid.Views;

    using Xceed.Wpf.DataGrid.ValidationRules;

    using System.Globalization;

    namespace DataGridTestApp

    {

    /// <summary>

    /// Interaction logic for Window1.xaml

    /// </summary>

    public partial class Window1 : System.Windows.Window

    {

    AircraftCrew.AircraftCrewDataTable _dtAircraftCrew;

    public Window1()

    {

    _dtAircraftCrew = new AircraftCrew.AircraftCrewDataTable(); // This is a typed dataset

    InitializeComponent();

    InitializeGrid();

    }

    private void InitializeGrid()

    {

    AircraftCrew.AircraftCrewRow row;

    row = _dtAircraftCrew.NewAircraftCrewRow();

    row.ID = Guid.NewGuid();

    row.FromRegion = 10;

    row.ToRegion = 10;

    _dtAircraftCrew.AddAircraftCrewRow(row);

    _dtAircraftCrew.AcceptChanges();

    wfhGrid.ItemsSource = new BindingListCollectionView(_dtAircraftCrew.DefaultView);

    wfhGrid.ReadOnly = false;

    wfhGrid.EditTriggers = EditTriggers.CellIsCurrent;

    wfhGrid.CellEditorDisplayConditions = CellEditorDisplayConditions.MouseOverCell;

    wfhGrid.ValidationMode = ValidationMode.CellEndingEdit;

    Column column = wfhGrid.Columns["FromRegion"];

    column.CellValidationRules.Add(new MinimumValueCellValidationRule(0));

    column = wfhGrid.Columns["ToRegion"];

    column.CellValidationRules.Add(new MinimumValueCellValidationRule(0));

    }

     

    public class MinimumValueCellValidationRule : CellValidationRule

    {

    private double m_min;

    public MinimumValueCellValidationRule(double min)

    {

    m_min = min;

    }

    // The minimum value of the cell content

    public override ValidationResult Validate(object value, CultureInfo culture, CellValidationContext context)

    {

    double number = Convert.ToDouble(value, CultureInfo.CurrentCulture);

    if (number < m_min)

    {

    string message = string.Format("Error");

    return new ValidationResult(false, message);

    }

    return ValidationResult.ValidResult;

    }

    }

     

    protected void btnEndEdit_Click(object sender, RoutedEventArgs args)

    {

    try

    {

    wfhGrid.EndEdit();

    }

    catch (DataGridException e1)

    {

    if (wfhGrid.HasValidationError)

    {

    MessageBox.Show("Exception after Validation called. CancelEdit() to be called and application is about to crash. Exception Reason - '" + e1.Message + "'");

    wfhGrid.CancelEdit();

    }

    return ;

    }

    catch (Exception e)

    {

    MessageBox.Show("Exception. Validation already called. Exception Reason - '" + e.Message + "'");

    return;

    }

     

    }

    }

    }

  •  08-14-2008, 11:27 AM Post no. 14070 in reply to 14010

    Re: Inline Editing Exception - An attempt was made to edit an item that is not part of the specified context

    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.
View as RSS news feed in XML
Contact | Site Map | Reviews | Legal Terms of Use | Trademarks | Privacy Statement Copyright 2008 Xceed Software Inc.