I have a filtered bindingsource:
tblChemicalsBindingSource.Filter = "OilProcess = 'true'";
Later on I use:
int autoid = Convert.ToInt32(ChemicaldataGridView.Rows[e.RowIndex].Cells["chemicalNameData"].Value); to get the autoid from a combobox attached to a datagridview.
Because it's filtered, this returns the wrong position:
tblChemicalsBindingSource.Position = tblChemicalsBindingSource.Find("AutoID", autoid);
Which in turn makes this datarow point to the wrong row:
DataRow dr = aocSQLDataSet.tblChemicals.Rows[tblChemicalsBindingSource.Position];
What is the prefered way to get the correct position/row when filtered?
BTW, I'm using FindByAutoID but I have questions about it.
DataRow dr = aocSQLDataSet.tblChemicals.FindByAutoID(autoid);
See my post at: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2783053&SiteID=1
Thanks,
Dennis
| | NDennisV Saturday, February 02, 2008 1:05 PM | Oh, I see the problem. There's no reason to think that the nthitem in a BindingSource's List is also going to be the nth item in a DataTable's Rows collection. This is a feature, not a bug: it lets objects that consume bound data, like the DataGridView, process the items with a loop like this:
Code Snippet
for (int i=0; i<myBindingSource.List.Count; i++)
{
// do something with myBindingSource.List[i];
}
But there's no need to despair. There are plenty of ways to get the DataRow you want.
A BindingSource's List (if you've bound it to a DataTable) doesn't contain DataRow objects. It contains DataRowView objects. The DataRowView is a simplification of DataRow that hides some of DataRow's complexities (specifically, row versions) from the BindingSource. I don't think it's possible to build robust data-bound applications without knowing about the interactions between DataRow, DataRowView, BeginEdit, and CancelEdit, but for the moment let's pretend you don't care about that.
If you want to get the DataRow which is the nth item in a BindingSource's List, you have to cast the item (which BindingSource thinks is of type System.Object) as a DataRowView and get its Row property. The syntax is:
Code Snippet
DataRow myRow = (DataRowView)myBindingSource.List[n].Row;
Kind of a pain, I know.
But wait. In your example, you already know the value ofwhat I'm assuming is the primary key of the row - the column named "AutoId". If your DataTable is strongly typed, and AutoId is in fact its primary key, then the typed DataTable has a method called FindByAutoId() that returns a DataRow. Just use that.
If it's not strongly-typed, then you don't have that method, but you can still find the row by its AutoId:
Code Snippet
DataRow myRow = myTable.Select("AutoId = " + autoid.ToString())[0];
And if, for some reason, you actually need to know the index of this DataRow in the DataTable's Rows collection, you can get it via the DataRowsCollection.IndexOf method. Though I can't think of a good reason why you would. | | Robert Rossney Sunday, February 03, 2008 11:58 AM | I believe you're mistaken. This code doesn't produce any assertion failures:
Code Snippet
DataTable t = new DataTable();
t.Columns.Add("ID", typeof(System.Int32));
// create 100 rows, with IDs of 0 through 99
for (int i = 0; i < 100; i++)
{
DataRow r = t.NewRow();
r[ "ID"] = i;
t.Rows.Add(r);
}
BindingSource b = new BindingSource();
b.DataSource = t;
b.Filter = "ID > 50";
b.Position = 0;
DataRowView v = (DataRowView)b.Current;
Debug.Assert((int)v.Row["ID"] == 51, "The row at position 0 should have an ID of 51.");
int position = b.Find("ID", 51);
Debug.Assert(position == 0, "The row with an ID of 51 should be at position 0.");
In short, when I apply a filter to the binding source and use Find to find an item, the position that Find returns is the position in the filtered list, not the unfiltered list. There's something going on in your code that you're not expecting - perhaps the filter isn't active (or a different filter is) at the time you're calling Find. | | Robert Rossney Saturday, February 02, 2008 9:30 PM | That's the problem. It's returning the filtered row and not the unfiltered row.
I'll try and explain it:
Lets say we have a database with 4 weeks of data arranged by the day of the week; Monday, Tuesday, Wednesday, etc. x 4 weeks.
Lets say I filter it on Monday. Since there are only 4 Mondays in the 4 weeks, the filtered list will be 4 records long.
If I want the last Monday a filtered "Find" will return the 4th position, but it's really the 22nd (or whatever) record in the database.
From my 1st post:
Because it's filtered, this returns the wrong position:
tblChemicalsBindingSource.Position = tblChemicalsBindingSource.Find("AutoID", autoid);
Which in turn makes this datarow point to the wrong row:
DataRow dr = aocSQLDataSet.tblChemicals.Rows[tblChemicalsBindingSource.Position];
It's the DataRow that's now getting the wrong position.
Sorry, I'm new to this enviroment. Is there a way to get the correct DataRow using the filtered BindingSource?
Thanks,
Dennis
| | NDennisV Saturday, February 02, 2008 10:47 PM | Oh, I see the problem. There's no reason to think that the nthitem in a BindingSource's List is also going to be the nth item in a DataTable's Rows collection. This is a feature, not a bug: it lets objects that consume bound data, like the DataGridView, process the items with a loop like this:
Code Snippet
for (int i=0; i<myBindingSource.List.Count; i++)
{
// do something with myBindingSource.List[i];
}
But there's no need to despair. There are plenty of ways to get the DataRow you want.
A BindingSource's List (if you've bound it to a DataTable) doesn't contain DataRow objects. It contains DataRowView objects. The DataRowView is a simplification of DataRow that hides some of DataRow's complexities (specifically, row versions) from the BindingSource. I don't think it's possible to build robust data-bound applications without knowing about the interactions between DataRow, DataRowView, BeginEdit, and CancelEdit, but for the moment let's pretend you don't care about that.
If you want to get the DataRow which is the nth item in a BindingSource's List, you have to cast the item (which BindingSource thinks is of type System.Object) as a DataRowView and get its Row property. The syntax is:
Code Snippet
DataRow myRow = (DataRowView)myBindingSource.List[n].Row;
Kind of a pain, I know.
But wait. In your example, you already know the value ofwhat I'm assuming is the primary key of the row - the column named "AutoId". If your DataTable is strongly typed, and AutoId is in fact its primary key, then the typed DataTable has a method called FindByAutoId() that returns a DataRow. Just use that.
If it's not strongly-typed, then you don't have that method, but you can still find the row by its AutoId:
Code Snippet
DataRow myRow = myTable.Select("AutoId = " + autoid.ToString())[0];
And if, for some reason, you actually need to know the index of this DataRow in the DataTable's Rows collection, you can get it via the DataRowsCollection.IndexOf method. Though I can't think of a good reason why you would. | | Robert Rossney Sunday, February 03, 2008 11:58 AM | Thank you very much Robert.
Another question please: Why would strongly-typed be a problem when using Select?
Dennis
| | NDennisV Sunday, February 03, 2008 12:50 PM | The problem (if it's a problem) is that a strongly-typed DataTable is a class derived from System.DataTable. The DataTable.Select method returns an array of DataRow objects. It doesn't return an array of typed DataRow objects.
This means that when you're working with strongly-typed DataTable and DataRow classes, you still occasionally have to deal with type-casting issues. For instance, it would be nice to write code like this:
Code Snippet
void ProcessRows(MyTypedTableDataTable t, string filter)
{
MyTypedTableDataRow[] rows = t.Select(filter);
// do something useful here
}
But that's a compile error, because the Select method returns an object of type DataRow[], not MyTypedTableDataRow[]. So you have to explicitly cast the result like this:
Code Snippet
MyTypedTableDataRow[] rows = (MyTypedTableDataRow[])t.Select(filter);
Fortunately, you can do implicit casting via enumeration. This will compile:
Code Snippet
foreach (MyTypedTableDataRow r in t.Select(filter))
{
// do something useful here
}
even if t is of type DataTable and not MyTypedTableDataTable. It'll throw an exception if any of the items in the array can't be cast as MyTypedTableDataRow, but you have to be pretty seriously bent on screwing yourself up to write code that would do that. | | Robert Rossney Sunday, February 03, 2008 1:08 PM | Thanks again Robert, I'm learning.
| | NDennisV Sunday, February 03, 2008 1:27 PM | As are we all. It was not long ago that the above was a complete mystery to me. But as Yogi Berra said, you can observe a lot just by looking. Reading the various bits of code that VS generates is a really helpful exercise: not only do you often get the answer to your questions, you also get to see real production codewhich makes solid use of techniques that may still be unfamiliar. (Well, they were in my case.)
The code samples in the documentation are designed to show you how the framework classes work. They often do a poor job of showing you how to write robust code that uses them. The generated code has its idiosyncracies, but it's code that's designed for hundreds of thousands of developers to compile and run, often without even knowing it's there. It's worth looking at how code like that is written. | | Robert Rossney Sunday, February 03, 2008 11:29 PM |
|