Datagridview not showing correct rowcount - vb.net

I made a gridview where items are added and a function is called to calculate the amount
after adding a item to list i call
Private Sub totalimsum()
Dim totalsum As Double = 0
Dim totalitem As Double = 0
For i As Integer = 0 To dgvitemlist.Rows.Count - 1
totalsum += dgvitemlist.Rows(i).Cells("Total").Value
'totalitem += dgvitemlist.Rows(i).Cells("qty").Value
Next
Label16.Text = dgvitemlist.Rows.Count.ToString()
amttotal = totalsum.ToString()
txttotal.Text = amttotal
calctotal()
End Sub
it works perfect
but when a row is deleted i again call the function to recalculate the amount but it miscalculate the amount and i found that rowcount is 1 more than row present in the datagridview so i added a new function and calling it but i think it is not a solution
Private Sub totalimsumd()
Dim totalsum As Double = 0
Dim totalitem As Double = 0
For i As Integer = 0 To dgvitemlist.Rows.Count - 2
totalsum += dgvitemlist.Rows(i).Cells("Total").Value
'totalitem += dgvitemlist.Rows(i).Cells("qty").Value
Next
Label16.Text = dgvitemlist.Rows.Count.ToString()
amttotal = totalsum.ToString()
txttotal.Text = amttotal
calctotal()
End Sub

Try rebuilding the data source of the DataGridView which means you reload the contents of the DataGridView.
Or
You may try to use the dgv.update(); and dgv.refresh(); methods whenever you delete a row.

Related

How to freeze merged columns in data grid view when scrolling vertically?

I have a data grid view where I need the columns to be frozen or fixed when scrolling vertically.
I have a data grid view control in vb.net windows application which displays the data in a parent-child hierarchy(as shown below). The first column displays the parent data and the second column displays all its child data. The child data in the second column can be as much as 100 rows or even more. So when scrolling down through the grid, the value in the first column does not remain there as it is while the values in the second column(i.e. the child data) scrolls down. So if the user wants to check to which parent, the current child info belongs to, then again he will have to scroll up to the starting of the column to find the name of the parent. I want the values in the first column to be displayed or frozen till it reaches the end of the list of its child values in the grid or at least till the next row where the next parent data starts. I have suggested the client to go with a tree view but they are not agreeing and need it in a data grid view itself. Is there anyway to achieve this in a data grid view?
Thanks in advance.
You can't freeze a row (in runtime, on dgv scrolling) with index greater than zero because all those before are frozen and at that point you can't scroll your datagridview.
If I understood correctly what you want I wrote this class quickly (probably should be optimized). Usage is simple.
1 - First create your own datagridview.
2 - then add your columns and rows (IMPORTANT: Put a “X” in the Tag in each row is a Parent or is considered as title for other rows as you seen in TestPopulate method) .
3 - Call the class I made by passing the datagridview (you created first) as a parameter. At this point this control takes its size, placement and REPLACE YOUR DATAGRIDVIEW .
Private Class CustomDgv
Inherits Panel
Dim WithEvents TopDgv As DataGridView = New DataGridView
Dim WithEvents DownDgv As DataGridView = New DataGridView
Dim Cols As Integer
' This variable is in case you have more rows as "headrow"
' In TestPopulate you can see how to get those
Dim listOfOwnerRows As List(Of Integer) = New List(Of Integer)
Dim currentTopRow As Integer = -1
Protected Overloads Property Height As Integer
Get
Return MyBase.Height
End Get
Set(value As Integer)
MyBase.Height = value
TopDgv.Height = TopDgv.RowTemplate.Height - 1
DownDgv.Height = value - TopDgv.Height - 1
End Set
End Property
Protected Overloads Property Width As Integer
Get
Return MyBase.Width
End Get
Set(value As Integer)
MyBase.Width = value
TopDgv.Width = value - 1
DownDgv.Width = value - 1
End Set
End Property
Sub New(dgvOriginal As DataGridView)
DownDgv = dgvOriginal
Dim parentCtrl As Control = dgvOriginal.Parent
parentCtrl.Controls.Remove(dgvOriginal)
parentCtrl.Controls.Add(Me)
Me.Location = DownDgv.Location
Me.Size = DownDgv.Size
Me.BorderStyle = DownDgv.BorderStyle
TopDgv.Width = Width - 2 - SystemInformation.VerticalScrollBarWidth
TopDgv.Height = TopDgv.RowTemplate.Height
TopDgv.ScrollBars = ScrollBars.None
TopDgv.ColumnHeadersVisible = False
TopDgv.BorderStyle = BorderStyle.None
DownDgv.ColumnHeadersVisible = False
DownDgv.BorderStyle = BorderStyle.None
TopDgv.Left = 0
DownDgv.Left = 0
DownDgv.Width = Width - 2
DownDgv.Height = Height - 2
For Each Col As DataGridViewColumn In DownDgv.Columns
Dim cIndex As Integer = TopDgv.Columns.Add(Col.Clone)
If Col.Frozen Then
TopDgv.Columns(cIndex).Frozen = True
End If
Cols += 1
Next
DownDgv.Top = 0
Me.Controls.Add(TopDgv)
Me.Controls.Add(DownDgv)
If DownDgv.Rows.Count > 0 Then
listOfOwnerRows = (From R As DataGridViewRow In DownDgv.Rows
Where R.Tag = "X"
Select R.Index).ToList
If listOfOwnerRows.Count > 0 Then
SetFrosenRow(listOfOwnerRows(0))
End If
End If
End Sub
Protected Sub SetFrosenRow(index As Integer)
If DownDgv.Rows.Count > index Then
TopDgv.Rows.Clear()
TopDgv.Rows.Add()
Dim currentRIndex As Integer = DownDgv.FirstDisplayedScrollingRowIndex
'If you want onlly the base row
For i As Integer = 0 To Cols - 1
TopDgv.Rows(0).Cells(i).Value = DownDgv.Rows(index).Cells(i).Value
Next
'Or else get the diplayed on top row
TopDgv.Rows(0).DefaultCellStyle = New DataGridViewCellStyle With {
.BackColor = Color.Bisque
}
currentTopRow = index
End If
End Sub
Protected Sub SetChildValuesInTopRow(index As Integer)
For i As Integer = 1 To Cols - 1
TopDgv.Rows(0).Cells(i).Value = DownDgv.Rows(index).Cells(i).Value
Next
End Sub
Private Sub DownDgv_Scroll(sender As Object, e As ScrollEventArgs) Handles DownDgv.Scroll
Try
If e.ScrollOrientation = ScrollOrientation.VerticalScroll Then
Dim topR As Integer = DownDgv.FirstDisplayedScrollingRowIndex
'If you want in top row the current value that is in the top uncomment this
SetChildValuesInTopRow(topR)
If listOfOwnerRows.Count > 0 Then
Dim rToSetAsOwner As Integer = listOfOwnerRows(listOfOwnerRows.Count - 1)
For i As Integer = listOfOwnerRows.Count - 1 To 0 Step -1
If listOfOwnerRows(i) <= topR Then
rToSetAsOwner = listOfOwnerRows(i)
Exit For
End If
Next
If rToSetAsOwner <> currentTopRow Then
SetFrosenRow(rToSetAsOwner)
End If
Console.WriteLine("rToSetAsOwner: " & rToSetAsOwner)
End If
Else
TopDgv.HorizontalScrollingOffset = DownDgv.HorizontalScrollingOffset
End If
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
End Class
Usage:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Try
' first populate you grid putting a tag in each row which is a header/parent/title for other rows
TestPopulate()
Dim customControl As Control = New CustomDgv(DataGridView1)
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
Sub TestPopulate()
For i As Integer = 0 To 100
DataGridView1.Rows.Add()
If i = 0 Then
DataGridView1.Rows.Item(0).Cells(0).Value = "Owner 0"
DataGridView1.Rows(0).Tag = "X"
End If
If i = 50 Then
DataGridView1.Rows.Item(50).Cells(0).Value = "Owner 50"
DataGridView1.Rows(50).Tag = "X"
End If
If i = 70 Then
DataGridView1.Rows.Item(70).Cells(0).Value = "Owner 70"
DataGridView1.Rows(70).Tag = "X"
End If
DataGridView1.Rows.Item(i).Cells(1).Value = "child_" & i.ToString & "_1"
DataGridView1.Rows.Item(i).Cells(2).Value = "child_" & i.ToString & "_2"
Next
End Sub
I hope I have been helpful

prevent go to next row when duplicated value in `datagridview`

I have datagrid that user will add values in just one column , and i want to prevent duplicated data in this column, i had mange that by the code bellow,
what I want is to keep the selection (focus) in the same editing cell(x)
if the entered data is duplicated, so I tried to get the current row index and return to it if the data is duplicated but its not working.
row_index = DataGridView1.CurrentRow.Index.ToString()
Dim cell As DataGridViewCell = DataGridView1.Rows(row_index).Cells(1)
DataGridView1.CurrentCell = cell
DataGridView1.BeginEdit(True)
NOTE : User will Add barcode number, so he will use barcode scanner or add it manually and press enter key.
Private Sub DataGridView1_RowValidated(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowValidated
If DataGridView1.Rows.Count > 2 Then
Dim i As Integer = 0
Dim row_index As Integer
' loop condition will loop while the row count is less or equal to i
While i <= DataGridView1.Rows.Count - 1
Dim j As Integer = 1
' loop condition will loop while the row count is less or equal to j
While j <= DataGridView1.Rows.Count - 1
Dim str As String = DataGridView1.Rows(i).Cells(1).Value()
Dim str1 As String = DataGridView1.Rows(j).Cells(1).Value()
If Not str1 = "" AndAlso Not str = "" Then
If str1 = str Then
'row_index = DataGridView1.SelectedCells.Item(i).RowIndex.ToString()
row_index = DataGridView1.CurrentRow.Index.ToString()
Dim cell As DataGridViewCell = DataGridView1.Rows(row_index).Cells(1)
DataGridView1.CurrentCell = cell
DataGridView1.BeginEdit(True)
Exit Sub
End If
End If
j += 1
End While
i += 1
End While
End If
End Sub
You can try it also in a for loop. This is how I do it. If what you mean is to stop/retain the selection(focus) or go to in a cell/row whenever it is a duplicate with the current data you have. This should work fine.
enter code here
For i = 0 To Datagridview1.Rows.Count - 1
If Datagridview1.Rows(i).Cells(1).Value = DataYouWillInput Then
DataGridView1.CurrentCell = DataGridView1.Rows(i).Cells(1)
Exit for
End If
Next
enter code here
If your condition returns true with the condition of current data and data you'll be adding. Just exit your loop. Use this.
DataGridView1.CurrentCell = DataGridView1.Rows(i).Cells(1)

How can I put an extra value?

I have a table (DataGridView) like this :
Col1 | Col2 | Col3
3 | Mars | Regular
Here is my code:
For a As Integer = 0 To Form3.DataGridView1.Rows.Count - 1
For b As Integer = 0 To Form3.DataGridView1.Rows.Count - 1
For c As Integer = 0 To Form3.DataGridView1.Rows.Count - 1
If Form3.DataGridView1.Rows(c).Cells(2).Value = "Regular" Then
If Form3.DataGridView1.Rows(b).Cells(1).Value = Form3.MetroComboBox7.Items(0) Then
fair = 7 * Form3.DataGridView1.Rows(a).Cells(0).Value
Label1.Text += fair
End If
End If
Next
Next
Next
I want to set that if Regular is selected on Col3 and Mars on Col2 then the value is 7 and it will multiply by row Col1 and it will be the same every row.
I think you should use the event dtgv.CellMouseClick.
Then you create conditions that you want. I give you an example here :
Public Sub event_select() Handles dtgv.CellMouseClick
Dim row As Integer = dtgv.CurrentRow.Index()
' If the column 2 and 3 are selected
If dtgv.Rows(row).Cells(1).Selected = True And dtgv.Rows(row).Cells(2).Selected = True Then
' If the value of the 2nd column is Mars and the value of the 3rd column is Regular
If dtgv.Rows(row).Cells(1).Value = "Mars" And dtgv.Rows(row).Cells(2).Value = "Regular" Then
Label1.Text = 7 * dtgv.Rows(row).Cells(0).Value
End If
End If
End Sub
You should also check that no other rows have cells selected.
One loop for all rows is enough to calculate "fair" for all rows
Const REGULAR_VALUE As String = "Regular"
Const FAIR_COEFFICENT As Integer = 7
Dim fairSum As Integer = 0
For Each row As DataGridViewRow in DataGridView1.Rows
If REGULAR_VALUE.Equals(row.Cells(2).Value.ToString()) = False Then Continue For
If Equals(MetroComboBox7.Items(0), row.Cells(1).Value) = False Then Continue For
Dim col1Value As Integer = Integer.Parse(row.Cells(1).Value)
Dim fair As Integer = col1Value * FAIR_COEFFICENT
fairSum += fair
Next
Label1.Text = fairSum.ToString()
And set Option Strict On in your project or at least in the code file(first line of file).
This will save you time by giving fast feedback about possible type converting errors during compile time.
Create class which represent your data in strongly typed manner
Public Class Ticket
Public Property Passenger As Integer
Public Property Destination As String
Public Property Status As String
End Class
Then you can add rows to the DataGridView in easy way in your form
Public Class YourForm
Private _tickets As New BindigList(Of Ticket)()
Public Sub New()
InitializeComponent() ' Forms required method
DataGridView1.DataSource = _tickets
End Sub
Private Sub Populate(passenger As Integer, destination As String, Status As String)
Dim newTicket As New Ticket With
{
.Passenger = passenger,
.Destination = destination,
.Status = Status,
}
_ticket.Add(newTicket)
End Sub
'Then you can loop all rows with correct types
Private Sub Calculate()
Dim fairSum As Integer = 0
For Each ticket As Ticket in _tickets
If REGULAR_VALUE.Equals(ticket.Status) = False Then Continue For
If ticket.Destination.Equals(MetroComboBox7.Items(0)) = False Then Continue For
Dim fair As Integer = ticket.Passenger * FAIR_COEFFICENT
fairSum += fair
Next
Label1.Text = fairSum.ToString()
End Sub
End Class

How to track item's index in type list

This may be a simple question, but I'm having a real mental block on it. I'm trying to track the index of a Report in my reportList (of Report type) when navigating forwards and backwards through the list.
I've done this one way by assigning the index of the selected item in a combobox. However I also need to do it directly from the list (already filled), with the only input of the user being the next and previous buttons.
Where I set the Report:
If wantFixture = True Then
ind = UC_Menu_Scout1.cmbSelectedPlayer.SelectedIndex
Else
ind = reportList(0) 'index of item in reportList-
'this doesn't work because reportList is of type Report, not integer
End If
reportList.Clear()
reportList = retrieveReport()
'*****General Information
UC_Menu_Scout1.lblRptPosition.Text = reportList(ind).PositionPlayed
Where the next Report can be navigated to:
Private Sub btnNextPlayer_Click(sender As System.Object, e As System.EventArgs) Handles btnNextPlayer.Click
'Moves to next item in playerList
Dim x As Integer = cmbSelectedPlayer.SelectedIndex
If x = cmbSelectedPlayer.Items.Count - 1 Then
x = 0
Else
x += 1
End If
cmbSelectedPlayer.SelectedIndex = x
lblNumberOfReports.Text = "Report: " & x + 1 & "/" & cmbSelectedPlayer.Items.Count
End Sub
Store the current index and report in variables.
Class level variables:
Private curIndex As Integer = 0
Private curReport As Report = Nothing
Next button:
If Not curIndex = reportList.Count - 1 Then
curIndex += 1
End If
curReport = reportList.ElementAt(curIndex)
Previous button:
If Not curIndex = 0 Then
curIndex -= 1
End If
curReport = reportList.ElementAt(curIndex)

how to calculate the gridview rows and show the answer into text box

i am trying to calculate the gridview rows value and want to show in textbox.
I have 5 rows in gridview , itemid , itemname,orderedqty,price,totalprice. when ever I click add the rows here, I want to calculate total amount of totalprice. want to show it to textbox.
what I was trying this is the code ,I think it will not work. please help me to find solution.
Public Sub Calculate()
Dim Countrow As Integer = 1
Dim AmountTotal As Decimal = 0
For Each row As DataGridView In dgvPurchaseOrder.Rows
Countrow += 1
AmountTotal = dgvPurchaseOrder.Rows.Count.ToString()
Next
'GridView1.FooterRow.Cells(1).Text = "Total = " & AmountTotal.ToString()
txtGrossTotal.Text = AmountTotal.ToString()
End Sub
Try Like This
Public Sub Calculate()
Dim Countrow As Integer = 1
Dim AmountTotal As Decimal = 0
For Each row As DataGridView In dgvPurchaseOrder.Rows
Countrow += 1
AmountTotal += row.Cells(4).Value // give correct price column index
Next
txtGrossTotal.Text = AmountTotal.ToString()
End Sub
Public Sub Calculate()
Dim Countrow As Integer = 1
Dim AmountTotal As Decimal = 0
For Each row As DataGridViewRow In dgvPurchaseOrder.Rows
Countrow += 1
AmountTotal += row.Cells(4).Value
Next
txtGrossTotal.Text = AmountTotal.ToString()
End Sub
#equisde you are the hero, this is the correct code.