search for the same cell in a different DataGridView - vb.net

I have a section of code that loops through a second DataGridView, trying to match the same index number from the main DataGridVew that the user interacts with:
Private Sub AllegationsDataGridView_CellEnter(sender As Object, e As EventArgs) Handles AllegationsDataGridView.CellEnter
Try
Dim currentcolumn As DataGridViewColumn =
AllegationsDataGridView.Columns(AllegationsDataGridView.CurrentCell.ColumnIndex)
For Each row As DataGridViewRow In parentgrid.Rows
If row.Cells.Item(0).Value = AllegationsDataGridView.CurrentRow.Cells(0).Value Then
parentgrid.CurrentCell = parentgrid(0, row.Index)
End If
Next
Catch ex As Exception
Debug.Print(ex.Message)
End Try
endsub:
End Sub
The problem is is that the datasource could potentially be thousands and thousands of entries, and I don't want this to loop through everysingle row until it finds the match. I wondered if there was a quicker way to approach this? The only examples I have seen when searching all use either a For Each Row approach, or a Loop Until approach, which would still present the same issue.

I suggest you to build a dictionary, when you initialize your datagridview with your datasource, that makes correspond the row key (AllegationsDataGridView.CurrentRow.Cells(0).Value) with the row index in the parentGrid. Use the datasource, and not the datagrid, to build this dictionary.
You will be able to access fastly to the corresponding row.

From comments: They both use the same datasource
I am not sure why a DGV the user doesnt interact with needs to have the CurrentCell kept in synch because if they are bound to the same datasource, changes to A are seen in B immediately. Nonetheless:
Private Sub dgv1_CellClick(sender As Object,
e As DataGridViewCellEventArgs) Handles dgv1.CellContentClick
If dgv2.Rows.Count = 0 Then Exit Sub
dgv2.CurrentCell = dgv2.Rows(e.RowIndex).Cells(e.ColumnIndex)
End Sub
Here, dgv1 is the user grid, dgv2 is the other ("parent" in your code?).
The For/Each iteration is not needed if they share a DS because the same data will be at each row and each column index. It will work even if the user has reordered the columns because the column index stays the same, just an internal DisplayIndex changes.
This uses the row and column indices from the DataGridViewCellEventArgs to set the CurrentCell. The Exit Sub is to account for starting up when one may have rows and not the other.
You may want to play with which event to respond to. CellContentClick seems least useful: if they click on any cell whitespace, it doesnt fire. It will also crash if the can click on a cell in the parent/dgv2 grid. CellEnter will also crash if they click on dgv2 directly.
CellClick seems to work okay, but there is the very slightest of delays before they synch up.

Related

How can I filter a ListView using TextBox and ComboBox?

Private Sub ComboBox2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox2.SelectedIndexChanged
ListView2.Items.Clear()
Dim curSelectionCombobox As String = ComboBox2.SelectedItem.ToString()
ListView2.Items.Add(listitm)
End Sub
Well basically this is what ive come up with the filtering thing in combobox which is obviously wont work
and in the combobox and button i didnt get to try coding those but im quite sure it wont work either im new in this language and im struggling to catch up giving the fact that this pandemic really gets me more and more stupider as the day passed by
Well my main problem is that the filtering in the groupBox_bookShelf is when i choose a genre in the combobox the static listview will filter leaving only the exact items its genre selected in the combobox
the second is the combobox and button im aiming to link the action of both property when filing in the groupBox_bookInformation then once the filter button is clicked i want to filter the lower listview leaving only the selected genre and its items
Here is the sample form ive been working on.
enter image description here
I am guessing that what is selected in the combo box is a value that appears in some of your list view items.
Start by calling .BeginUpdate() This will prevent the user interface from repainting on each update which would really slow things down.
I loop through the items and test one of the sub items to see if it matches the combo selection. If it does not match is is removed.
Be sure to call .EndUpdate or the changes will not show up.
Private Sub ComboBox2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox2.SelectedIndexChanged
Dim curSelectionCombobox As String = ComboBox2.SelectedItem.ToString()
ListView2.BeginUpdate()
For Each lvi As ListViewItem In ListView2.Items
If lvi.SubItems(6).Text <> curSelectionCombobox Then
lvi.Remove()
End If
Next
ListView2.EndUpdate()
End Sub

VB.NET Check for keydown on dragdrop

I've tried searching quite a bit for this answer but haven't been able to find a good solution.
I have a datagridview on my form where users can drag and drop files onto the grid and certain columns are filled in. This works fine.
I want to be able to check if a user has a certain key pressed at the time the file is dropped. If so, I want to use that to add specific data to one of the columns in the datagrid.
Is this possible?
EDIT:
I have used keydown outside of dragdrop before but it seems that I'm missing something. The code I have is below. No matter what I do, I never get "T is pressed" but I always get "T is not pressed".
Private Sub frmReader_DragDrop(sender As Object, e As DragEventArgs) Handles Me.DragDrop
Dim files As String() = CType(e.Data.GetData(DataFormats.FileDrop), String())
If Keyboard.IsKeyDown(Keys.T) Then
MsgBox("T is pressed.")
' Put certain info into the datagridview
Else
MsgBox("T is not pressed.")
' Put other data into the datagridview
End If
End Sub
God, embarassing... I changed "Keys.T" to "Key.T" and it's working fine. Sorry for the bother.

How to update a group of combo boxes using Loops

I have a form with combo boxes (cmbPort#) to select up to 8 serial ports. I want to first clear the item list for each, populate them with currently available system ports, and then add the option "Off" to each list. Finally, to set each combo box according to defaults saved in string spName(). I created a GroupBox (gBox1) and dragged each cmbPort onto it but I'm not sure how to reference the controls on it. I'm using VB 2015.
Can you help with VB.NET code to use loops ("For Each" or similar) to do this more efficiently?
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cmbPort1.Items.Clear()
...
cmbPort8.Items.Clear()
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort1.Items.Add(sp)
...
cmbPort8.Items.Add(sp)
Next
cmbPort1.Items.Add("Off")
...
cmbPort8.Items.Add("Off")
cmbPort1.Text = spName(1)
...
cmbPort8.Text = spName(8)
End Sub
Loops are an incredibly useful tool to master. I pitched here some code so you can get the idea, but I was working out of IDE so you might have to apply some fixes to this code to make it work as you want it to.
The main idea is that you shouldn't have to write a line more than once. If you multiply the same operation on several lines of code, you create potential problems for the future. Loops and subs are really helpful to prevent this kind of issues.
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'This is for later
Dim cmbIndex As Integer = 1
'You basically do the same operations for every Combobox in your list, son only one loop needed
For Each cmbPort As ComboBox In {cmbPort1, cmbPort2, cmbPort3 [...] cmbPort8} 'this is a way to declare an array
'Clear
cmbPort.Items.Clear()
'Add SerialPortNames
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort.Items.Add(sp)
Next
'Add "Off"
cmbPort.Items.Add("Off")
'This last one is a little bit trickier because you want to match numbers
'This is the place where you get errors when something doesn't go as planned
'If you need to keep it inside the loop here's a way to achieve that, but honestly I would't do that
'Instead I would suggest that you take this part out of the loop and do it manually
If spName(cmbIndex) IsNot Nothing Then cmbPort.Text = spName(cmbIndex)
cmbIndex += 1
Next
End Sub
You shouldn't consider efficiency into this equation, as this operation will not be called all the time, only on load. I mean: you should always do things in the best way you can, but optimization is sometimes the enemy of good, readable code.

How can cancelling DataGridViewCellValidatingEventArgs replace all event handlers with itself?

I'm having a very strange problem in a VB application. I have a function written like this:
In the innermost condition, two statements are commented out here. These were found to have no effect on the strange behaviour. This is the minimal example I've found causing trouble.
(Note that the names of objects have been changed in this example.)
Private Sub MyForm_CellValidating(ByVal sender As Object, ByVal e As DataGridViewCellValidatingEventArgs) Handles myDGV.CellValidating
Dim dgv As DataGridView = CType(sender, DataGridView)
Select Case dgv.Columns(e.ColumnIndex).Name
Case "uniqueColumn"
' Validate that the values in our unique column are unique.
For i As Integer = 0 To dgv.RowCount - 1
If i <> e.RowIndex Then
' Here i != j, so compare to the value...
If e.FormattedValue = dgv.Rows(i).Cells(e.ColumnIndex).FormattedValue Then
e.Cancel = True
'dgv.ShowRowErrors = True
'dgv.Rows(e.RowIndex).ErrorText = "Data in the unique column must be unique"
End If
End If
Next 'i
Case Else
' Perform no validation.
End Select
End Sub
What trouble, you ask? For some inexplicable reason, whenever the line
e.Cancel = True
is executed, afterwards, nearly all buttons and form widgets in the entire application, including even the close button in its window bar (what a user would use to exit the application) stop doing whatever they previously did and now call this event handler instead.
In other words, commenting out that line (and doing the validation manually when the form is submitted) fixes the problems. I'd like to know why this happens, though. Some pointers:
Here's a list of which things are not affected:
The minimize and maximize button in the top bar.
All objects in its menu bar.
This handler is private to its form class, it's not referenced anywhere else in the application.
I'm at a loss. Just how? What could possibly cause this?
e.Cancel is for stopping the validation when the input is deemed incorrect. This causes the cell to still have focus as the user is expected to correct whatever they did wrong. The CellValidating event will then be raised again whenever the cell is about to lose focus until your code deems the input to be correct.
You can use the Control.CausesValidation property to control whether a control (for instance a button) should raise validation events when it gains focus.

Detect rows in DataGridView whose cell values have been changed by user

I have a VB.NET WinForms project that I'm building in VS2013. In a DataGridView bound to a DataSet I want to highlight each row that the user has changed - and if the user changes a row's contents back to its original values (as compared to the database values) I want to remove the highlighting.
I have been Googling for a while now and really haven't gotten anywhere.
All I know at this point is that EmployeesDataSet.HasChanges(DataRowState.Modified) returns False in the CellValueChanged event after having changed text in a cell and clicked out of the row.
My assumption is that the overall method would be something like on KeyUp event compare the current row's cell values to the DataSet (or BindingSource or TableAdapter?) and if anything is different, highlight the row, otherwise set the row to the default backcolor.
But if that's the right approach I don't understand what I would compare the row's contents to. Would it be the DataSet? The TableAdapter? The BindingSource? If it's one of those, how to I compare the correct row?
UPDATE
Some more research has made some progress:
I found this iteration code:
Dim dsChanged As DataSet = EmployeesDataSet.GetChanges()
For Each dt As DataTable In dsChanged.Tables
For Each row As DataRow In dt.Rows
For i As Integer = 0 To dt.Columns.Count - 1
Dim currentBackColor As System.Drawing.Color = dgvEmployees.AlternatingRowsDefaultCellStyle.BackColor
If Not row(i, DataRowVersion.Current).Equals(row(i, DataRowVersion.Original)) Then
dgvEmployees.Rows(dt.Rows.IndexOf(row)).DefaultCellStyle.BackColor = Color.LightPink
Else
' No changes so set it to its original color
dgvEmployees.Rows(dt.Rows.IndexOf(row)).DefaultCellStyle.BackColor = currentBackColor
End If
Next
Next
Next
I put this in a separate Sub, which is being called in the DataGridView.CellValueChanged event.
That correctly detects the rows that have changed cell values, but my code to color the background isn't quite right. As is, it is coloring each successive row from top to bottom as I make changes - regardless of what row in the DGV I edit.
I assumed that dt.Rows.IndexOf(row) would correctly get the correct index of the DGV, since I'm iterating through the DGV's DataTable.
Well, if you go hunting long enough and spend enough time trying different things, you'll eventually find an answer...
Here's the working code I ended up with:
Private Sub dgvEmployees_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgvEmployees.CellValueChanged
' Pass the row and cell indexes to the method so we can change the color of the correct row
CompareDgvToDataSource(e.RowIndex, e.ColumnIndex)
End Sub
Private Sub CompareDgvToDataSource(ByVal rowIndex As Integer, ByVal columnIndex As Integer)
If Not dgvEmployees Is Nothing And dgvEmployees.Rows.Count > 0 Then
' Condition required because this Method is also called when the DGV is being built and populated
Console.WriteLine("rowIndex: " & rowIndex.ToString() & ", columnIndex: " & columnIndex.ToString() & ", cell value: " & dgvEmployees.Rows(rowIndex).Cells(columnIndex).Value.ToString())
End If
' Force ending Edit mode so the last edited value is committed
EmployeesBindingSource.EndEdit()
Dim dsChanged As DataSet = EmployeesDataSet.GetChanges()
If Not dsChanged Is Nothing Then
For Each dt As DataTable In dsChanged.Tables
For Each row As DataRow In dt.Rows
For i As Integer = 0 To dt.Columns.Count - 1
If Not row(i, DataRowVersion.Current).Equals(row(i, DataRowVersion.Original)) Then
Console.WriteLine("Row index: " & dt.Rows.IndexOf(row))
dgvEmployees.Rows(rowIndex).DefaultCellStyle.BackColor = Color.LightPink
End If
Next
Next
Next
End If
End Sub
A couple of notes:
Without calling EndEdit() on the BindingSource the changes won't be detected since this is being called by the CellValueChanged, which happens before the BindingSource is changed.
I tried adding an Else clause to set the BackColor to the original color (for when the DGV row is detected to be the same as the DataSet's row or when validation fails), but I can't figure out how to account for the DGV.AlternatingRowsDefaultCellStyle.BackColor property being set. Ideas???
I think this could be improved by, since I have the row and column indexes in the Method, just going directly to the DataSet's/DataTable's corresponding row and comparing just that, instead of iterating through the entire DataSet. Ideas on that one would be appreciated, but I'll do some more testing to see if I can get it (I figured it out myself this far...)