Select a specific row from ComboBox and add amount in other Column from same row in DataGridView VB.NET - vb.net

I'm using VB.NET for a small project. I have a DataGridView with some rows and columns.
I want to send amounts inside a specific row & column. For this purpose I'm using a ComboBox to select the receiver, but I cant manage to select the specific row & column for the amount.
Here is a photo of the form:
[
I managed to add the receivers inside the ComboBox and to add an amount, but in all rows.
This is code (it auto generates the rows for now).
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim rowId As Integer = Me.DataGridView1.Rows.Add()
Dim row As DataGridViewRow = Me.DataGridView1.Rows(rowId)
row.Cells("Column1").Value = "UNITED BANK"
row.Cells("Column2").Value = "1000"
row.Cells("Column3").Value = "ExampleInfo"
Dim rowId2 As Integer = Me.DataGridView1.Rows.Add()
Dim row2 As DataGridViewRow = Me.DataGridView1.Rows(rowId2)
row2.Cells("Column1").Value = "FREE BANK"
row2.Cells("Column2").Value = "2000"
row2.Cells("Column3").Value = "ExampleInfo"
Dim bolAdd As Boolean = False
Dim strValue As String
For Each myRow As DataGridViewRow In Me.DataGridView1.Rows
bolAdd = True
strValue = myRow.Cells("Column1").Value
For Each myItem As String In Me.ComboBox1.Items
If myItem = strValue Then bolAdd = False
Next
If bolAdd AndAlso Not (strValue Is Nothing) Then Me.ComboBox1.Items.Add(strValue)
Next
If Me.ComboBox1.SelectedIndex = -1 Then
Me.ComboBox1.SelectedIndex = 0
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For Each rowX As DataGridViewRow In Me.DataGridView1.Rows
rowX.Cells(1).Value = Val(rowX.Cells(1).Value) + Val(TextBox1.Text)
Next
End Sub
End Class
With this code, I'm able to add an amount, but to all rows.
I want to add an amount only to the row I selected in the ComboBox.

Your current code in the Button1_Click event is adding the amount from the text box to ALL the rows in the grid regardless of “what” value is selected in the combo box.
From what you describe, you want the amount from the text box to be added to rows that “match” the value in the combo box. The current code is never checking this, so the value from the text box is added to all the rows in the grid.
Therefore, the code needs to check to see if the value in Column1 "Receiver" “matches” the value in the combo box. If it DOES match, then add the value from the text box to the current value in Column2 "USD."
It is unclear if there may be more rows in the grid that have the same Column1 "Receiver" value and if you want to update those values also. I am assuming that there will only be ONE row in the grid that “matches” the value in the combo box. Therefore, once we find this value and add the amount, then we are done and the code will return without looping through the rest of the rows in the grid.
So is what you need to do is alter the code in the button click event to do this checking as described above.
There are a lot of checks we need to make. We need to check that the combo box has a value to compare to. Also, before we check ANY cell value, we need to make sure the cells value is not null BEFORE we try and call the cell's Value ToString method.
In addition, the cell MAY have a value, however, it may not be a number, so we have to check and make sure the value in the cell is an actual number and the same would apply to the text box.
So, walking through the code below would go something like…
If (Int32.TryParse(TextBox1.Text, addedValue)) … checks to see if
the text box amount is a valid number.
If (Not String.IsNullOrEmpty(targetComboValue)) … checks to make
sure the combo box value is not empty.
Then we start the loop through all the rows in the grid.
If (Not rowX.IsNewRow) … checks to see if the row is the “new” row
which we will ignore this “new” row.
If (rowX.Cells("Column1").Value IsNot Nothing) … checks to make
sure the “Receiver” cell is not null
If (rowX.Cells("Column1").Value.ToString() = targetComboValue) …
checks to see if the “Receiver” cells value matches the value in the
combo box.
If (rowX.Cells("Column2").Value IsNot Nothing) … check to make sure
the “USD” cell is not null.
And Finally,…
If (Int32.TryParse(rowX.Cells("Column2").Value.ToString(), currentValue)) … check to see if the value in the “USD” cell is a valid number.
If all these checks succeed then add the value from the text box to the current value in the “USD” cell.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim targetComboValue = ComboBox1.SelectedItem
Dim addedValue As Int32
Dim currentValue As Int32
If (Int32.TryParse(TextBox1.Text, addedValue)) Then
If (Not String.IsNullOrEmpty(targetComboValue)) Then
For Each rowX As DataGridViewRow In Me.DataGridView1.Rows
If (Not rowX.IsNewRow) Then
If (rowX.Cells("Column1").Value IsNot Nothing) Then
If (rowX.Cells("Column1").Value.ToString() = targetComboValue) Then
If (rowX.Cells("Column2").Value IsNot Nothing) Then
If (Int32.TryParse(rowX.Cells("Column2").Value.ToString(), currentValue)) Then
rowX.Cells("Column2").Value = currentValue + addedValue
Return
End If
End If
End If
End If
End If
Next
End If
End If
End Sub
I hope this makes sense.

You can edit the desired row just by using a syntax as follow:
Me.DataGridView1.Rows(1).Cells(2).Value = "your string"
So if you want to select corresponding to your combo index :
Dim myId As Integer = Me.ComboBox1.SelectedIndex
Me.DataGridView1.Rows(myId).Cells(2).Value = "your string"
But this assuming that the count of combo and DataGridView are same.
If not you should think about to add an id Column in your DataGridView ...
If you mean something else please explain, I will edit.

Related

Datagridview DataTable edit, update and show at a time VB.net

i made a data table like this :
Dim DTCart As New DataTable
DTCart.Columns.Add(New DataColumn With {.ColumnName = "Code", .DataType = GetType(String)})
DTCart.Columns.Add(New DataColumn With {.ColumnName = "Name", .DataType = GetType(String)})
DTCart.Columns.Add(New DataColumn With {.ColumnName = "Amount", .DataType = GetType(Double)})
DTCart.Columns.Add(New DataColumn With {.ColumnName = "Price", .DataType = GetType(Long)})
DTCart.Columns.Add(New DataColumn With {.ColumnName = "Total", .DataType = GetType(Long), .Expression = "Amount*Price"})
DTCart.Columns("Code").ReadOnly = True
DTCart.Columns("Name").ReadOnly = True
DTCart.Columns("Amount").ReadOnly = False
DTCart.Columns("Price").ReadOnly = True
DTCart.Columns("Total").ReadOnly = True
Then i show the data table to Datagridview using DataGridView.DataSource = DTCart
i set the 'Amount' Column to be editable so then i can update it by editing the cell in Datagridview like this :
Private Sub DataGridViewCart_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridViewCart.CellEndEdit
If DataGridViewCart.CurrentRow.Cells(2).Value.ToString = "" Then
DataGridViewCart.CurrentRow.Cells(2).Value = 0
End If
Dim FindRow() As DataRow = DTCart.Select("Code='" & DataGridViewCart.CurrentRow.Cells(0).Value & "'")
FindRow(0)("Amount") = DataGridViewCart.CurrentRow.Cells(2).Value
Total()
End Sub
The 'Total()' method is just a method to sum 'Total' Column on DataTable so i can set a specific label's text to its value like this :
Sub Total()
Dim sum As Double = 0
For Each row As DataRow In DTCart.Rows
sum += row.Item("Total")
Next
LabelTotal.Text = sum
End Sub
Now, the problem is, when i finish editing the cell, the DataTable is Updated (i checked), but the LabelTotal.Text isn't.
The funny thing is this problem comes only the first time i edit the cell.
When i tried to edit the same cell a second time it runs perfectly, everything is updated.
Help.. :'(
sorry for my bad english
I question your comment that…
”When i tried to edit the same cell a second time it runs perfectly,
everything is updated.” … ?
In my small tests... the sum of the “Total” column always reflected the “previous” total and NOT the actual total. In other words, the LabelTotal always missed the last change.
The reason for this is because when the code “sums” the tables “Total” column, it is when a value in the Amount column CHANGES (… note … the event will not fire when the “expression” column is updated). Therefore when the user changes an Amount value in the grid, the grid’s CellEndEdit event fires and sums the Total column and places that value into the label… HOWEVER since the Total column is an “Expression” column, its value has not yet been updated and will NOT be updated until AFTER the grid’s CellEndEdit has exited… therefore the last “Amount” change is not calculated in the sum.
I suggest you drop the Total method that loops through each row and sums the values and instead let the DataTable do this work for you using its Compute method to sum the Total column. This may simplify the code; however it will not solve the previous issue.
In order to keep the label updated with the correct sum of the Total column, I suggest you call the tables AcceptChanges() method BEFORE calling the tables compute method. Something like…
Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridViewCart.CellEndEdit
If DataGridViewCart.CurrentRow.Cells(2).Value.ToString = "" Then
DataGridViewCart.CurrentRow.Cells(2).Value = 0
End If
DTCart.AcceptChanges()
LabelTotal.Text = DTCart.Compute("SUM(Total)", "").ToString()
End Sub
Lastly, I agree with Mary’s comment that you should change the Amount, Price and Total columns to Decimal. Otherwise, you will lose fractional parts.
Edit… Getting the correct sum without calling the data tables AcceptChanges() method…
After further review, it is possible that you may not want to call the data tables AcceptChanges event. An example is commented in addition to the possible situation where you may be tracking the changes made. In that case, you could instead wire up the DataTable.RowChanged event. It will fire AFTER the expression column has been updated so the SUM should be correct. This may look something like…
AddHandler DTCart.RowChanged, AddressOf DTCart_RowChanged
Add the line above to the Forms Load event... and below the event...
Private Sub DTCart_RowChanged(sender As Object, e As DataRowChangeEventArgs)
LabelTotal.Text = DTCart.Compute("SUM(Total)", "").ToString()
End Sub

Delete a row in DataDridView when a value of NumericUpDown equal to zero

For Each row As DataGridViewRow In DataGridView1.Rows
If row.Cells(0).Value = "Hawaiian" Then
row.Cells(1).Value = Double.Parse(NumericUpDown1.Value)
row.Cells(2).Value = Double.Parse(row.Cells(1).Value) * price
Exit Sub
End If
Next
DataGridView1.Rows.Add("Hawaiian", 1, price)
the code I put in NumericUpDown to add the item to DataGridView1 is above
I want the row to be deleted when Qty or Amount equal to zero, in this case, is the row for Hawaiian be delete
If NumericUpDown1.Value = 0 Then
DataGridView1.Rows.RemoveAt(DataGridView1.CurrentRow.Index)
End If
I tried this but it only deleted the first row in DataGridView1. Please help me if you know the answer, I really appreciate it. I have been trying to solve this for a week, used so many methods online all not work, I still have no clue.
Expand: the code I added from the comment, it's not deleting
The code
You are making this far more complicated than it has to be. To simplify things, I suggest you create a method that takes three (3) parameters; a string pizza name, a decimal quantity and a decimal price.
Inside this method, the first step is to check to see if quantity is zero. If it is zero, then loop through all the rows in the grid until we find the pizza name. If the pizza name is NOT found then we can return as the row is not there to begin with. If the row IS found, then we will delete that row.
If the quantity is NOT zero, then we will loop through all the rows in the grid until we find the pizza name. If the pizza name is NOT found, then we know we want to “add” the row to the grid. If the pizza name IS found, then update the quantity and amount cells.
This method may look something like…
Private Sub AddOrRemovePizzaInGrid(targetPizza As String, quantity As Decimal, price As Decimal)
If quantity = 0 Then
For Each row As DataGridViewRow In DataGridView1.Rows
If row.Cells("Item").Value IsNot Nothing Then
If row.Cells("Item").Value.ToString().Equals(targetPizza) Then
DataGridView1.Rows.Remove(row)
Return
End If
End If
Next
Return
End If
' quantity is not zero
For Each row As DataGridViewRow In DataGridView1.Rows
If Not row.IsNewRow Then
If row.Cells("Item").Value IsNot Nothing Then
If row.Cells("Item").Value.ToString().Equals(targetPizza) Then
row.Cells("Qty").Value = quantity
row.Cells("Amount").Value = quantity * price
Return
End If
End If
End If
Next
' if we get here - the target pizza was not found so add as new row
DataGridView1.Rows.Add(targetPizza, quantity, quantity * price)
End Sub
In addition, since there are numerous NUD (NumericUpDown) controls on the form and each NUD is tied to a specific pizza, then instead of having a ValueChanged event for “each” NUD, I suggest you create ONE (1) ValueChanged event and have “each” NUD subscribe to this same event. When the event fires, we will check to see “which” NUD is changing its value, then simply call the above method with the proper info. This should reduce the number of events you have to manage down to one ValueChanged event for all the NUDs.
To make the code more readable I suggest you give each NUD a more logical name like NUD_Pepperoni, NUD_Hawaiian etc… as we will use the NUDs Name property to identify “which” NUDs value has changed. This event that uses the above method may look something like…
Private Sub NUD_ValueChanged(sender As Object, e As EventArgs) Handles NUD_Pepperoni.ValueChanged, NUD_Hawaiian.ValueChanged, NUD_Americano.ValueChanged
Dim target_NUD As NumericUpDown = CType(sender, NumericUpDown)
Select Case target_NUD.Name
Case "NUD_Pepperoni"
AddOrRemovePizzaInGrid("Pepperoni", target_NUD.Value, 8.5D)
Case "NUD_Hawaiian"
AddOrRemovePizzaInGrid("Hawaiian", target_NUD.Value, 8.5D)
Case "NUD_Americano"
AddOrRemovePizzaInGrid("Americano", target_NUD.Value, 8.5D)
' add more pizza types here
End Select
End Sub
According to the screenshot I think that you want to delete the item row from the DataGridView when its quantity set to zero, if so:
You have first to look to the row index in DGV then delete it by doing this:
Make a sub to find the related row & delete it
Call this sub from ValueChanged event of the Numeric object, like this:
Private Sub NumericUpDown1_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown1.ValueChanged
If NumericUpDown1.Value = 0 Then
DeleteRowFromDGV("Hawaiian")
End If
End Sub
Private Sub DeleteRowFromDGV(itemName As String)
With DataGridView1
Dim rowToDelete As Short
For i As Short = 0 To .RowCount - 1
If .Item(0, i).Value = itemName Then 'suppose that the column index of item name is 0
rowToDelete = i
Exit For
End If
Next
.Rows.RemoveAt(rowToDelete)
End With
End Sub

ArgumentOutOfRangeException on Datagridview with 1 row

I have a strange problem. I have a form with on it 2 unbounded datagridviews and 2 buttons. With the buttons I switch the rows from 1 datagrid to the other.
In the beginning the left datagrid is filled with a number of rows and the right datagrid is empty. So when I click on the button "Add" the selected row from the left datagrid is removed and added to the right datagrid. With the button "Delete" the selected row of the right datagrid is added back to the left datagrid.
When there is only one row in the right datagrid, and I select it to "delete" it, the row is removed from the right datagrid and added to the left without an exception. Now I have a situation where there is only one row in the left datagrid and when I click "Add" to move it to the right datagrid I get an ArgumentOutOfRangeException (index is out of bounds...)
Below is the code which throws the exception
For i As Integer = DgvLeft.SelectedRows.Count - 1 To 0 Step -1
ind = DgvLeft.SelectedRows(i).Index
If ind > 0 Then
DgvLeft.Rows.RemoveAt(ind)
Else
DgvLeft.Rows.Remove(DgvLeft.SelectedRows(i))
End If
Next
So I store the row index in a variable. The first time I used the RemoveAt function and the exception is thrown. To resolve this I added the If-structure and tried the Remove function. But again the exception is thrown.
I don't understand why the exception is thrown. I use the same code for the "delete" button and there it doesn't happen. Also when I store the RowIndex in a variable the index is known, but not when I try to Remove the row.
Can someone help me with this strange problem?
Since the posted for loop is “broken”, I feel it is unnecessary to question what you are trying to accomplish here. However, given what you described where there are two grids and two buttons (Add/Delete) on a form. When the “Add” button is clicked, it moves the “selected rows” from the “left” grid to the “right” grid, and then deletes the “selected rows” rows from the “left” grid. If the “Delete” button is clicked, then the opposite process happens moving the “selected rows” from the right grid to the left grid, then delete the selected rows from the right grid.
If this is correct, then it appears you are making this more complicated than it has to be. This would be much easier if you used a data source of some form. However, to break the above problem down, it appears that two methods may come in handy for what you want to do. The first one could simply add the selected rows from one given grid to another given grid. The next method would simply delete the selected rows from a given grid. With these two methods implemented, the “add” button would be…
AddSelectedRows(dgvLeft, dgvRight)
RemoveSelectedRows(dgvLeft)
The delete button would be…
AddSelectedRows(dgvRight, dgvLeft)
RemoveSelectedRows(dgvRight)
Before I explain the code below, it should be noted that your current posted code is making a huge programming “no-no” and without question is a problem for you. As Mary and others have pointed out, “changing the value of the counter variable in a loop or (as the code does) changing the collection it is looping through is rarely if ever done.”
Try the code below it may make things easier,
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddColumns(dgvLeft)
AddColumns(dgvRight)
FillGrid(dgvLeft)
End Sub
Private Sub FillGrid(dgv As DataGridView)
For i = 0 To 15
dgv.Rows.Add("C0R" + i.ToString(), "C1R" + i.ToString(), "C2R" + i.ToString())
Next
End Sub
Private Sub AddColumns(dgv As DataGridView)
Dim txtCol = New DataGridViewTextBoxColumn()
txtCol.Name = "Col0"
txtCol.HeaderText = "Col 0"
dgv.Columns.Add(txtCol)
txtCol = New DataGridViewTextBoxColumn()
txtCol.Name = "Col1"
txtCol.HeaderText = "Col 1"
dgv.Columns.Add(txtCol)
txtCol = New DataGridViewTextBoxColumn()
txtCol.Name = "Col2"
txtCol.HeaderText = "Col 2"
dgv.Columns.Add(txtCol)
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
AddSelectedRows(dgvLeft, dgvRight)
RemoveSelectedRows(dgvLeft)
End Sub
Private Sub btnDelete_Click(sender As Object, e As EventArgs) Handles btnDelete.Click
AddSelectedRows(dgvRight, dgvLeft)
RemoveSelectedRows(dgvRight)
End Sub
Private Sub RemoveSelectedRows(dgv As DataGridView)
Dim totalRowsToDelete = dgv.SelectedRows.Count
Dim selectedRow As DataGridViewRow
For i = totalRowsToDelete - 1 To 0 Step -1
selectedRow = dgv.SelectedRows(i)
If (Not selectedRow.IsNewRow) Then
dgv.Rows.RemoveAt(dgv.SelectedRows(i).Index)
End If
Next
End Sub
Private Sub AddSelectedRows(sourceDGV As DataGridView, destinationDGV As DataGridView)
Dim selectedRowCount = sourceDGV.Rows.GetRowCount(DataGridViewElementStates.Selected)
If (selectedRowCount > 0) Then
Dim selectedRow As DataGridViewRow
For i = 0 To selectedRowCount - 1
selectedRow = sourceDGV.SelectedRows(i)
If (Not selectedRow.IsNewRow) Then
destinationDGV.Rows.Add(selectedRow.Cells(0).Value.ToString(),
selectedRow.Cells(1).Value.ToString(),
selectedRow.Cells(2).Value.ToString())
End If
Next
End If
End Sub
You are changing the value of DgvLeft.SelectedRows.Count in your For loop.
Try...
Dim RowCount As Integer = DgvLeft.SelectedRows.Count
For i As Integer = RowCount - 1 To 0 Step -1
I want to thank Mary for her answer, because that made me think about my code. I looked at the code of the "delete" button, where I use the same for loop, and that code doesn't throw an exception. So there must be a difference between the code of my two buttons.
This is an expanded piece of my code in the "add" button:
For i As Integer = DgvLeft.SelectedRows.Count - 1 To 0 Step -1
'fill the array with the row values
For j As Integer = 0 To DgvLeft.Columns.Count - 1
fields(j) = DgvLeft.SelectedRows(i).Cells(DgvLeft.Columns(j).Name).Value
Next
'delete the row in datagrid Left
ind = DgvLeft.SelectedRows(i).Index
If ind > 0 Then
DgvLeft.Rows.RemoveAt(ind)
Else
DgvLeft.Rows.Remove(DgvLeft.SelectedRows(i))
End If
'add the row in datagrid Right
DgvRight.Rows.Add(fields)
Next
In the code snippet above I attempt to remove a row BEFORE I add the array of values from this row to the other datagrid. When I looked at the code of the "delete" button I attempt to remove the row AFTER I add the array of values to the other datagrid.
So I moved this piece of code below the code of adding the array of values and it worked. Probably the index exception is thrown by the array rather than the datagridview.

Check if a Range in Datagridview contains an "," if yes change it to ":"

My application contains a Datagridview, the user has the possibility to enter values like: Day, Time, how many hours did he work etc. The problem is that my second application calculates with this data. It has to be in a certain format.
Like time should be "09:15", but i noticed that some users are using "09,15" instead. Can you help me guys, I need a code that can check if a Range in Datagridview contains some " blacklisted char" and if yes, replaces it with the right one.
Thanks for all.
Do not save values as a string.
Validate input string straight in the needed type.
Then validated values pass to the second application.
In this case you don't need "blacklisted chars"
Private Sub DataGridView_CellValidating(sender As Object,
e As DataGridViewCellValidatingEventArgs)
If e.RowIndex < 0 Then Exit sub
Dim dgv As DataGridView = DirectCast(sender, DataGridView)
Dim columnIndexWithInteger As Integer = 2
Dim columnIndexWithDate As Integer = 3
Select Case e.ColumnIndex
Case columnIndexWithInteger
Dim temp As Integer
If Integer.TryParse(e.FormattedValue, temp) = False Then
e.Cancel = True
End If
Case columnIndexWithDate
Dim temp As Date
If Date.TryParse(e.FormattedValue, temp) = False Then
e.Cancel = True
End If
End Select
End Sub
In DataGridView, you have one handle that allows you to check the validity of an edited cell : CellValueValidating. It is called before the user change is taken into account (CellValueChanged event).
You can find example and explanation here :
- https://msdn.microsoft.com/en-us/library/ykdxa0bc(v=vs.110).aspx
- https://msdn.microsoft.com/en-us/library/7ehy30d4(v=vs.110).aspx
You can also have a look at CellValueChanged, CellValueValidated and CellEndEdit events.

Getting the column names of the selected row in SQL?

I have a program with 2 forms and both forms have their own grid. Grid1 is on form1, Grid2 is on form2. I want Grid2 to serve as a pop up window which is used for editing. When a user doubleclicks a row on Grid1, it should do a requery on just that row and display that single row on Grid2. I can't figure out how to get the column names of the selectedrow then do a query on it's unique identifers. What I have so far:
(This is on form1 by the way)
Public Sub dgvForm1_CellDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvForm1.CellDoubleClick
Dim i As Integer = 0
Dim rowIndex As Integer
Dim cellName as string
While dgvForm1.SelectedRows.Item(0).Cells(i).ColumnIndex < dgvForm1.ColumnCount
rowIndex = dgvForm1.SelectedRows.Item(0).HeaderCell.RowIndex
cellName = dgvForm1.SelectedRows.Item(0).HeaderCell.toString
Select Case cellName
Case "control_no"
Dim sControlNum = cellName.ToString
Case "store_id"
Dim sStoreNum = cellName.ToString
End Select
i = i + 1
End While
end sub
I simply just want the user to doubleclick a row that is displayed on a datagridview with many other records, and that triggers a doubleclick event. This would then loop through the selectedrow's column names until it finds both control_id and store_id and gets their values. P.s. I tried google for a good hour or so but I had a hard time getting any techniques to work.
UPDATE:
I should probably add, this is a bound datagrid that uses sqlClient and its dataadapter/dataset method.
myTable.Columns.ColumnName.
Read more at http://msdn.microsoft.com/en-us/library/system.data.datacolumn.columnname.aspx