2016-07-18 13 views
3

У меня есть DataGridView на вторичном TabPage, и я хотел бы, чтобы данные внутри сетки для обновления, когда TabPage введен, но я не хочу RowEnter событие, чтобы быть если пользователь фактически не нажал на строку. Кажется, что первая строка в сетке автоматически выбрана ПОСЛЕ погашения события TabPage.Enter, поэтому у меня возникают проблемы с ее подавлением.Обновление DataGridView, но не выбрать ни одной строки, когда TabPage вошел

Код, показывающий проблему, приведен ниже. Я создал элементы управления во время выполнения, поэтому вы можете просто скопировать-вставить, но в реальной программе я использовал конструктор.

Поведение, которое я хотел бы видеть, заключается в том, что после выбора TabPage2DataGridView полна данных, но 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 
+0

Когда вы выберете «TabPage», первый элемент управления в порядке табуляции на этой странице получит фокус. Если ваша сетка является первым элементом управления, она получит фокус. Если это единственный элемент управления, у вас нет выбора, кроме как для получения фокуса. – jmcilhinney

+0

ОК, я обновил для установки '.TabIndex' для каждого элемента управления, но проблема все еще возникает. Я не думаю, что «DataGridView» изменяет выбранную строку, когда получает фокус. Я думаю, что «DataGridView» устанавливает выбор при инициализации. (Фактически, сама вкладка TabPage сохраняет фокус при нажатии на нее) – SSS

+0

Это зависит от того, что вы подразумеваете под «выбранным». В элементе управления «DataGridView» выбранный на самом деле означает, что он выделен и не имеет ничего общего с каким-либо событием «RowEnter». 'RowEnter' будет только поднят, когда строка станет текущей строкой, то есть строкой, которая содержит текущую ячейку. Можно установить 'CurrentCell' в' Nothing', и он останется 'Nothing', пока сетка не станет фокусом. – jmcilhinney

ответ

3

Из TabPage documentation Remarks section

управления, содержащегося в TabPage не создается, пока закладка не отображаются, и любые привязки данных в этих элементах управления не активны, пока закладка не отображаются.

Поскольку вы связывание DataGridView в TabPage.Enter случае к новому DataTable, вы можете использовать DataGridView.DataBindingComplete событие, чтобы очистить выбор по умолчанию.

Private Sub DataGridView1_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles DataGridView1.DataBindingComplete 
    TextBox1.Clear() 
    DataGridView1.ClearSelection() 
End Sub 
+0

Это кажется лучшим решением. Спасибо всем, кто внес свой вклад. – SSS

+0

@SSS, вы избили меня до удара. Мне не понравился этот ответ, и я опубликовал только тот, который, я думаю, лучше и собирался удалить его. :) – TnTinMn

2

Если то, что вы хотите, чтобы пользователь сознательно выбрать строку для действия происходит, чем вы, вероятно, необходимы сочетание замкового события и событие KeyPress для DataGrid для обработки действия. Вам понадобится, чтобы пользователь нажал клавишу, когда она добирается до строки, которую он хочет выбрать, чтобы вызвать то, что вы хотите сделать.

В приведенном ниже примере они могут перемещаться по сетке readonly с помощью клавиши со стрелкой и «активировать» строку с enter.

, например:

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 
2

Вместо того, чтобы обновление текстового поля на 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 
1

Спасибо за тех, кто внес свой вклад до настоящего времени. Одним из решений, которое я обнаружил, является установка флага в событии 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 
+1

Я думаю, что большинство хороших альтернатив будут вариациями при использовании флага, чтобы знать, когда игнорировать. Это не похоже на игнорирование начальных изменений флажка при загрузке формы. Проблема окончательно зная, когда пользователь выбирает строку, поскольку она может быть с помощью мыши, клавиатуры и т. Д. – Plutonix

2

Хорошо, я просто добавил несколько строк к коду, который вы опубликовали, и он работает так, как вы просили.

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

1

Вы сталкиваетесь с двумя проблемами с кодом.Первый факт заключается в том, что:

Элементы управления, содержащиеся в TabPage, не создаются до тех пор, пока не будет показана страница закладки, и любые привязки данных в этих элементах управления не будут активированы до тех пор, пока не будет показана страница закладки. [1]

1: TabPage documentation - Remarks section

Такое поведение TabPage требует особого внимания при попытке достичь желаемого эффекта, как это может изменить ожидаемый порядок триггеров событий.

Во-вторых, использование ключевого слова VB.Net Handles для подключения обработчика событий. Ключевое слово Handles может привести к срабатыванию обработчика событий, прежде чем вы действительно захотите, чтобы он был активным. Это приводит к трюкам кодирования, таким как создание и установка флагов переменных для управления выполнением кода. Более четкий механизм заключается в использовании ключевого слова Addhandler для присоединения обработчика событий только тогда, когда это необходимо, вместо того чтобы полагаться на трюк, чтобы обойти поведение, созданное синтаксическим сахаром.

Вместо использования события RowEnter в приведенном ниже примере используется событие SelectionChanged для обновления TextBox. Это позволит коду функционировать с использованием или без использования режима выбора FullRowSelect для DataGridView. The TabControl.SelectedIndexChanged event is used call the Метод RefreshGrid instead of the TabPage.Enter event. This pushes the DataBinding occur to after the 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 
+0

Спасибо! Мне никогда не приходило в голову, что «Handles» был частью проблемы, но теперь вы упомянули об этом, очевидно, что использование «AddHandler» вместо этого является одним из способов управления синхронизацией событий при запуске. Использование «TabControl.SelectedIndexChanged», а не «TabPage.Enter», также является хорошим советом – SSS

 Смежные вопросы

  • Нет связанных вопросов^_^