【问题标题】:Update DataGridView but do not select any rows when TabPage entered更新 DataGridView 但输入 TabPage 时不选择任何行
【发布时间】:2016-11-20 12:16:08
【问题描述】:

我在辅助TabPage 上有一个DataGridView,我希望在输入TabPage 时更新网格内的数据,但我不希望处理RowEnter 事件,除非用户实际点击了一行。在TabPage.Enter 事件触发后,网格中的第一行似乎是自动选择的,所以我无法抑制它。

显示问题的代码如下。我在运行时创建了控件,因此您可以复制粘贴,但在实际程序中我使用了设计器。

我希望看到的行为是,选择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

【问题讨论】:

  • 当您选择TabPage 时,该页面上Tab 顺序中的第一个控件将获得焦点。如果您的网格是第一个控件,那么它将获得焦点。如果它是唯一的控件,那么您别无选择,只能让它获得焦点。
  • 好的,我已经更新为每个控件设置.TabIndex,但问题仍然存在。我认为DataGridView 在收到焦点时不会更改所选行。我认为DataGridView 在初始化时设置了选择。 (事实上​​,TabPage 本身在你点击时会保持焦点)
  • 这完全取决于您所说的“选定”。在DataGridView 控件中,selected 实际上意味着它被突出显示并且与任何RowEnter 事件无关。 RowEnter 只会在一行成为当前行时引发,即包含当前单元格的行。可以将CurrentCell 设置为Nothing,并且在网格获得焦点之前它将保持Nothing
  • 谢谢。 “选定”是指用户单击一行以使其成为当前行。我尝试将DataGridView1.CurrentCell = Nothing 添加到TabPage2_Enter 的末尾,但RowEnter 事件仍然会触发。
  • 我会使用点击事件并获取被点击的行。

标签: vb.net events datagridview tabcontrol tabpage


【解决方案1】:

来自TabPage documentation Remarks section

在标签页显示之前不会创建 TabPage 中包含的控件,并且在显示标签页之前不会激活这些控件中的任何数据绑定。

由于您将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

【讨论】:

  • 这似乎是最好的解决方案。感谢所有做出贡献的人。
  • @SSS,你打败了我。我不喜欢这个答案,只是发布了一个我认为更好的答案,并打算删除这个答案。 :)
【解决方案2】:

如果您希望用户有意识地选择要发生的操作的行,那么您可能需要结合 Click 事件和 KeyPress 事件来让数据网格处理该操作。当用户到达他想要选择的行时,您需要按一个键来触发您想要执行的操作。

在下面的示例中,他们可以使用箭头键导航只读网格并使用回车键“激活”一行。

例如:

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

【讨论】:

    【解决方案3】:

    不要在 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
    

    【讨论】:

      【解决方案4】:

      好的,我刚刚在您发布的代码中添加了几行代码,它按照您要求的方式运行。

      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.)

      【讨论】:

        【解决方案5】:

        感谢迄今为止做出贡献的人。我发现的一种解决方案是在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
        

        【讨论】:

        • 我认为大多数好的替代方案将是使用标志来知道何时忽略的变体。这与加载表单时忽略初始复选框更改没有什么不同。问题是明确知道用户何时选择一行,因为它可以通过鼠标、键盘等
        【解决方案6】:

        您正在与代码中的两个问题作斗争。首先是事实:

        在标签页显示之前不会创建 TabPage 中包含的控件,并且在显示标签页之前不会激活这些控件中的任何数据绑定。 [1]

        1: TabPage documentation - Remarks section

        TabPage 的这种行为在尝试达到预期效果时需要特别注意,因为它可以改变事件触发器的预期顺序。

        第二个是使用 VB.Net Handles 关键字来连接事件处理程序。 Handles 关键字可能会导致事件处理程序在您真正希望它处于活动状态之前触发。这会导致编码技巧,例如创建和设置 flag 变量来控制代码执行。更清晰的机制是使用Addhandler 关键字仅在需要时附加事件处理程序,而不是依靠噱头来解决由语法糖创建的行为。

        下面的示例不使用RowEnter 事件,而是使用SelectionChanged 事件来更新TextBox。这将允许代码在使用或不使用 DataGridView. TheTabControl.SelectedIndexChangedevent is used call theRefreshGrid Methodinstead of theTabPage.Enterevent. This pushes the DataBinding occur to after theTabPage 选择模式的情况下运行,并避免一些事件计时问题。

        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
        

        【讨论】:

        • 谢谢!我从来没有想到Handles 是问题的一部分,但现在你提到它,很明显使用AddHandler 是在启动时控制事件计时的一种方法。使用 TabControl.SelectedIndexChanged 而不是 'TabPage.Enter' 也是一个很好的提示
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多