DataGrid bound to ArrayList problem

thomas10001

Regular
Joined
Jun 22, 2003
Messages
57
Location
Samoa & Sweden
I have a DataGrid that I have bound to an ArrayList.

If I create a new row and save right away everything is fine.

But if I select any column in the new row BEFORE saving the first row I get an error message
saying:

--------------------------------------------------------------------
Error when committing the row to the original data store.
Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index Do you want to correct the value?
[Yes] [No]

If I select [No] I get another errror message saying
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
at System.Collections.ArrayList.get_Item(Int32 index)

--------------------------------------------------------------------

After I started to catch the CurrentCellChanged event for the DataGrid
I found out that the CurrentRowIndex of DataGrid and Position of CurrencyManager is -1.

And if I try to set the value to 0, it gets back to -1 automatically. :(

If I would had another row in arraylist (ie if I saved right away, close my window and open it again) and then create a new row. Then I can click in another column without getting this error.

I have searched alot on internet and found that there are other having the same problem. But have not got a solution that works. Someone suggested to
reinitiate the datasource. Tried that with no success.

Is there anyone here who know how to fix this problem?


:confused:
 
I was able to get the (New) adding new rows to work by using the code below. (It has a own defined class DataMapping and DataList which takes care of the columngrid styles and the values for the arraylist)
After finding out that (from some other with similar problems) the datasource
must be set to a dummy temporarily to get it to work. It is a bit awkward to have to set it to a dummy value!

But I cannot get the delete command to work properly. If I delete the last row I get the arrayoutofbounds exception later if I click somewhere in the grid (not in the code below but somewhere undebuggable). But if there is only one single row (which is also last) I don't get the exception.

The key seems to be the position. Position gets to -1 sometimes and even if I explicity set it to 0 it doesn't change.

Anyone has a clue how to get the delete of the last row to work?

If I delete the last row of course Position must be set to one row less otherwise it points outside the array. But I cannot get the position to be updated correctly in this case.


private void updateSource(object dataSource)
{
System.Windows.Forms.DataGrid dg = ((UserControlWinGrid)this.getCurrentUserControl()).getDataGrid();
dg.DataMember = "";
dg.DataSource = dataSource;
dg.Refresh();
}

private void addArrayList(DataMapping dm)
{
DataList dl = new DataList();
int pos = dm.arrayList.Add(dl);

DataList dl2 = new DataList();
System.Collections.ArrayList al = new ArrayList();
al.Add(dl2);

cs = (CurrencyManager)this.BindingContext[dm.arrayList];
if(cs.Position < 0)
{
this.updateSource(null);
this.updateSource(al); // set dummy array
this.updateSource(this.getCurrentUserControl().getDataMapping().arrayList);
cs.Refresh();
cs.Position = pos;
}
cs.EndCurrentEdit();
cs.Position = pos; // moved to here 2005-01-22
cs.Refresh();

this.updateSource(null);
this.updateSource(al); // set dummy array

this.updateSource(this.getCurrentUserControl().getDataMapping().arrayList);
}

private void deleteArrayList(DataMapping dm,System.Data.DataSet ds,string showTable)
{
int removeAt = ((UserControlWinGrid)this.getCurrentUserControl()).getDataGrid().CurrentCell.RowNumber;

dm.arrayList.RemoveAt(removeAt);

//-------------------------------------------------------
// try this to get rid of exception
cs = (CurrencyManager)this.BindingContext[dm.arrayList];

int position = removeAt-1;
cs.Refresh();
if(position < 0)
position = 0;
//-------------------------------------------------------

this.BindingContext[ds,showTable].RemoveAt(removeAt);
this.BindingContext[ds,showTable].Position = position;

//-------------------------------------------------------
// more test code

// create a dummy array
DataList dl = new DataList();
System.Collections.ArrayList al = new ArrayList();
al.Add(dl);

this.updateSource(null);
this.updateSource(al); // set dummy array
this.updateSource(dm.arrayList); // set back original
}
 
Here is the exception I get. Caused by the "stubborn" Position pointing wrong

************** 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()
:o
 
thomas:
I have a less efficient way of tackling your requirements.....but it works. ;)

Code:
public void DeleteGridRow()
{
    int rowIDX = grid.CurrentCell.RowNumber;

    if(rowIDX >= 0)
    {
        ArrayList list = (ArrayList) grid.DataSource;
        grid.DataSource = null;
        list.RemoveAt(rowIDX);
        grid.DataSource = list;
    }
}

public void AddGridRow(object dataItem)
{
    ArrayList list = (ArrayList) grid.DataSource;
    grid.DataSource = null;
    list.Add(dataItem);
    grid.DataSource = list;
}
 
Last edited:
RobEmDee said:
thomas:
I have a less efficient way of tackling your requirements.....but it works. ;)


I tried the code for delete. But unfortenaly I still get the same exception
if I delete the last row (if there are more than one row). The exception happens after the delete has been done, if I click somewhere in the grid.
Also I see that the position indicator does not show if I delete the last position.
I am using C#. Can it be that there is a problem in C# with grids and arraylists?


************** 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()
 
thomas:
How are you triggering the row deletion? e.g. Button on the Form, Select row header and clicking Delete key on keyboard, from a ContextMenu.....
 
RobEmDee said:
thomas:
How are you triggering the row deletion? e.g. Button on the Form, Select row header and clicking Delete key on keyboard, from a ContextMenu.....

Hi

I have a button on my form which calls my delete method.

By the way when my grid is empty the headers don't show. Even though I have initialized the variables in the class that are used in the list. I read somewhere that this was required. Maybe this has something to do with this problem?!
 
I do both

dm.arrayList.RemoveAt(removeAt);

and

this.BindingContext[ds,showTable].RemoveAt(removeAt);

Is it correct that I would have to delete from the dataset as well or should it be handled automatically by the arrayList since its connected to the dataset.

However before I do my Update of the dataset I have to copy the values over from my arrayList into the dataset. Is this really the correct way?

Thomas
 
RobEmDee said:
thomas:
How are you triggering the row deletion? e.g. Button on the Form, Select row header and clicking Delete key on keyboard, from a ContextMenu.....

I have a special class (DataMapping and DataList) for GridColumnStyles I use which is general so I don't have to have a hardcoded class for the items in the grid. I don't know if the problem could be in that code. But it doesn't feel like it is there. I could had posted it here unless it wasn't that much code.
 
I post a bit of the code here...

I post part of the DataList and DataMapping I am using in my example in case the problem lies here.

I have several int's double strings etc in my code to make it general. But here I have removed them to make the code shorter.

public class DataList
{
private string myString1 = "";

private int myInt1 = 0;

private bool isNullInt1 = true;

public DataList()
{
}

public string string1
{
get {return myString1;}
set {myString1 = value;}
}


public int int1
{
get {return myInt1;}
set {myInt1 = value;isNullInt1 = false;}
}

public bool isNullint1
{
get {return isNullInt1;}
}
}
}

//-----------------------------------------------------------

public class DataMapping
{
private StringCollection myMappingName;
private StringCollection myMappingTable;
private StringCollection myMappingColumn;
private StringCollection myInternalName;
private StringCollection myRelationName;
private StringCollection myType;
private ArrayList myIndex; private ArrayList dataLists;

private int intUsed = 0;
private int stringUsed = 0;

private int myCount = 0;

public ArrayList arrayList
{
get
{
return dataLists;
}
}

public void clearDataList()
{
dataLists.Clear();
}

public DataMapping()
{
this.dataLists = new ArrayList();
this.myMappingName = new StringCollection();
this.myMappingTable = new StringCollection();
this.myMappingColumn = new StringCollection();
this.myInternalName = new StringCollection();
this.myRelationName = new StringCollection();
this.myType = new StringCollection();
this.myIndex = new ArrayList();
}

public int getNRelations()
{
return this.myRelationName.Count;
}

public string getRelationName(int item)
{
return this.myRelationName[item];
}

public string getTableName(int item)
{
return this.myMappingTable[item];
}

public void Add(string mappingName,string mappingTable,string mappingColumn,string type,string relationName)
{
addMapping(mappingName,mappingTable,mappingColumn,type);
this.myRelationName.Add(relationName);
}

// mappingName a unique name for this mapping
// mappingTable table to find the value
// mappingColumn column in mappingTable to find value
// type int or string (so far)
public void Add(string mappingName,string mappingTable,string mappingColumn,string type)
{
addMapping(mappingName,mappingTable,mappingColumn,type);
this.myRelationName.Add("");
}

private void addMapping(string mappingName,string mappingTable,string mappingColumn,string type)
{
this.myCount++;
this.myMappingName.Add(mappingName);
this.myMappingTable.Add(mappingTable);
this.myMappingColumn.Add(mappingColumn);
this.myType.Add(type);

// ie: we have "round" as the first mappingname and it is an int
string internalName = "";
int index = 0;

switch(type)
{
case "int":
intUsed++;
index = intUsed;
internalName = "int"+intUsed;
break;
case "string":
stringUsed++;
index = stringUsed;
internalName = "string"+stringUsed;
break;
}

this.myInternalName.Add(internalName);
this.myIndex.Add(index);
}

public string getInternalName(string mappingName)
{ // returns the internal name corresponding the mappingName
int item = this.myMappingName.IndexOf(mappingName);

return this.myInternalName[item];
}

public string getMappingNameFromColumnName(string columnName)
{
int item = this.myMappingColumn.IndexOf(columnName);

return this.myMappingName[item];
}

public string getColumnName(int item)
{
return this.myMappingColumn[item];
}

public string getInternal(int item)
{
return this.myInternalName[item];
}

public string getMapping(int item)
{
return this.myMappingName[item];
}

public string getType(int item)
{
return this.myType[item];
}

public int getIndex(string mappingName)
{
int item = this.myMappingName.IndexOf(mappingName);
int index = 0;

index = (int)this.myIndex[item];

return index;
}

public void AddDataList()
{
dataLists.Add(new DataList());
}


public void setValue(int row,string mappingName,int intValue)
{
switch(getIndex(mappingName))
{
case 1:
((DataList)dataLists[row]).int1 = intValue;
break;
}
}


public void setValue(int row,string mappingName,string stringValue)
{
switch(getIndex(mappingName))
{
case 1:
((DataList)dataLists[row]).string1 = stringValue;
break;
}
}

public int getIntValue(int row,string mappingName)
{
switch(getIndex(mappingName))
{
case 1:
return ((DataList)dataLists[row]).int1;
}

return 0;
}

public bool getIsNullInt(int row,string mappingName)
{
switch(getIndex(mappingName))
{
case 1:
return ((DataList)dataLists[row]).isNullint1;
}

return true;
}

public string getStringValue(int row,string mappingName)
{
switch(getIndex(mappingName))
{
case 1:
return ((DataList)dataLists[row]).string1;
}

return "";
}

public int Count
{
get {return myCount;}
}
}
}

//-----------------------------------------------------------

Code for columnstyles

public void addDataMapping(string mappingName,string mappingTable,string mappingColumn,string type)
{
dataManager.addDataMapping(mappingName,mappingTable,mappingColumn,type);
}

public void addDataMapping(string mappingName,string mappingTable,string mappingColumn,string type,string relationName)
{
dataManager.addDataMapping(mappingName,mappingTable,mappingColumn,type,relationName);
}

public string getDataInternalName(string mappingName)
{
return dataManager.getDataInternalName(mappingName);
}

public void addDataGridTableStyle(DataGrid dataGrid)
{
gridTableStyle = new DataGridTableStyle();
gridTableStyle.DataGrid = dataGrid;
gridTableStyle.HeaderForeColor = System.Drawing.SystemColors.ControlText;

// if using dataMappings we have an ArrayList inside the DataMapping object
// and mappingName must be "ArrayList"
// otherwise mappingName is the name of tables[0]
// since we then should only have one table
if(this.getDataMapping().Count > 0)
gridTableStyle.MappingName = "ArrayList";
else
gridTableStyle.MappingName = getDataSet().Tables[0].TableName;

dataGrid.TableStyles.Add(gridTableStyle);
}

public void addGridColumnStyle(string header,string mappingName,int width,bool readOnly,string columnType,string formatString)
{
DataGridTextBoxColumn column = new DataGridTextBoxColumn();

column.HeaderText = header;
// check if we have dataMappings
if(this.getDataMapping().Count > 0)
column.MappingName = getDataInternalName(mappingName); // the the internal mapping name
else
column.MappingName = mappingName; // mapping name is the name itself

column.Width = width;
column.ReadOnly = readOnly;
column.TextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.KeyDown);
column.TextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.KeyUp);
if(!formatString.Equals(""))
column.Format = formatString;
gridTableStyle.GridColumnStyles.Add(column);
}


//-----------------------------------------------------------


Code to setup my datamapping and columnstyles for my datagrid

addDataMapping("bedid","bed","bedid","int");
addDataMapping("roomid","bed","roomid","int");
addDataMapping("bedname","bed","bedname","string");

...

addDataGridTableStyle(this.grid);
addGridColumnStyle("BedId","bedid",0,false,"TextBox","");
addGridColumnStyle("RoomId","roomid",0,false,"TextBox","");
addGridColumnStyle("Bed Name","bedname",80,false,"TextBox","");
 
thomas:
What are the requirements in place that call for the use of an ArrayList and a DataSet? In my opinion, if the data needs to end up in a DataSet anyways, you should just bypass the complications of the ArrayList and use your DataSet for everything.
 
Hi

I have made a framework where I want to be able to use other controls than a textbox in the grid. ie comboboxes etc.

I thought this is the only? way to be able to use other controls.

I also do not want the empty insertion row (marked *) which I don't know how to suppress.

Thomas
 
Hi

Another reason I just rediscovered is that I have problem to set values (primary keys I have generated if I have datagridtablestyle attached)

If I just have a plain dataset, no datagridtablestyle , I do this to set the values in the new row.

DataSet ds1 = getDataSet();
BindingManagerBase myMgr = (CurrencyManager) BindingContext[ds1,tableName];

if(myMgr.Count > 0)
{
// This is the way to set a value in a row which has not been saved yet
// and merged into the normal dataset
DataRowView tempRowView = (DataRowView) myMgr.Current;

if(tempRowView.IsNew)
{
tempRowView[columnName] = columnValue;
System.Console.WriteLine(tableName+"."+columnName+" = "+columnValue);
}
}


But when I have datagridtablestyle the above code doesn't work.
I don't know how to get a DataRowView or similar if I have datagridtablestyle .

If I knew how to do this I would give up the arraylist for now.

Thomas
 
thomas:
You can control how the underlying DataTable can be used in the DataGrid through manipulating its DefaultView

Code:
// Data in DataGrid will automatically be Read-Only
dataSet1.Tables["Table1"].DefaultView.AllowEdit = false;

// Rows in DataGrid cannot be added (no '*' Row)
dataSet1.Tables["Table1"].DefaultView.AllowNew = false;

// Rows in DataGrid cannot be deleted
dataSet1.Tables["Table1"].DefaultView.AllowDelete = false;

You make want to check out ComponentOne grids. They come with all of this functionality and a lot more out of the box. I started using them last year and have never looked back since.

thomas10001 said:
Hi

I have made a framework where I want to be able to use other controls than a textbox in the grid. ie comboboxes etc.

I thought this is the only? way to be able to use other controls.

I also do not want the empty insertion row (marked *) which I don't know how to suppress.

Thomas
 
Hi

I were able to get set the keys now and having gridstyles. And my delete and add works fine. Thanks.

But I am not able to supress the * row with allowNew = false.

Is there something that should be set on the datagrid as well to hide/supress it?


Thomas
 
Last edited:
Back
Top