I have a form with 4 tabs and a binding navigator at the top of the form. I would like to use the binding navigator on each tab to scroll on datagriewviews on each tab. At the moment I can only scroll on the first tab. Please help me if you know how I can bind this to each tab in VB.net 2005
You need to use a different BindingSource on each tab. When the user selects a different tab, which raises the SelectedIndexChanged event of the TabControl, you need to assign the BindingSource for that tab to the BindingSource property of the BindingNavigator.
Thank you very much I will give it a try tomorrow and will post back with the result. Do you by any chance know the actual steps I should take or a sample of code to this may help.
Handle the TabControl's SelectedIndexChanged event and assign the appropriate BindingSource to the BindingNavigator's property. How you identify the appropriate BindingSource is up to you, but the easiest way would be to assign it to the Tag property of the TabPage when you create it. Then the code would be:
I have tried to assign the binding navigator to the tag control but it does not appear under the tag property for the tabControl only the different binding sources appear. Is there any other way in code that i could do this? Thanks
The Tag property of the TabPage, not the TabControl. You have to assign each BindingSource to the Tag property of its corresponding TabPage and you have to do it IN CODE.
I know that this is an ancient thread, but I came across it today via a Google search while trying to find a solution to what appears to be the same issue that the OP had.
My form has a single BindingNavigator and a TabControl w/3 TabPages. Like the OP, I'm trying to share the BindingNavigator w/each of these TabPages.
When the user selects a node from the TreeView its AfterSelect event fires which then sets the TabControl's SelectedIndex property.
Code:
If tvMain.SelectedNode.Level = 0 Then
'...user selected a Project node
lngProjectID = CLng(tvMain.SelectedNode.Tag)
tcMain.SelectedIndex = tcMain.TabPages("pgeProjects").TabIndex
ElseIf tvMain.SelectedNode.Level = 1 Then
'...user selected a Revision node
lngProjectID = CLng(tvMain.SelectedNode.Parent.Tag)
lngRevisionID = CLng(tvMain.SelectedNode.Tag)
tcMain.SelectedIndex = tcMain.TabPages("pgeRevisions").TabIndex
Else
'...user selected a RevisionNote node
lngProjectID = CLng(tvMain.SelectedNode.Parent.Parent.Tag)
lngRevisionID = CLng(tvMain.SelectedNode.Parent.Tag)
lngRevisionNoteID = CLng(tvMain.SelectedNode.Tag)
dtmRevisionNoteDate = CDate(tvMain.SelectedNode.Text)
tcMain.SelectedIndex = tcMain.TabPages("pgeNotes").TabIndex
End If
When the SelectedIndex property is set in the above code an unhandled error is raised (even though I have a Try/Catch block in the AfterSelect event handler):
At this point, the TabControl's SelectedIndex event handler has not fired.
As per your post #4...
You have to assign each BindingSource to the Tag property of its corresponding TabPage
@Mark@SF, it would appear that somewhere you have assigned a DataSet to a DataSource property and set the DataMember to the name of a DataTable and later you are changing that DataSource without changing the DataMember. That could be in a variety of places. For instance, if you assigned a DataSet to the DataSource of a BindingSource and set the DataMember of a DataGridView that you bound that BindingSource to, then later assign a DataTable to the DataSource of another BindingSource and then bound that BindingSource to the same grid. Personally, I prefer to always bind a DataTable directly, rather than binding a DataSet and setting the DataMember to the name of the DataTable. If you have bound in the designer though, you can't do that.
The TabIndex controls the order in which controls receive focus when using the Tab key. They may be the same as the index in the parent collection but they certainly don't have to be. That code should be like this:
Thanks for the tip, I've changed the code to use the SelectedTab instead of SelectedIndex.
Regarding the unhandled error, the BindingSource.DataMember is a Relation.
Code:
With bsRevisions
.DataSource = Nothing
.DataMember = "Project_Revisions" '...bind to relationship (before setting the DataSource property, which fires the PositionChanged() event of the BindingSource)
.DataSource = bsProjects '...fires the BindingSource's PositionChanged() event
.Sort = "RevisionName"
End With
I've searched the form's code (CTL-F / Current Document) and can confirm that the bsRevisions DataSource and DataMember properties are only set once, in a procedure that configures all 3 BindingSource components (bsProjects, bsRevisions, and bsRevisionObjects). Immediately after each of these components have been configured and in the TreeView control's AfterSelect event, I have inspected the contents of the bsRevisions BindingSource are as expected.
Code:
Sub InspectBindingSource(bs As BindingSource)
'https://stackoverflow.com/questions/2170442/vb-net-how-iterate-through-a-bindingsource
Dim strMethodName = New System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name '...this procedure's name
Dim flgDebug As Boolean = False '...debug/test purposes only
Dim drv As DataRowView
Try
If flgDebug Then Debug.WriteLine("{0} ({1})", strMethodName, Date.Now)
For i As Integer = 0 To bs.Count - 1
drv = DirectCast(bs.Item(i), DataRowView)
ShowRowInfo(drv.Row, True)
Next i
Catch ex As Exception
HandleError(ex, True)
Finally
drv = Nothing
End Try
End Sub
Just for demonstration, I modified the TreeView's AfterSelect event to include explicitly configuring the 3 BindingSource components.
Code:
With bsProjects
.DataSource = Nothing
.DataMember = "Projects" '...bind to datatable (before setting the DataSource property, which fires the PositionChanged() event of the BindingSource)
.DataSource = dsMain '...fires the BindingSource's PositionChanged() event
.Sort = "ProjectName"
End With
With bsRevisions
.DataSource = Nothing
.DataMember = "Project_Revisions" '...bind to relationship (before setting the DataSource property, which fires the PositionChanged() event of the BindingSource)
.DataSource = bsProjects '...fires the BindingSource's PositionChanged() event
.Sort = "RevisionName"
End With
With bsRevisionNotes
.DataSource = Nothing
.DataMember = "Revision_RevisionNotes" '...bind to relationship (before setting the DataSource property, which fires the PositionChanged() event of the BindingSource)
.DataSource = bsRevisions '...fires the BindingSource's PositionChanged() event
.Sort = "RecDate"
End With
If flgDebug Then
InspectBindingSource(bsProjects)
InspectBindingSource(bsRevisions)
InspectBindingSource(bsRevisionNotes)
End If
If tvMain.SelectedNode.Level = 0 Then
'...user selected a Project node
lngProjectID = CLng(tvMain.SelectedNode.Tag)
tcMain.SelectedTab = tcMain.TabPages("pgeProjects")
ElseIf tvMain.SelectedNode.Level = 1 Then
'...user selected a Revision node
lngProjectID = CLng(tvMain.SelectedNode.Parent.Tag)
lngRevisionID = CLng(tvMain.SelectedNode.Tag)
tcMain.SelectedTab = tcMain.TabPages("pgeRevisions")
Else
'...user selected a RevisionNote node
lngProjectID = CLng(tvMain.SelectedNode.Parent.Parent.Tag)
lngRevisionID = CLng(tvMain.SelectedNode.Parent.Tag)
lngRevisionNoteID = CLng(tvMain.SelectedNode.Tag)
dtmRevisionNoteDate = CDate(tvMain.SelectedNode.Text)
tcMain.SelectedTab = tcMain.TabPages("pgeNotes")
End If
The unhandled error now occurs when setting the bsProjects.DataMember = "Projects". The Projects DataTable has 2 rows (dsMain.Tables("Projects").Rows.Count = 2).
Is the issue that I'm using a Relation as the DataMember of the BindingSource?
Last edited by Mark@SF; Apr 29th, 2020 at 09:55 PM.
I'm not sure what else to tell you. At some point, you are setting an object as a DataSource with "Project_Revisions" as the DataMember and that is invalid. I might start by setting watches on all my binding properties and then stepping through the code to see what changes when and what coincides with that exception.
Also, have you checked the stack trace of the exception to see whether that yields anything useful? I'm not hopeful that it will but you should ALWAYS examine an exception to see whether it contains anything useful.
Each item in the stack trace is a method that was executing at the time. If a Click event handler calls Method1 that calls Method2 that calls Method3 that throws an exception then all those methods will be in the stack trace, with Method3 at the top. You can look at the top of the stack trace to see where the exception was actually thrown and look down to see where it came from. Hopefully you can see some of your own methods in the call stack. If so, that tells you where you can put a breakpoint for your next debugging run to enable you to examine the state just before the exception is thrown. Sometimes there is nothing but system calls in the call stack though, so it can be hard to determine what action in your own code set of that chain of method calls.
I might start by setting watches on all my binding properties and then stepping through the code to see what changes when and what coincides with that exception.
Great tip! I've never used the Watch Window and it showed me where the DataSource/DataMember issue was.
So everything is working, thanks to your helpful comments.