Adding new row to unbound datagridview - vb.net

I have an unbound datagridview. Because of the various things I am doing with the data in the grid I do not want to bind it. The columns are predefined in the settings (Edit Columns) of the datagridview.
I want to create a new row and then populate the grid row with data. I am trying to use the .Add.Rows method but it is failing with
{"Index was out of range. Must be non-negative and less than the size of the collection." & vbCrLf & "Parameter name: index"}
The following SQL retrieves data:
USE CCAP
declare #ScheduleName as varchar(30) = 'Walk-In Center April Wk 1 2019'
Select ShiftName, ScheduleStart, ScheduleEnd, Position, ADP_ID1,
Name1,ADP_ID2, Name2, ADP_ID3, Name3, ADP_ID4, Name4, ADP_ID5,
Name5, ADP_ID6, Name6, ADP_ID7, Name7
from FormattedSchedules
where ScheduleName = #ScheduleName;
and the rowcount is greater than 0 so it is getting results.
I do not understand what index is out of range or why the collection is 0
Code is below:
Tried .Rows.Add(1) and .Rows.Add() and .Rows.Add("")
Dim FSchedCmd As SqlCommand
Dim FSchedSQL As String
Dim FSchedConn As New SqlConnection()
Dim FSchedadapter As New SqlDataAdapter()
Dim i As Integer = 0
Dim rowIndex As Integer
Dim row As DataGridViewRow
AddedNewRow = 1
Dim dsFSched As New DataSet()
FSchedSQL = "Select ShiftName, ScheduleStart, ScheduleEnd, Position, ADP_ID1, Name1, ADP_ID2, Name2, ADP_ID3, Name3, ADP_ID4, Name4, ADP_ID5, Name5, ADP_ID6, Name6, ADP_ID7, Name7 from FormattedSchedules where ScheduleName = #ScheduleName;"
Try
If GlobalVariables.logProd = 1 Then
GlobalVariables.strConnection = "CCAPProdConnectionString"
Else
GlobalVariables.strConnection = "CCAPTestConnectionString"
End If
FSchedConn.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings(GlobalVariables.strConnection).ConnectionString
FSchedConn.Open()
FSchedCmd = New SqlCommand(FSchedSQL, FSchedConn)
FSchedCmd.Parameters.Add("#ScheduleName", SqlDbType.VarChar).Value = cboCreateScheduleName.Text
FSchedadapter.SelectCommand = FSchedCmd
FSchedadapter.Fill(dsFSched)
FSchedadapter.Dispose()
FSchedCmd.Dispose()
FSchedConn.Close()
'dgvCreateSchedule.DataSource = dsFSched.Tables(0)
dgvCreateSchedule.Rows.Clear()
With dgvCreateSchedule
Dim RowNo As Long = 0
'.RowCount = 0
While RowNo <= dsFSched.Tables(0).Rows.Count - 1
.Rows.Add(1)
.Rows(RowNo).Cells(0).Value = dsFSched.Tables(0).Rows(RowNo).Item(0) 'ShiftName
'.Rows(RowNo).Cells(1).Value = dsFSched.Tables(0).Rows(RowNo).Item(1) 'Start Time
.Rows(RowNo).Cells(1).Value = Convert.ToDateTime(dsFSched.Tables(0).Rows(RowNo).Item(1)).TimeOfDay
'.Rows(RowNo).Cells(2).Value = dsFSched.Tables(0).Rows(RowNo).Item(2) 'End Time
.Rows(RowNo).Cells(2).Value = Convert.ToDateTime(dsFSched.Tables(0).Rows(RowNo).Item(2)).TimeOfDay 'End Time
.Rows(RowNo).Cells(3).Value = dsFSched.Tables(0).Rows(RowNo).Item(3) 'Position
.Rows(RowNo).Cells(4).Value = dsFSched.Tables(0).Rows(RowNo).Item(4) 'ADP_ID1
.Rows(RowNo).Cells(5).Value = dsFSched.Tables(0).Rows(RowNo).Item(5) 'Name1
.Rows(RowNo).Cells(6).Value = dsFSched.Tables(0).Rows(RowNo).Item(6) 'ADP_ID2
.Rows(RowNo).Cells(7).Value = dsFSched.Tables(0).Rows(RowNo).Item(7) 'Name2
.Rows(RowNo).Cells(8).Value = dsFSched.Tables(0).Rows(RowNo).Item(8) 'ADP_ID3
.Rows(RowNo).Cells(9).Value = dsFSched.Tables(0).Rows(RowNo).Item(9) 'Name3
.Rows(RowNo).Cells(10).Value = dsFSched.Tables(0).Rows(RowNo).Item(10) 'ADP_ID4
.Rows(RowNo).Cells(11).Value = dsFSched.Tables(0).Rows(RowNo).Item(11) 'Name4
.Rows(RowNo).Cells(12).Value = dsFSched.Tables(0).Rows(RowNo).Item(12) 'ADP_ID5
.Rows(RowNo).Cells(13).Value = dsFSched.Tables(0).Rows(RowNo).Item(13) 'Name5
.Rows(RowNo).Cells(14).Value = dsFSched.Tables(0).Rows(RowNo).Item(14) 'ADP_ID6
.Rows(RowNo).Cells(15).Value = dsFSched.Tables(0).Rows(RowNo).Item(15) 'Name6
.Rows(RowNo).Cells(16).Value = dsFSched.Tables(0).Rows(RowNo).Item(16) 'ADP_ID7
.Rows(RowNo).Cells(17).Value = dsFSched.Tables(0).Rows(RowNo).Item(17) 'Name7
RowNo = RowNo + 1
End While
End With
If dgvCreateSchedule.RowCount > 0 Then
dgvCreateSchedule.Rows(0).Selected = True
dgvCreateSchedule.CurrentCell = dgvCreateSchedule.Rows(0).Cells(0)
'dgvCreateSchedule.FirstDisplayedScrollingRowIndex = dgvCreateSchedule.CurrentRow.Index
End If
Catch ex As Exception
MessageBox.Show("Cannot open FormattedSchedules to load grid")
End Try
AddedNewRow = 0
Error message from line: .Rows.Add(1)
Index was out of range. Must be non-negative and less than the size of the collection." & vbCrLf & "Parameter name: index

This should be the fastest option:
dgvCreateSchedule.Rows.Clear()
For Each xrow As DataRow In TempDataTable.dsFSched.Tables(0).Rows
dgvCreateSchedule.Rows.Add(xrow.ItemArray)
Next
What it does adds all "Cells" along with row.
And when editing cells, I prefer to use
dgvCreateSchedule(y,x).Value = somevalue
'Though it's a little bit strange, as it's column first then row for location hence y then x axis , opposed to usual row then column thats x then y axis

Add it like this, assuming there is the same column count/order
.Rows.Add(dsFSched.Tables(0).Rows(RowNo).ItemArray)

I changed the name of the DGV to DataGridView1 because that is what I happened to have in my test project.
You can use conditional compile statements to chose the correct connection string. Not necessary to keep a Boolean variable somewhere to determine correct string. I know I would forget to change it for the release version.
You did a good job closing and disposing of database objects but if there is an error all that good work will be for naught. A Using...End Using block will accomplish the close, dispose even if there is an error.
Pass the connection string directly to the constructor of the connection and pass the Sql statement and the connection directly to the constructor of the command.
Don't open your connection until the last minute. In the case of a DataAdapter.Fill, the connection is opened and closed for you however, if the adapter finds and open connection it leaves it open. In this case there is no need for an adapter or a DataSet.
I do not see anything wrong with your line .Rows.Add(1). The problem comes on the next line. The index of DataGridView.Rows is an Int32, Integer in vb.net, and you have declared RowNo as Long. Of course you will want to use the code suggested by #CruleD answer.
Private Sub OPCode()
Dim dt As New DataTable
Dim FSchedSQL = "Select ShiftName, ScheduleStart, ScheduleEnd, Position, ADP_ID1, Name1, ADP_ID2, Name2, ADP_ID3, Name3, ADP_ID4, Name4, ADP_ID5, Name5, ADP_ID6, Name6, ADP_ID7, Name7 from FormattedSchedules where ScheduleName = #ScheduleName;"
Try
#If Not DEBUG Then
GlobalVariables.strConnection = "CCAPProdConnectionString"
#Else
GlobalVariables.strConnection = "CCAPTestConnectionString"
#End If
Using FSchedConn As New SqlConnection(ConfigurationManager.ConnectionStrings(GlobalVariables.strConnection).ConnectionString)
Using FSchedCmd As New SqlCommand(FSchedSQL, FSchedConn)
FSchedCmd.Parameters.Add("#ScheduleName", SqlDbType.VarChar).Value = cboCreateScheduleName.Text
FSchedConn.Open()
dt.Load(FSchedCmd.ExecuteReader)
End Using
End Using
DataGridView1.Rows.Clear()
For Each xrow As DataRow In dt.Rows
DataGridView1.Rows.Add(xrow.ItemArray)
Next
If DataGridView1.RowCount > 0 Then
DataGridView1.Rows(0).Selected = True
DataGridView1.CurrentCell = DataGridView1.Rows(0).Cells(0)
End If
Catch ex As Exception
MessageBox.Show("Error loading grid")
End Try
End Sub

Related

Datagridview SelectedRows index error | Access and VB.NET

I have an error in my software. My form contains a DataGridView connected with MS_access database.
When I click on a row in the DataGridView, I get this error:
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
My code:
Try
If (DataGridView1.Rows.Count <= 0) Then Return
FlatTextBox1.Text = String.Empty
FlatTextBox2.Text = String.Empty
FlatTextBox3.Text = String.Empty
RichTextBox1.Text = String.Empty
Dim indx As String = DataGridView1(1, DataGridView1.SelectedRows(0).Index).Value.ToString
Dim dt As DataTable = New DBConnect().selectdata(String.Format("SELECT famille3.Article, famille3.quantité, famille3.prixch, famille3.prixvn, famille3.dateex, famille3.description FROM famille3 where famille3.ID = {0} ", indx))
If dt.Rows.Count > 0 Then
FlatTextBox1.Text = dt.Rows(0)(0).ToString
FlatTextBox2.Text = dt.Rows(0)(1).ToString
FlatTextBox3.Text = dt.Rows(0)(2).ToString
FlatTextBox9.Text = dt.Rows(0)(3).ToString
FlatTextBox10.Text = (dt.Rows(0)(3).ToString - dt.Rows(0)(2).ToString) * dt.Rows(0)(1).ToString
FlatTextBox11.Text = dt.Rows(0)(4).ToString
RichTextBox1.Text = dt.Rows(0)(5).ToString
End If
dt.Dispose()
dt = Nothing
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
I think the stack is here
Dim indx As String = DataGridView1(1, DataGridView1.SelectedRows(0).Index).Value.ToString
How can I resolve it?
In a CellClick event of a DataGridView you receive a parameter of type DataGridViewCellEventArgs. This parameter contains the ColumnIndex and the RowIndex of the clicked cell. So getting the value of the column 1 of the clicked row is really simple as
Sub DataGridView1_CellClick(sender as Object,e As DataGridViewCellEventArgs)
Dim indx As Integer = Convert.ToInt32(DataGridView1(1, e.RowIndex).Value)
.....
Notice that I have applied a Convert.ToInt32 to the value of the first column. I assume that this is the numeric primary key to find your record. Remember that VB.NET allows you to freely convert string to numbers (VB6 compatibility) but in general this is considered a bad practice. Check your project properties and set Option Strict On instead and you will be forced to improve your code.
Another advice that I feel to give is to not write query in that way. This method is called sql string concatenation and it is the root of Sql Injection. (In your current case there is no big risk, but better to take good habits and always use parameterized queries)

Deleting items in listview if data already exists

i'am populating my listview and when i click the command button the data will be saved to database but if a data already exists in the database the data will be removed from my listview. my problem is that when the data already exists yes it removes the data but it always leave 1 data in my listview. for example i populate my list with 10 data that already exist in database and when i press the command button since its already in the database it will delete the 10 data in my listview what happens is it leaves the last data in the listview
Dim ListOfExistingItem As List(Of Integer) = New List(Of Integer)
Try
locconn.Open()
For x = 0 To ListView2.Items.Count - 1
Dim a As String = ListView2.Items.Item(x).Text
a = ListView2.Items.Item(x).Text
Dim command As New SqlCommand
Dim TMP_SQL_VAL As String = "select count([Check-Out]) from tbl_list1 where barcode = '" + a + "'"
command = New SqlCommand(TMP_SQL_VAL, locconn)
Dim READER As SqlDataReader
READER = command.ExecuteReader()
READER.Read()
If READER(0) = 0 Then
MsgBox("Barcode: " & a & "is still Inside", MsgBoxStyle.Exclamation)
TextBox4.Text = Nothing
ListOfExistingItem.Add(x)
READER.Close()
End If
READER.Close()
Next
If Not IsNothing(ListOfExistingItem) Then
For Each x As Integer In ListOfExistingItem
If Not x >= ListView2.Items.Count - 1 Then
ListView2.Items.RemoveAt(x)
End If
Next
End If
I think your problem is a little more complex than an off-by-1 error.
When you loop through ListOfExistingItem, which should contains the indexes of items to be removed, you are removing items from ListView2.
The problem with this is that by removing an item from the list, you are changing the position of all items after it.
For example, take the list { a, b, c }. The index of 'a' is 0, 'b' is 1, and 'c' is 2.
Now, if you were to say list.RemoveAt(1) then it would remove 'b'. This will then cause the index of 'c' to change to 1 instead of two. Then when you go and try to remove 'c', it wont be in the same place.
For an quick fix, try adding 'ListOfExistingItem.Reverse' before the last 'for' loop:
Call ListOfExistingItem.Reverse()
For Each x As Integer In ListOfExistingItem
If Not x >= ListView2.Items.Count - 1 Then
ListView2.Items.RemoveAt(x)
End If
Next
This way, you will start with the last item in the list and work your way backwards, which should avoid the problem.

new records to add first row of datagrid view in vb.net

In my VB.Net application I am filling my data grid view like this:
Dim cmdd1 As New SqlCommand("DashBordFetch1", con.connect)
cmdd1.CommandType = CommandType.StoredProcedure
cmdd1.Parameters.Add("#locid", SqlDbType.Int).Value = locid
da1.SelectCommand = cmdd1 dr = cmdd1.ExecuteReader
While dr.Read
flag = False
carid1 = dr("Car_Id")
For j = 0 To dcnt1 - 2
If carid1 = dgv.Item(0, j).Value Then
flag = True
Exit For
End If
Next
If flag = False Then
If dr("Car_Id") Is DBNull.Value Then
carid1 = "null"
Else
carid1 = dr("Car_Id")
End If
If dr("Plate_Source") Is DBNull.Value Then
platesource1 = "Null"
Else
platesource1 = dr("Plate_Source")
End If
Dim row1 As String() = {carid1, platesource1}
DGVDeliverd.Rows.Add(row1)
End If
End While
..also i am using Timer..every 2 minutes timer will work .some time new records will added to my datagridview..while adding new record that is adding to last row of my datagrid view,,i want to add every time new records to my first row of datagridview. how i can do this?
I am using windows forms
You can use Insert instead of Add to specify the position for the new row. So always insert at position zero:
DGVDeliverd.Rows.Insert(0, row1)
See MSDN here for details (screenshot below).
Use:
Order by your primary id desc
to keep the latest data on top of the grid
update #1
DGVDeliverd.Controls[0].Controls.AddAt(0, row1);

Adding two column values to listbox in vb.net

I have a table named users which has the following columns in it
User_id,user_name,user_pwd,First_Name,Middle_Name,Last_Name and user_type.
I have dataset named dst and created a table called user in the dataset. Now I want to populate listbox with user_Name, First_Name, Last_name of each and every row in the table user.
I am able to add one column value at a time but not getting how to add multiple column values of each row to listbox
Dim dt As DataTable = Dst.Tables("user")
For Each row As DataRow In dt.Rows
lstUsers.Items.Add(row("User_Name"))
Next
Above code works perfectly but I also want to add First_name as well as last_name to the list box at the same time.
Use same approach as you have, but put all values you want in one string.
Dim dt As DataTable = Dst.Tables("user")
For Each row As DataRow In dt.Rows
Dim sItemTemp as String
sItemTemp = String.Format("{0},{1},{2}", row("User_Name"), row("First_Name"), row("Last_Name"))
lstUsers.Items.Add(sItemTemp)
Next
String.Format() function will call .ToString() on all parameters.
In this case if row(ColumnName) is NULL value then .ToString() return just empty string
You have 2 choices:
Using the ListBox:
To use the ListBox, set the font to one that is fixed width like courier new (so that the columns line up), and add the items like this:
For Each row As DataRow In dt.Rows
lstUsers.Items.Add(RPAD(row("User_Name"),16) & RPAD(row("First_Name"),16) & RPAD(row("Last_Name"),16))
Next
The RPAD function is defined like this:
Function RPAD(a As Object, LENGTH As Object) As String
Dim X As Object
X = Len(a)
If (X >= LENGTH) Then
RPAD = a : Exit Function
End If
RPAD = a & Space(LENGTH - X)
End Function
Adjust the LENGTH argument as desired in your case. Add one more for at least one space. This solution is less than ideal because you have to hard-code the column widths.
Use a DataGridView control instead of a ListBox. This is really the best option, and if you need, you can even have it behave like a ListBox by setting the option to select the full row and setting CellBorderStyle to SingleHorizontal. Define the columns in the designer, but no need to set the widths - the columns can auto-size, and I set that option in the code below. if you still prefer to set the widths, comment out the AutoSizeColumnsMode line.
The code to set up the grid and add the rows goes like this:
g.Rows.Clear() ' some of the below options are also cleared, so we set them again
g.AutoSizeColumnsMode = DataGridViewAutoSizeColumnMode.AllCells
g.CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal
g.SelectionMode = DataGridViewSelectionMode.FullRowSelect
g.AllowUserToAddRows = False
g.AllowUserToDeleteRows = False
g.AllowUserToOrderColumns = True
For Each row As DataRow In dt.Rows
g.Rows.Add(row("User_Name"), row("First_Name"), row("Last_Name"))
Next
You might solved your problem by now but other users like me might have issue with it.
Above answers given worked for me even but I found a same answer in a simple way according to what I want..
cmd = New SqlCommand("select User_Name, First_Name, Last_Name from User")
Dim dr As SqlDataReader = cmd.ExecuteReader(YourConnectionString)
If dr.HasRows Then
Do While dr.Read
lst.Items.Add(dr.Item(0).ToString & " " & dr.Item(1).ToString & " " & dr.Item(2).ToString)
Loop
End If
This worked for me, maybe wrong way but I found it simple :)
May I suggest you use a ListView control instead of Listbox?
If you make the switch, here's a sample subroutine you could use to fill it up with the data you said you want. Adapt it the way you like; there's much room for improvement but you get the general idea:
Public Sub FillUserListView(lstUsers As ListView, Dst As DataSet)
Dim columnsWanted As List(Of String) = New List(Of String)({"User_Name", "First_Name", "Last_Name"})
Dim dt As DataTable = Dst.Tables("user")
Dim columns As Integer = 0
Dim totalColumns = 0
Dim rows As Integer = dt.Rows.Count
'Set the column titles
For Each column As DataColumn In dt.Columns
If columnsWanted.Contains(column.ColumnName) Then
lstUsers.Columns.Add(column.ColumnName)
columns = columns + 1
End If
totalColumns = totalColumns + 1
Next
Dim rowObjects(columns - 1) As ListViewItem
Dim actualColumn As Integer = 0
'Load up the rows of actual data into the ListView
For row = 0 To rows - 1
For column = 0 To totalColumns - 1
If columnsWanted.Contains(dt.Columns(column).ColumnName) Then
If actualColumn = 0 Then
rowObjects(row) = New ListViewItem()
rowObjects(row).SubItems(actualColumn).Text = dt.Rows(row).Item(actualColumn)
Else
rowObjects(row).SubItems.Add(dt.Rows(row).Item(actualColumn))
End If
lstUsers.Columns.Item(actualColumn).Width = -2 'Set auto-width
actualColumn = actualColumn + 1
End If
Next
lstUsers.Items.Add(rowObjects(row))
Next
lstUsers.View = View.Details 'Causes each item to appear on a separate line arranged in columns
End Sub

DataTable: How to get item value with row name and column name? (VB)

I have a simple DataTable where one of the columns contains unique values. For example:
ColumnName1 ColumnName2
value1 35
value2 44
value3 10
Because I know that value 1, 2 and 3 will always be different one from another, I would like to get a value of this table only using ColumnName2 and one of the values of ColumnName1.
That would for example be:
searchedValue = DataTable.Rows("value3").Item("ColumnName2)
'result would be 10
I tried the following examples unsuccessfully:
with the DataTable.Select method: returns an array of rows, but I only need one
with the DataTable.Rows.IndexOf method: if I understood well, I need to provide the whole row contents for it to be found with this method.
Dim rows() AS DataRow = DataTable.Select("ColumnName1 = 'value3'")
If rows.Count > 0 Then
searchedValue = rows(0).Item("ColumnName2")
End If
With FirstOrDefault:
Dim row AS DataRow = DataTable.Select("ColumnName1 = 'value3'").FirstOrDefault()
If Not row Is Nothing Then
searchedValue = row.Item("ColumnName2")
End If
In C#:
var row = DataTable.Select("ColumnName1 = 'value3'").FirstOrDefault();
if (row != null)
searchedValue = row["ColumnName2"];
'Create a class to hold the pair...
Public Class ColumnValue
Public ColumnName As String
Public ColumnValue As New Object
End Class
'Build the pair...
For Each row In [YourDataTable].Rows
For Each item As DataColumn In row.Table.Columns
Dim rowValue As New ColumnValue
rowValue.ColumnName = item.Caption
rowValue.ColumnValue = row.item(item.Ordinal)
RowValues.Add(rowValue)
rowValue = Nothing
Next
' Now you can grab the value by the column name...
Dim results = (From p In RowValues Where p.ColumnName = "MyColumn" Select p.ColumnValue).FirstOrDefault
Next
For i = 0 To dt.Rows.Count - 1
ListV.Items.Add(dt.Rows(i).Item("STU_NUMBER").ToString)
ListV.Items(i).SubItems.Add(dt.Rows(i).Item("FNAME").ToString & " " & dt.Rows(i).Item("MI").ToString & ". " & dt.Rows(i).Item("LNAME").ToString)
ListV.Items(i).SubItems.Add(dt.Rows(i).Item("SEX").ToString)
Next
Try:
DataTable.Rows[RowNo].ItemArray[columnIndex].ToString()
(This is C# code. Change this to VB equivalent)