Library tutorials & articles
Winforms Data Binding Lessons Learned
- Displaying Many-to-Many Relationships
- Reacting to Illegal Row Editing in a DataGrid
- Reacting to Illegal Row Deletion in a DataGrid
- How to Make a Non-DataGrid Control Act as the Mast
How to Make a Non-DataGrid Control Act as the Mast
This problem would occur whenever you want a control other than a DataGrid to serve as a master in a master-details, data-bound form. Imagine that the control that you have is a tree view. You fill up the treeNodes and set the node.Tag property with a DataRow object. You want the user to be able to click on a tree node and let a DataGrid on the form show the child rows of the selected data row.
If we have a ListBox or ComboBox control, this could be easy. You just set the BindingManager's position property to the current index of the control. But what about a tree view? The solution is pretty simple using a DataView object.
A DataView object has the ability to find a row based on a key and a value, meaning that if the DataRow is sorted according to the primary key of the table, you can use it to look up a row based on a given primary key. It contains a Find method, which returns the index to the row that was found. So, all you have to do is create a new DataView object, make it sort on your PK, and use it to find an index. Then you set the Binding manager's position according to the returned index. You need to bind your controls to that particular DataView, so you get the correct position. One word of caution: a DataTable object has by default a DefaultDataView property that you can use.
It is recommended to use a new DataView object, which gives you more flexibilty, and you can create 2 or even 10 different views of your data. You can sort them, filter them, or just show the rows that were last changed, added, deleted, or otherwise adapted. It's all there using the DataView.
Here's the code to handle the user selecting a tree node:
'Create a custom view of the data
Private view As New DataView(m_ds.Tables("Stuff"))
'make it sort based on the PK
view.Sort = "ID"
'Handling a listbox event
Private Sub listBox1_SelectedIndexChanged(sender As Object, e As System.EventArgs)
'get the binding manager to set the position
Dim bind As BindingManagerBase = BindingContext(m_ds, "Stuff")
'Using the 'Find' of the DataView
'returns the needed row index!
'Just make sure you bind the ValueMember
'property of the listbox
bind.Position = view.Find(listBox1.SelectedValue)
End Sub 'listBox1_SelectedIndexChanged
'Handling a TreeView event
Private Sub treeView1_AfterSelect(sender As Object, e As System.Windows.Forms.TreeViewEventArgs)
'get the binding manager to set the position
Dim bind As BindingManagerBase = BindingContext(m_ds, "Stuff")
Dim row As DataRow = CType(treeView1.SelectedItem.Tag, DataRow)
Dim wantedID As Integer = Integer.Parse(row("ID").ToString())
bind.Position = view.Find(wantedID)
End Sub 'treeView1_AfterSelect
Related articles
Related discussion
-
How to write a query set to excel using vb.net
by BarbaMariolino (1 replies)
-
Very Urgent regarding deleting the images from a folder
by rameshbandi (2 replies)
-
Block Accessing MSSQL 2000
by militia (0 replies)
-
.NET Developer in Ghana Required....
by sysview (0 replies)
-
Sending SMS to mobile using secure gateway from VB.net 2008 c#
by pratikasthana17 (0 replies)
Related podcasts
-
xpert to Expert: Inside Concurrent Basic (CB)
"Concurrent Basic extends Visual Basic with stylish asynchronous concurrency constructs derived from the join calculus. Our design advances earlier MSRC work on Polyphonic C#, Comega and the Joins Library. Unlike its C# based predecessors, CB adopts a simple event-like syntax familiar to VB progr...
The solution is to bind the entire Child table to a bindingSource and that to a dataGridView. In this case the child table would be the courses that the soldier registered for.
Then, whenever a soldier is selected, store the selected row in a global variable, such as lastSelectedSoldierRow. Then, when that changes, you do a select on the table that connects the two. The registers table in this case. For example registerDataTable.Select("soldier_ID = " + lastSelectedSoldierRow.ID.ToString());
Take all those rows and add the course Ids to a globally declared stringBuilder. Whenever a different soldier is selected, add -1 to the stringBuilder as an initializer. Then, for each additional course ID added to the stringBuilder, add a comma to the stringBuilder before it. You'll end up with something like "1,2,3,4,5,6". Next, set the child table's bindingSource.Filter to a that string like so: Filter = "ID in (1,2,3,4,5)".
That will filter all the courses, down to the ones which belong to the soldier. Whenever a new course is added, append the ID to the stringBuilder and reset the filter.
Here's an example scrap of source code from my own project. It's messy, but may be helpful to somebody if the above wasn't enough.
StringBuilder settingsFilterSB = new StringBuilder();
private void dataGridViewComputers_SelectionChanged(object sender, EventArgs e)
{
if (this.dataGridViewComputers.SelectedRows.Count == 1 || this.dataGridViewComputers.SelectedCells.Count > 0)
{
object dataBoundItem;
if (this.dataGridViewComputers.SelectedRows.Count == 1)
dataBoundItem = this.dataGridViewComputers.SelectedRows[0].DataBoundItem;
else
dataBoundItem = this.dataGridViewComputers.Rows[this.dataGridViewComputers.SelectedCells[0].RowIndex].DataBoundItem;
lastSelectedPcRow = (CallistoDataSet.ComputerOrGroupRow)((DataRowView)dataBoundItem).Row;
settingsFilterSB = new StringBuilder();
settingsFilterSB.Append("-1");
int selectedPcId = lastSelectedPcRow.ID;
foreach(CallistoDataSet.ComputerOrGroup2SettingsRow row in this.callistoDataSet.ComputerOrGroup2Settings.Rows)
{
if(row.ComputerOrGroup_ID == selectedPcId)
settingsFilterSB.Append("," + row.ComputerSettings_ID.ToString());
}
this.computerSettingsBindingSource.Filter = "ID in (" + settingsFilterSB.ToString() + ")";
Use a BindingSource control then use its Find method to get the index of the row.
I'm getting this error message: "Object reference not set to an instance of an object." when I try to run the code below. I've spent a couple days trying to figure out what it is, but I'm kind of new to vb.net and programming. I have two tables that look something like below (not all fields are shown). They should be related via the userID fields as a "many tasks to each user" relation. Any help on how to do this or why there is an error when I run the code would be greatly appreciated.
Table: Users Table:Tasks
userID userID
userName taskID
password taskName
Dim
connection As New OleDbConnection("Provider=Microsoft.JET.OLEDB.4.0; data source=" & Application.StartupPath & "\blueteam2.mdb") Dim adpUsers As New OleDbDataAdapter("SELECT * FROM Users,Tasks,Projects", connection) Dim ds As New DataSet TryadpUsers.Fill(ds, "Users")
ds.Relations.Add("UserTasks", ds.Tables("Users").Columns("userID"), ds.Tables("Tasks").Columns("userID"))
dgUsers.SetDataBinding(ds, "Users.userID")
dgTasks.SetDataBinding(ds, "Tasks.userID")
Catch ex As ExceptionMessageBox.Show(ex.Message, "Admin Main Form Load")
End TryAnyway, I just want to thank you for this nice and realy usefull article.
I not only got the relationships shown in a DataGrid, but also I could
modify them through the DataSet.
try
Private view As DataView = m_ds.Tables("Stuff").DefaultView
But anyway I think that the trick is that the field ID is PK
I tried your code but the dv index is out of sync with the bm index. Please explain. You can email me at glenn_r@shaw.ca.
Here my challenge. I have reference to a datarow in a datatable. I want the set the bindingcontext position to that datarow. How can I do this? When I sort the dataview it goes out of sync with the datatable index returned when I use the dv find method.
Thanks,
Glenn
This thread is for discussions of Winforms Data Binding Lessons Learned.