DataGrid: IndexOutOfRange Exception

M

Marcus Kwok

This is a repost of a question I posted to the codeproject.com forums,
but got no responses:
http://www.codeproject.com/script/comments/forums.asp?msg=1872655&forumid=1650#xx1872655xx

I forgot to mention in the above post that I am using Managed C++
(Visual Studio .NET 2003 SP1) / .NET Framework 1.1.


In my application, I have a DataGrid that is used to display search
results from a query into a Firebird SQL database. The DataGrid is not
directly connected to the DB, but instead I perform my query and store
the results in a std::vector, perform some more work on it, then copy
the final results into an ArrayList that the DataGrid is connected to.

So, when I do a search, it goes like this:


/* Obtain final std::vector with query results */

/* search_results_list is an ArrayList */
search_results_list->Clear();

// copy data into ArrayList
tr = db_conn->BeginTransaction();
typedef vector::const_iterator CIter;
for (CIter it = run_ids.begin(); it != run_ids.end(); ++it) {
/* look up Run ID in DB and store the run in disp */
search_results_list->Add(disp);
}
tr->Commit();

/* reset DataSource to refresh the display */
datagrid_search_results->DataSource = 0;
datagrid_search_results->DataSource = search_results_list;


This works fine the first time, however I am having the following
problem. If I click on a row, then subsequently perform another search
that makes the row disappear, I get an IndexOutOfRange exception:


************** Exception Text **************
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Windows.Forms.DataGrid.Edit(String instantText)
at System.Windows.Forms.DataGrid.Edit()
at System.Windows.Forms.DataGrid.OnEnter(EventArgs e)
at System.Windows.Forms.Control.NotifyEnter()
at System.Windows.Forms.ContainerControl.UpdateFocusedControl()


However, my DataGrid has set the ReadOnly property to true. In my other
DataGrid that is used for a different purpose, I was able to solve it by
setting the CurrentSelectedRow property to 0 before removing the
elements, but this does not work in this situation.

I am not sure if it is related to this, but the only event I am handling
for this DataGrid is the MouseUp event, in order to get it to select the
entire row when a cell is clicked:

// Select the entire row when the user clicks on a cell in the row
System::Void datagrid_MouseUp(System::Object* sender, System::Windows::Forms::MouseEventArgs* e)
{
using System::Drawing::point;
using System::Windows::Forms::DataGrid;
using System::Windows::Forms::DataGridCell;

Point pt = Point(e->X, e->Y);
DataGrid* datagrid = __try_cast(sender);
DataGrid::HitTestInfo* hti = datagrid->HitTest(pt);
if (hti->Type == DataGrid::HitTestType::Cell) {
datagrid->CurrentCell = DataGridCell(hti->Row, hti->Column);
datagrid->Select(hti->Row);
}
}
 
M

Mike

This is a repost of a question I posted to the codeproject.com forums,
but got no responses:http://www.codeproject.com/script/comments/forums.asp?msg=1872655&for...

I forgot to mention in the above post that I am using Managed C++
(Visual Studio .NET 2003 SP1) / .NET Framework 1.1.

In my application, I have a DataGrid that is used to display search
results from a query into a Firebird SQL database. The DataGrid is not
directly connected to the DB, but instead I perform my query and store
the results in a std::vector, perform some more work on it, then copy
the final results into an ArrayList that the DataGrid is connected to.

So, when I do a search, it goes like this:

/* Obtain final std::vector with query results */

/* search_results_list is an ArrayList */
search_results_list->Clear();

// copy data into ArrayList
tr = db_conn->BeginTransaction();
typedef vector::const_iterator CIter;
for (CIter it = run_ids.begin(); it != run_ids.end(); ++it) {
/* look up Run ID in DB and store the run in disp */
search_results_list->Add(disp);
}
tr->Commit();

/* reset DataSource to refresh the display */
datagrid_search_results->DataSource = 0;
datagrid_search_results->DataSource = search_results_list;

This works fine the first time, however I am having the following
problem. If I click on a row, then subsequently perform another search
that makes the row disappear, I get an IndexOutOfRange exception:

************** Exception Text **************
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Windows.Forms.DataGrid.Edit(String instantText)
at System.Windows.Forms.DataGrid.Edit()
at System.Windows.Forms.DataGrid.OnEnter(EventArgs e)
at System.Windows.Forms.Control.NotifyEnter()
at System.Windows.Forms.ContainerControl.UpdateFocusedControl()

However, my DataGrid has set the ReadOnly property to true. In my other
DataGrid that is used for a different purpose, I was able to solve it by
setting the CurrentSelectedRow property to 0 before removing the
elements, but this does not work in this situation.

I am not sure if it is related to this, but the only event I am handling
for this DataGrid is the MouseUp event, in order to get it to select the
entire row when a cell is clicked:

// Select the entire row when the user clicks on a cell in the row
System::Void datagrid_MouseUp(System::Object* sender, System::Windows::Forms::MouseEventArgs* e)
{
using System::Drawing::point;
using System::Windows::Forms::DataGrid;
using System::Windows::Forms::DataGridCell;

Point pt = Point(e->X, e->Y);
DataGrid* datagrid = __try_cast(sender);
DataGrid::HitTestInfo* hti = datagrid->HitTest(pt);
if (hti->Type == DataGrid::HitTestType::Cell) {
datagrid->CurrentCell = DataGridCell(hti->Row, hti->Column);
datagrid->Select(hti->Row);
}
}

Hi Marcus,

I recently went through the same thing with the datagrid using C#, I
was trying to bind a collection object to my datagrid and whenever I
removed an item from my collection I got an IndexOutOfRangeException.
I was only able to find 1 article that helped me with this problem,
perhaps it may help you as well, I know the discussion centers around
VB.NET but there is some code from this thread done in C# that may
give you an idea on how to solve your problem.

Mike

http://groups.google.ca/group/micro...tText)+++Object&rnum=1&hl=en#6c65a2f4672ffa6c

public class Customer : IEditableObject
{


struct CustomerData
{
internal string id ;
internal string firstName ;
internal string lastName ;
}


private CustomersList parent;
private CustomerData custData;
private CustomerData backupData;
private bool inTxn = false;


// Implements IEditableObject
void IEditableObject.BeginEdit()
{
Console.WriteLine("Start BeginEdit");
if (!inTxn)
{
this.backupData = custData;
inTxn = true;
Console.WriteLine("BeginEdit - " +
this.backupData.lastName);
}
Console.WriteLine("End BeginEdit");
}


void IEditableObject.CancelEdit()
{
Console.WriteLine("Start CancelEdit");
if (inTxn)
{
this.custData = backupData;
inTxn = false;
Console.WriteLine("CancelEdit - " +
this.custData.lastName);
}
Console.WriteLine("End CancelEdit");
}


void IEditableObject.EndEdit()
{
Console.WriteLine("Start EndEdit" + this.custData.id +
this.custData.lastName);
if (inTxn)
{
backupData = new CustomerData();
inTxn = false;
Console.WriteLine("Done EndEdit - " + this.custData.id +
this.custData.lastName);
}
Console.WriteLine("End EndEdit");
}


public Customer(string ID) : base()
{
this.custData = new CustomerData();
this.custData.id = ID;
this.custData.firstName = "";
this.custData.lastName = "";
}


public string ID
{
get
{
return this.custData.id;
}
}


public string FirstName
{
get
{
return this.custData.firstName;
}
set
{
this.custData.firstName = value;
}
}


public string LastName
{
get
{
return this.custData.lastName;
}
set
{
this.custData.lastName = value;
}
}


internal CustomersList Parent
{
get
{
return parent;
}
set
{
parent = value ;
}
}


private void OnCustomerChanged()
{
if (!inTxn && Parent != null)
{
Parent.CustomerChanged(this);
}
}



}


public class CustomersList : CollectionBase, IBindingList
{
private ListChangedEventArgs resetEvent = new
ListChangedEventArgs(ListChangedType.Reset, -1);
private ListChangedEventHandler onListChanged;

public void LoadCustomers()
{
IList l = (IList)this;
l.Add(ReadCustomer1());
l.Add(ReadCustomer2());
OnListChanged(resetEvent);
}


public Customer this[int index]
{
get
{
return (Customer)(List[index]);
}
set
{
List[index] = value;
}
}


public int Add (Customer value)
{
return List.Add(value);
}


public Customer AddNew()
{
return (Customer)((IBindingList)this).AddNew();
}


public void Remove (Customer value)
{
List.Remove(value);
}


protected virtual void OnListChanged(ListChangedEventArgs ev)
{
if (onListChanged != null)
{
onListChanged(this, ev);
}
}


protected override void OnClear()
{
foreach (Customer c in List)
{
c.Parent = null;
}
}


protected override void OnClearComplete()
{
OnListChanged(resetEvent);
}


protected override void OnInsertComplete(int index, object value)
{
Customer c = (Customer)value;
c.Parent = this;
OnListChanged(new
ListChangedEventArgs(ListChangedType.ItemAdded,
index));
}


protected override void OnRemoveComplete(int index, object value)
{
Customer c = (Customer)value;
c.Parent = this;
OnListChanged(new
ListChangedEventArgs(ListChangedType.ItemDeleted,
index));
}


protected override void OnSetComplete(int index, object oldValue,
object newValue)
{
if (oldValue != newValue)
{


Customer oldcust = (Customer)oldValue;
Customer newcust = (Customer)newValue;


oldcust.Parent = null;
newcust.Parent = this;


OnListChanged(new
ListChangedEventArgs(ListChangedType.ItemAdded, index));
}
}


// Called by Customer when it changes.
internal void CustomerChanged(Customer cust)
{


int index = List.IndexOf(cust);


OnListChanged(new
ListChangedEventArgs(ListChangedType.ItemChanged,
index));
}


// Implements IBindingList.
bool IBindingList.AllowEdit
{
get { return true ; }
}


bool IBindingList.AllowNew
{
get { return true ; }
}


bool IBindingList.AllowRemove
{
get { return true ; }
}


bool IBindingList.SupportsChangeNotification
{
get { return true ; }
}


bool IBindingList.SupportsSearching
{
get { return false ; }
}


bool IBindingList.SupportsSorting
{
get { return false ; }
}


// Events.
public event ListChangedEventHandler ListChanged
{
add
{
onListChanged += value;
}
remove
{
onListChanged -= value;
}
}


// Methods.
object IBindingList.AddNew()
{
Customer c = new Customer(this.Count.ToString());
List.Add(c);
return c;
}


// Unsupported properties.
bool IBindingList.IsSorted
{
get { throw new NotSupportedException(); }
}


ListSortDirection IBindingList.SortDirection
{
get { throw new NotSupportedException(); }
}


PropertyDescriptor IBindingList.SortProperty
{
get { throw new NotSupportedException(); }
}


// Unsupported Methods.
void IBindingList.AddIndex(PropertyDescriptor property)
{
throw new NotSupportedException();
}


void IBindingList.ApplySort(PropertyDescriptor property,
ListSortDirection direction)
{
throw new NotSupportedException();
}


int IBindingList.Find(PropertyDescriptor property, object key)
{
throw new NotSupportedException();
}


void IBindingList.RemoveIndex(PropertyDescriptor property)
{
throw new NotSupportedException();
}


void IBindingList.RemoveSort()
{
throw new NotSupportedException();
}


// Worker functions to populate the list with data.
private static Customer ReadCustomer1()
{
Customer cust = new Customer("536-45-1245");
cust.FirstName = "Jo";
cust.LastName = "Brown";
return cust;
}


private static Customer ReadCustomer2()
{
Customer cust = new Customer("246-12-5645");
cust.FirstName = "Robert";
cust.LastName = "Brown";
return cust;
}


}
 
M

Marcus Kwok

Mike said:
I recently went through the same thing with the datagrid using C#, I
was trying to bind a collection object to my datagrid and whenever I
removed an item from my collection I got an IndexOutOfRangeException.
I was only able to find 1 article that helped me with this problem,
perhaps it may help you as well, I know the discussion centers around
VB.NET but there is some code from this thread done in C# that may
give you an idea on how to solve your problem.

Mike

http://groups.google.ca/group/micro...tText)+++Object&rnum=1&hl=en#6c65a2f4672ffa6c

Hi Mike,
Thanks for your response. In the meantime I have been put onto a
different task, but I will eventually have to return to this issue. I
have saved your post and will definitely examine it in more detail.
 
M

Marcus Kwok

Well, I managed to find the solution to my problem. After resetting the
DataSource, I needed to refresh the currency manager by adding the
following two lines:
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top