更新 DataGridView 但在进入 TabPage 时不 select 任何行
Update DataGridView but do not select any rows when TabPage entered
我在辅助 TabPage
上有一个 DataGridView
,我希望在输入 TabPage
时更新网格内的数据,但我不希望 RowEnter
事件被处理,除非用户实际点击了一行。似乎网格中的第一行是在 TabPage.Enter
事件触发后自动选择的,所以我无法抑制它。
显示问题的代码如下。我在运行时创建了控件,因此您只需复制粘贴即可,但在实际程序中我使用了 Designer。
我希望看到的行为是,在选择 TabPage2
之后,DataGridView
充满了数据,但 TextBox1
是空的,直到我点击一行。
Public Class Form1
Private WithEvents DataGridView1 As DataGridView
Private WithEvents TextBox1 As TextBox
Private WithEvents TabPage2 As TabPage
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
'Add controls to the form (usually I use the designer to do this)
Dim TabPage1 As New TabPage() With {.Name = "TabPage1", .Text = "TabPage1"}
TabPage2 = New TabPage() With {.Name = "TabPage2", .Text = "TabPage2"}
DataGridView1 = New DataGridView With {.Name = "DataGridView1", .SelectionMode = DataGridViewSelectionMode.FullRowSelect, .MultiSelect = False, .ReadOnly = True, .AllowUserToAddRows = False, .Size = New Size(TabPage1.Size.Width, TabPage2.Size.Height - 40), .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom, .TabIndex = 1}
TextBox1 = New TextBox With {.Name = "TextBox1", .Top = DataGridView1.Bottom + 5, .Width = DataGridView1.Width, .Visible = True, .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom, .TabIndex = 0}
TabPage2.Controls.Add(TextBox1)
TabPage2.Controls.Add(DataGridView1)
Dim TabControl1 As New TabControl() With {.Name = "TabControl1"}
TabControl1.TabPages.Add(TabPage1)
TabControl1.TabPages.Add(TabPage2)
TabControl1.Size = Me.ClientRectangle.Size
TabControl1.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom
Me.Controls.Add(TabControl1)
End Sub
Private Sub DataGridView1_RowEnter(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowEnter
'I would like the textbox to fill ONLY after the user has selected a row in DataGridView1.
'The problem I am having is that the first row auto-selects once I enter the tab
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
TextBox1.Text = CStr(drw(1))
End Sub
Private Sub TabPage2_Enter(sender As Object, e As EventArgs) Handles TabPage2.Enter
RefreshGrid() 'Refresh the data in the list
End Sub
Sub RefreshGrid()
'simulate a database query
DataGridView1.DataSource = Nothing
Dim dtb As New DataTable
dtb.Columns.Add("C1")
dtb.Columns.Add("C2")
dtb.Rows.Add("1", "One")
dtb.Rows.Add("2", "Two")
dtb.Rows.Add("3", "Three")
dtb.Rows.Add("4", "Four")
dtb.Rows.Add("5", "Five")
DataGridView1.DataSource = dtb
End Sub
End Class
如果您希望用户有意识地选择一行来执行操作,那么您可能需要数据网格的 Click 事件和 KeyPress 事件的组合来处理该操作。当用户到达他想要选择的行以触发您想要执行的操作时,您需要用户按下一个键。
在下面的示例中,他们可以使用箭头键导航只读网格,并 'activate' 使用回车键导航一行。
例如:
Private Sub mygrid_Keydown(sender As Object, e As KeyEventArgs) Handles mygrid.KeyDown
If e.KeyCode = (Keys.Return) Then
executemymethod(sender.CurrentCell.RowNumber)
End If
End Sub
Private Sub mygrid_Click (sender As Object, e As System.EventArgs) Handles mygrid.Click
executemymethod(sender.CurrentCell.RowNumber)
End Sub
不是在 RowEnter 上更新文本框,而是将其设置为在 CellClick 上更新。
Private Sub DataGridView1_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
If e.RowIndex = -1 Then Exit Sub 'Don't do anything for the header being clicked
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
TextBox1.Text = CStr(drw(1))
End Sub
感谢那些迄今为止做出贡献的人。我发现的一种解决方案是在 TabPage.VisibleChanged
事件中设置一个标志。此事件似乎在 TabPage.Entered
事件之后但在 DataGridView.RowEnter
事件之前触发(请参阅下面的代码)。我将添加赏金以鼓励更多贡献。
Private mblnStopRowEnter As Boolean
Private Sub TabPage2_Enter(sender As Object, e As EventArgs) Handles TabPage2.Enter
mblnStopRowEnter = True
RefreshGrid() 'Refresh the data in the list
End Sub
Private Sub TabPage2_VisibleChanged(sender As Object, e As EventArgs) Handles TabPage2.VisibleChanged
If TabPage2.Visible Then mblnStopRowEnter = False
End Sub
Private Sub DataGridView1_RowEnter(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowEnter
'I would like the textbox to fill ONLY after the user has selected a row in DataGridView1.
'The problem I am having is that the first row auto-selects once I enter the tab
If mblnStopRowEnter Then Exit Sub
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
TextBox1.Text = CStr(drw(1))
End Sub
来自TabPage documentation Remarks section
Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown.
由于您将 TabPage.Enter
事件中的 DataGridView
绑定到新的 DataTable
,您可以使用 DataGridView.DataBindingComplete
事件清除默认选择。
Private Sub DataGridView1_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles DataGridView1.DataBindingComplete
TextBox1.Clear()
DataGridView1.ClearSelection()
End Sub
好的,我只是在您发布的代码中添加了几行,它按照您要求的方式运行。
Public Class Form1
Private WithEvents DataGridView1 As DataGridView
Private WithEvents TextBox1 As TextBox
Private WithEvents TabPage2 As TabPage
Private actualClick As Boolean = False
Private secondBool As Boolean = False
Private pageBool As Boolean = False
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
'Add controls to the form (usually I use the designer to do this)
Dim TabPage1 As New TabPage() With {.Name = "TabPage1", .Text = "TabPage1"}
TabPage2 = New TabPage() With {.Name = "TabPage2", .Text = "TabPage2"}
DataGridView1 = New DataGridView With {.Name = "DataGridView1", .SelectionMode = DataGridViewSelectionMode.FullRowSelect, .MultiSelect = False, .ReadOnly = True, .AllowUserToAddRows = False, .Size = New Size(TabPage1.Size.Width, TabPage2.Size.Height - 40), .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom, .TabIndex = 1}
TextBox1 = New TextBox With {.Name = "TextBox1", .Top = DataGridView1.Bottom + 5, .Width = DataGridView1.Width, .Visible = True, .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom, .TabIndex = 0}
TabPage2.Controls.Add(TextBox1)
TabPage2.Controls.Add(DataGridView1)
Dim TabControl1 As New TabControl() With {.Name = "TabControl1"}
TabControl1.TabPages.Add(TabPage1)
TabControl1.TabPages.Add(TabPage2)
TabControl1.Size = Me.ClientRectangle.Size
TabControl1.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom
Me.Controls.Add(TabControl1)
End Sub
Private Sub DataGridView1_RowEnter(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowEnter
'I would like the textbox to fill ONLY after the user has selected a row in DataGridView1.
'The problem I am having is that the first row auto-selects once I enter the tab
TextBox1.Text = ""
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
If actualClick = True And secondBool = True Then
TextBox1.Text = CStr(drw(1))
End If
If actualClick = True Then
secondBool = True
End If
actualClick = True
End Sub
Private Sub TabPage2_Enter(sender As Object, e As EventArgs) Handles TabPage2.Enter
RefreshGrid() 'Refresh the data in the list
actualClick = False
If pageBool = True Then
secondBool = True
End If
pageBool = True
TextBox1.Text = ""
End Sub
Sub RefreshGrid()
'simulate a database query
DataGridView1.DataSource = Nothing
Dim dtb As New DataTable
dtb.Columns.Add("C1")
dtb.Columns.Add("C2")
dtb.Rows.Add("1", "One")
dtb.Rows.Add("2", "Two")
dtb.Rows.Add("3", "Three")
dtb.Rows.Add("4", "Four")
dtb.Rows.Add("5", "Five")
DataGridView1.DataSource = dtb
End Sub
End Class
我使用三个全局变量作为标志来跟上是否应更改 TextBox1。 (需要两个标志,因为 RowEnter 事件处理程序在调用时实际上连续调用了两次。需要第三个标志是因为在程序开始时选择标签页 2 与选择标签页 1 之后触发的事件不同,然后重新选择标签页 2.)
您的代码存在两个问题。第一个是事实:
Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown. [1]
1: TabPage documentation - Remarks section
TabPage
的这种行为在尝试达到预期效果时需要特别注意,因为它可能会改变事件触发器的预期顺序。
第二个是使用 VB.Net Handles
关键字来连接事件处理程序。 Handles
关键字可能会导致事件处理程序在您真正希望它处于活动状态之前触发。这导致编码技巧,例如创建和设置 flag 变量来控制代码执行。一种更清晰的机制是使用 Addhandler
关键字仅在需要时附加事件处理程序,而不是依靠噱头来解决由语法糖创建的行为。
下面的示例没有使用 RowEnter
事件,而是使用 SelectionChanged
事件来更新 TextBox
。这将允许代码在使用或不使用 DataGridView. The
TabControl.SelectedIndexChangedevent is used call the
RefreshGrid 方法 instead of the
TabPage.Enter[= 的 FullRowSelect
选择模式的情况下运行22=]TabPage` 已显示并避免了一些事件计时问题。
Public Class Form1
Private WithEvents DataGridView1 As DataGridView
Private WithEvents TextBox1 As TextBox
Private WithEvents TabPage2 As TabPage
Private WithEvents TabControl1 As TabControl
Sub New()
InitializeComponent()
Dim TabPage1 As New TabPage() With {.Name = "TabPage1", .Text = "TabPage1"}
TabPage2 = New TabPage() With {.Name = "TabPage2", .Text = "TabPage2"}
DataGridView1 = New DataGridView With {.Name = "DataGridView1", .SelectionMode = DataGridViewSelectionMode.FullRowSelect, .MultiSelect = False, .ReadOnly = True, .AllowUserToAddRows = False, .Size = New Size(TabPage1.Size.Width, TabPage2.Size.Height - 40), .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom, .TabIndex = 1}
TextBox1 = New TextBox With {.Name = "TextBox1", .Top = DataGridView1.Bottom + 5, .Width = DataGridView1.Width, .Visible = True, .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom, .TabIndex = 0}
TabPage2.Controls.Add(TextBox1)
TabPage2.Controls.Add(DataGridView1)
TabControl1 = New TabControl() With {.Name = "TabControl1"}
TabControl1.TabPages.Add(TabPage1)
TabControl1.TabPages.Add(TabPage2)
TabControl1.Size = Me.ClientRectangle.Size
TabControl1.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom
Me.Controls.Add(TabControl1)
End Sub
Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles TabControl1.SelectedIndexChanged
If TabControl1.SelectedIndex = 1 Then
Me.RefreshGrid() 'Refresh the data in the list
End If
End Sub
Private Sub DataGridView1_SelectionChanged(sender As Object, e As EventArgs) 'Handles DataGridView1.SelectionChanged
TextBox1.Text = DirectCast(DataGridView1.CurrentRow.DataBoundItem, DataRowView).Item(1).ToString()
End Sub
Sub RefreshGrid()
'simulate a database query
Dim dt As DataTable = TryCast(DataGridView1.DataSource, DataTable)
If dt IsNot Nothing Then
DataGridView1.DataSource = Nothing
dt.Dispose()
End If
Dim dtb As New DataTable
dtb.Columns.Add("C1")
dtb.Columns.Add("C2")
dtb.Rows.Add("1", "One")
dtb.Rows.Add("2", "Two")
dtb.Rows.Add("3", "Three")
dtb.Rows.Add("4", "Four")
dtb.Rows.Add("5", "Five")
' clear any existing handler first. if there is no existing handler, this will not cause an error
RemoveHandler DataGridView1.SelectionChanged, AddressOf DataGridView1_SelectionChanged
DataGridView1.DataSource = dtb
' setting the DataSource will select the 1st row, so clear it
DataGridView1.ClearSelection()
' attach the handler
AddHandler DataGridView1.SelectionChanged, AddressOf DataGridView1_SelectionChanged
If Not String.IsNullOrWhiteSpace(TextBox1.Text) Then TextBox1.Clear() ' clear any previous text
End Sub
End Class
我在辅助 TabPage
上有一个 DataGridView
,我希望在输入 TabPage
时更新网格内的数据,但我不希望 RowEnter
事件被处理,除非用户实际点击了一行。似乎网格中的第一行是在 TabPage.Enter
事件触发后自动选择的,所以我无法抑制它。
显示问题的代码如下。我在运行时创建了控件,因此您只需复制粘贴即可,但在实际程序中我使用了 Designer。
我希望看到的行为是,在选择 TabPage2
之后,DataGridView
充满了数据,但 TextBox1
是空的,直到我点击一行。
Public Class Form1
Private WithEvents DataGridView1 As DataGridView
Private WithEvents TextBox1 As TextBox
Private WithEvents TabPage2 As TabPage
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
'Add controls to the form (usually I use the designer to do this)
Dim TabPage1 As New TabPage() With {.Name = "TabPage1", .Text = "TabPage1"}
TabPage2 = New TabPage() With {.Name = "TabPage2", .Text = "TabPage2"}
DataGridView1 = New DataGridView With {.Name = "DataGridView1", .SelectionMode = DataGridViewSelectionMode.FullRowSelect, .MultiSelect = False, .ReadOnly = True, .AllowUserToAddRows = False, .Size = New Size(TabPage1.Size.Width, TabPage2.Size.Height - 40), .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom, .TabIndex = 1}
TextBox1 = New TextBox With {.Name = "TextBox1", .Top = DataGridView1.Bottom + 5, .Width = DataGridView1.Width, .Visible = True, .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom, .TabIndex = 0}
TabPage2.Controls.Add(TextBox1)
TabPage2.Controls.Add(DataGridView1)
Dim TabControl1 As New TabControl() With {.Name = "TabControl1"}
TabControl1.TabPages.Add(TabPage1)
TabControl1.TabPages.Add(TabPage2)
TabControl1.Size = Me.ClientRectangle.Size
TabControl1.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom
Me.Controls.Add(TabControl1)
End Sub
Private Sub DataGridView1_RowEnter(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowEnter
'I would like the textbox to fill ONLY after the user has selected a row in DataGridView1.
'The problem I am having is that the first row auto-selects once I enter the tab
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
TextBox1.Text = CStr(drw(1))
End Sub
Private Sub TabPage2_Enter(sender As Object, e As EventArgs) Handles TabPage2.Enter
RefreshGrid() 'Refresh the data in the list
End Sub
Sub RefreshGrid()
'simulate a database query
DataGridView1.DataSource = Nothing
Dim dtb As New DataTable
dtb.Columns.Add("C1")
dtb.Columns.Add("C2")
dtb.Rows.Add("1", "One")
dtb.Rows.Add("2", "Two")
dtb.Rows.Add("3", "Three")
dtb.Rows.Add("4", "Four")
dtb.Rows.Add("5", "Five")
DataGridView1.DataSource = dtb
End Sub
End Class
如果您希望用户有意识地选择一行来执行操作,那么您可能需要数据网格的 Click 事件和 KeyPress 事件的组合来处理该操作。当用户到达他想要选择的行以触发您想要执行的操作时,您需要用户按下一个键。
在下面的示例中,他们可以使用箭头键导航只读网格,并 'activate' 使用回车键导航一行。
例如:
Private Sub mygrid_Keydown(sender As Object, e As KeyEventArgs) Handles mygrid.KeyDown
If e.KeyCode = (Keys.Return) Then
executemymethod(sender.CurrentCell.RowNumber)
End If
End Sub
Private Sub mygrid_Click (sender As Object, e As System.EventArgs) Handles mygrid.Click
executemymethod(sender.CurrentCell.RowNumber)
End Sub
不是在 RowEnter 上更新文本框,而是将其设置为在 CellClick 上更新。
Private Sub DataGridView1_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
If e.RowIndex = -1 Then Exit Sub 'Don't do anything for the header being clicked
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
TextBox1.Text = CStr(drw(1))
End Sub
感谢那些迄今为止做出贡献的人。我发现的一种解决方案是在 TabPage.VisibleChanged
事件中设置一个标志。此事件似乎在 TabPage.Entered
事件之后但在 DataGridView.RowEnter
事件之前触发(请参阅下面的代码)。我将添加赏金以鼓励更多贡献。
Private mblnStopRowEnter As Boolean
Private Sub TabPage2_Enter(sender As Object, e As EventArgs) Handles TabPage2.Enter
mblnStopRowEnter = True
RefreshGrid() 'Refresh the data in the list
End Sub
Private Sub TabPage2_VisibleChanged(sender As Object, e As EventArgs) Handles TabPage2.VisibleChanged
If TabPage2.Visible Then mblnStopRowEnter = False
End Sub
Private Sub DataGridView1_RowEnter(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowEnter
'I would like the textbox to fill ONLY after the user has selected a row in DataGridView1.
'The problem I am having is that the first row auto-selects once I enter the tab
If mblnStopRowEnter Then Exit Sub
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
TextBox1.Text = CStr(drw(1))
End Sub
来自TabPage documentation Remarks section
Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown.
由于您将 TabPage.Enter
事件中的 DataGridView
绑定到新的 DataTable
,您可以使用 DataGridView.DataBindingComplete
事件清除默认选择。
Private Sub DataGridView1_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles DataGridView1.DataBindingComplete
TextBox1.Clear()
DataGridView1.ClearSelection()
End Sub
好的,我只是在您发布的代码中添加了几行,它按照您要求的方式运行。
Public Class Form1
Private WithEvents DataGridView1 As DataGridView
Private WithEvents TextBox1 As TextBox
Private WithEvents TabPage2 As TabPage
Private actualClick As Boolean = False
Private secondBool As Boolean = False
Private pageBool As Boolean = False
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
'Add controls to the form (usually I use the designer to do this)
Dim TabPage1 As New TabPage() With {.Name = "TabPage1", .Text = "TabPage1"}
TabPage2 = New TabPage() With {.Name = "TabPage2", .Text = "TabPage2"}
DataGridView1 = New DataGridView With {.Name = "DataGridView1", .SelectionMode = DataGridViewSelectionMode.FullRowSelect, .MultiSelect = False, .ReadOnly = True, .AllowUserToAddRows = False, .Size = New Size(TabPage1.Size.Width, TabPage2.Size.Height - 40), .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom, .TabIndex = 1}
TextBox1 = New TextBox With {.Name = "TextBox1", .Top = DataGridView1.Bottom + 5, .Width = DataGridView1.Width, .Visible = True, .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom, .TabIndex = 0}
TabPage2.Controls.Add(TextBox1)
TabPage2.Controls.Add(DataGridView1)
Dim TabControl1 As New TabControl() With {.Name = "TabControl1"}
TabControl1.TabPages.Add(TabPage1)
TabControl1.TabPages.Add(TabPage2)
TabControl1.Size = Me.ClientRectangle.Size
TabControl1.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom
Me.Controls.Add(TabControl1)
End Sub
Private Sub DataGridView1_RowEnter(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowEnter
'I would like the textbox to fill ONLY after the user has selected a row in DataGridView1.
'The problem I am having is that the first row auto-selects once I enter the tab
TextBox1.Text = ""
Dim drw As DataRow = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView).Row
If actualClick = True And secondBool = True Then
TextBox1.Text = CStr(drw(1))
End If
If actualClick = True Then
secondBool = True
End If
actualClick = True
End Sub
Private Sub TabPage2_Enter(sender As Object, e As EventArgs) Handles TabPage2.Enter
RefreshGrid() 'Refresh the data in the list
actualClick = False
If pageBool = True Then
secondBool = True
End If
pageBool = True
TextBox1.Text = ""
End Sub
Sub RefreshGrid()
'simulate a database query
DataGridView1.DataSource = Nothing
Dim dtb As New DataTable
dtb.Columns.Add("C1")
dtb.Columns.Add("C2")
dtb.Rows.Add("1", "One")
dtb.Rows.Add("2", "Two")
dtb.Rows.Add("3", "Three")
dtb.Rows.Add("4", "Four")
dtb.Rows.Add("5", "Five")
DataGridView1.DataSource = dtb
End Sub
End Class
我使用三个全局变量作为标志来跟上是否应更改 TextBox1。 (需要两个标志,因为 RowEnter 事件处理程序在调用时实际上连续调用了两次。需要第三个标志是因为在程序开始时选择标签页 2 与选择标签页 1 之后触发的事件不同,然后重新选择标签页 2.)
您的代码存在两个问题。第一个是事实:
Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown. [1]
1: TabPage documentation - Remarks section
TabPage
的这种行为在尝试达到预期效果时需要特别注意,因为它可能会改变事件触发器的预期顺序。
第二个是使用 VB.Net Handles
关键字来连接事件处理程序。 Handles
关键字可能会导致事件处理程序在您真正希望它处于活动状态之前触发。这导致编码技巧,例如创建和设置 flag 变量来控制代码执行。一种更清晰的机制是使用 Addhandler
关键字仅在需要时附加事件处理程序,而不是依靠噱头来解决由语法糖创建的行为。
下面的示例没有使用 RowEnter
事件,而是使用 SelectionChanged
事件来更新 TextBox
。这将允许代码在使用或不使用 DataGridView. The
TabControl.SelectedIndexChangedevent is used call the
RefreshGrid 方法 instead of the
TabPage.Enter[= 的 FullRowSelect
选择模式的情况下运行22=]TabPage` 已显示并避免了一些事件计时问题。
Public Class Form1
Private WithEvents DataGridView1 As DataGridView
Private WithEvents TextBox1 As TextBox
Private WithEvents TabPage2 As TabPage
Private WithEvents TabControl1 As TabControl
Sub New()
InitializeComponent()
Dim TabPage1 As New TabPage() With {.Name = "TabPage1", .Text = "TabPage1"}
TabPage2 = New TabPage() With {.Name = "TabPage2", .Text = "TabPage2"}
DataGridView1 = New DataGridView With {.Name = "DataGridView1", .SelectionMode = DataGridViewSelectionMode.FullRowSelect, .MultiSelect = False, .ReadOnly = True, .AllowUserToAddRows = False, .Size = New Size(TabPage1.Size.Width, TabPage2.Size.Height - 40), .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom, .TabIndex = 1}
TextBox1 = New TextBox With {.Name = "TextBox1", .Top = DataGridView1.Bottom + 5, .Width = DataGridView1.Width, .Visible = True, .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom, .TabIndex = 0}
TabPage2.Controls.Add(TextBox1)
TabPage2.Controls.Add(DataGridView1)
TabControl1 = New TabControl() With {.Name = "TabControl1"}
TabControl1.TabPages.Add(TabPage1)
TabControl1.TabPages.Add(TabPage2)
TabControl1.Size = Me.ClientRectangle.Size
TabControl1.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom
Me.Controls.Add(TabControl1)
End Sub
Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles TabControl1.SelectedIndexChanged
If TabControl1.SelectedIndex = 1 Then
Me.RefreshGrid() 'Refresh the data in the list
End If
End Sub
Private Sub DataGridView1_SelectionChanged(sender As Object, e As EventArgs) 'Handles DataGridView1.SelectionChanged
TextBox1.Text = DirectCast(DataGridView1.CurrentRow.DataBoundItem, DataRowView).Item(1).ToString()
End Sub
Sub RefreshGrid()
'simulate a database query
Dim dt As DataTable = TryCast(DataGridView1.DataSource, DataTable)
If dt IsNot Nothing Then
DataGridView1.DataSource = Nothing
dt.Dispose()
End If
Dim dtb As New DataTable
dtb.Columns.Add("C1")
dtb.Columns.Add("C2")
dtb.Rows.Add("1", "One")
dtb.Rows.Add("2", "Two")
dtb.Rows.Add("3", "Three")
dtb.Rows.Add("4", "Four")
dtb.Rows.Add("5", "Five")
' clear any existing handler first. if there is no existing handler, this will not cause an error
RemoveHandler DataGridView1.SelectionChanged, AddressOf DataGridView1_SelectionChanged
DataGridView1.DataSource = dtb
' setting the DataSource will select the 1st row, so clear it
DataGridView1.ClearSelection()
' attach the handler
AddHandler DataGridView1.SelectionChanged, AddressOf DataGridView1_SelectionChanged
If Not String.IsNullOrWhiteSpace(TextBox1.Text) Then TextBox1.Clear() ' clear any previous text
End Sub
End Class