So I have a function when fired queries against the database (twice) and returns the results in a dataset. It checks the results (to make sure there are some) then loops through and grabs each row from the returned dataset and imports (copies) it to a different dataset.
I also add a primary key into a List so I do not add the same item twice (from the 2nd query) to the dataset.
I then return the dataset.
The problem? The query works and there is a returned value.. however the dataset in which I aim to import the row(s) to does not keep the imported row.
Please make any suggestions regarding my approach and the issue at hand; I'm always looking to improve!
Public Function GetClientsWithMonitors(ByVal argHost As FOO.Interfaces.BAR) As DataSet
Try
Dim localDataSet As New DataSet()
Dim clientsWithMonitors As New DataSet()
Dim tempList As New List(Of Integer)
clientsWithMonitors.Clear()
localDataSet.Clear()
tempList.Clear()
clientsWithMonitors.Tables.Add()
'SQL getting monitors applied to clients
Dim clientSQL As String = "SELECT DISTINCT c.ClientID, c.Name FROM agents a LEFT JOIN clients c ON c.ClientID = a.ClientID WHERE a.ClientID > 0"
'SQL getting monitors applied directly to an agent
Dim agentSQL As String = "SELECT DISTINCT c.ClientID, c.Name FROM clients c LEFT JOIN computers comp ON c.ClientID = comp.ClientID LEFT JOIN agents a ON comp.ComputerID = a.ComputerID WHERE a.LocID = 0 AND a.ClientID = 0 AND a.ComputerID > 0"
localDataSet = argHost.GetDataSet(clientSQL)
If localDataSet.Tables.Count > 0 AndAlso localDataSet.Tables(0).Rows.Count > 0 Then
For Each row As DataRow In localDataSet.Tables(0).Rows
If Not tempList.Contains(CInt(row("ClientID").ToString())) Then
Dim clientID As Integer = CInt(row("ClientID").ToString())
clientsWithMonitors.Tables(0).ImportRow(row)
tempList.Add(clientID)
End If
Next
End If
If localDataSet.Tables.Count > 0 AndAlso localDataSet.Tables(0).Rows.Count > 0 Then
localDataSet.Clear()
localDataSet = argHost.GetDataSet(agentSQL)
For Each row As DataRow In localDataSet.Tables(0).Rows
If Not tempList.Contains(CInt(row("ClientID").ToString())) Then
Dim clientID As Integer = CInt(row("ClientID").ToString())
clientsWithMonitors.Tables(0).ImportRow(row)
tempList.Add(clientID)
End If
Next
End If
Return clientsWithMonitors
Catch ex As Exception
LogEventViaHost(argHost, "Error Getting dataset of clients with a specified monitor" & ex.StackTrace & " " & ex.Message)
Return Nothing
End Try
Column names must be explicitly stated; for some reason I was thinking the dataset it would implicitly inherit the column names from the imported datarow row.
clientsWithMonitors.Tables(0).Columns.Add("Foo")
clientsWithMonitors.Tables(0).Columns.Add("Bar")
Related
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
how to calculate the amount of Selected rows in my table
I only want to calculate the to Total amount with the Status of Printed = 'Y' and Receive = 'N'
https://i.stack.imgur.com/iDqPE.png
Heres my code, this code will only calculate the total of Column amount
Dim rows As Integer = 0
Dim total_amount As Double
Try
Do Until rows = DataGridView1.RowCount
Dim AMOUNT = DataGridView1.Rows(rows).Cells(1).Value
total_amount = total_amount + AMOUNT
rows = rows + 1
Loop
Catch ex As Exception
End Try
lblAmount.Text = total_amount
Dim dblValue As Double = total_amount
lblTotalWithheld.Text = (dblValue.ToString("N",CultureInfo.InvariantCulture))
If you want to do something for every row in the grid then loop through the Rows collection, e.g.
For Each row As DataGridViewRow In DataGridView1.Rows
'Use row here.
Next
If you want to do something for each selected row then loop through the SelectedRows collection, e.g.
For Each row As DataGridViewRow In DataGridView1.SelectedRows
'Use row here.
Next
You say:
how to calculate the amount of Selected rows in my table
but it's not clear whether you mean selected in the UI or with the specified values in the specified columns. Either way, the code inside the loop will be the same:
If CStr(row.Cells(0).Value) = "Y" AndAlso CStr(row.Cells(1).Value) = "N" Then
Dim amount = CDbl(row.Cells(2).Value)
'Use amount here.
End If
I've used arbitrary column indexes there, so you should change them as required.
EDIT:
If your actual criteria for processing involves the state of the data rather than the state of the UI then you should actually not be using the grid at all, but rather the data source. If you aren't already, you should start by populating a DataTable, binding that to a BindingSource and then binding that to the grid, e.g.
myDataAdapter.Fill(myDataTable)
myBindingSource.DataSource = myDataTable
myDataGridView.DataSource = myBindingSource
You can then loop through the BindingSource to get the desired data, e.g.
Dim amount As Double = 0
For Each rowView As DataRowView In myBindingSource
If CStr(rowView("Printed")) = "Y" AndAlso
CStr(rowView("Receive")) = "N" Then
amount += CDbl(rowView("Amount"))
End If
Next
You could even throw some LINQ at it:
Dim amount = myBindingSource.Cast(Of DataRowView)().
Where(Function(drv) CStr(drv("Printed")) = "Y" AndAlso
CStr(drv("Receive")) = "N").
Sum(Function(drv) CDbl(rowView("Amount")))
or, in query syntax:
Dim amount = (From drv As DataRowView In myBindingSource
Where CStr(drv("Printed")) = "Y" AndAlso
CStr(drv("Receive")) = "N")
Select CDbl(rowView("Amount"))).Sum()
Possibly the easiest option of all, though, is to use the Compute method of the DataTable, e.g.
Dim amount = CDbl(myDataTable.Compute("SUM(Amount)",
"Printed = 'Y' AND Receive = 'N'")
I know this is very basic, and I've done this hundreds of times. But, for some strange reason I am executing this command on a database, and it fails when it tries to read a column from the result. If I execute this statement in SQL Plus logged in as the same credentials, the row (table has 1 row) is selected just fine. Any ideas what I am doing wrong? I tried accessing the columns by name, by index, and indeed any column - all of them give no data. I tried without the .NextResult() (just in case), same exception.
'...
' Determine if this database is Multisite enabled
Dim multisiteCmd As OleDbCommand = DbConnection.CreateCommand
multisiteCmd.CommandText = "SELECT * FROM TDM_DB_VERSION;"
Dim dbVersionReader As OleDbDataReader = multisiteCmd.ExecuteReader()
If dbVersionReader.HasRows Then
dbVersionReader.NextResult()
'If a ReplicaID was generated for the Database ID, then this is part of a
'multisite implementation
'Dim dbRepID As String = dbVersionReader("DB_REPLICID")
Dim dbRepID As String = dbVersionReader(9)
PluginSettings.UseMultisite = False
If Not dbRepID Is Nothing Then
If dbRepID.Length > 0 Then
PluginSettings.UseMultisite = True
PluginSettings.MultisiteReplicaId = dbRepID
End If
End If
End If
dbVersionReader.Close()
As you can see from these Immediate commands, the connection is open:
? DbConnection.Provider
"OraOLEDB.Oracle"
? DbConnection.State
Open {1}
NextResult() is for statements that have more than one result set. For example, if you had sent a command like this:
"SELECT * FROM TDM_DB_VERSION;SELECT * FROM dual;"
Note there are two queries in there. You can handle them both with a single call to the database and a single OleDbDataReader, and NextResult() is part of how you do that.
What you want instead is this:
Dim multisiteCmd As OleDbCommand = DbConnection.CreateCommand
multisiteCmd.CommandText = "SELECT * FROM TDM_DB_VERSION;"
Dim dbVersionReader As OleDbDataReader = multisiteCmd.ExecuteReader()
If dbVersionReader.Read() Then
'If a ReplicaID was generated for the Database ID, then this is part of a
'multisite implementation
'Dim dbRepID As String = dbVersionReader("DB_REPLICID")
Dim dbRepID As String = dbVersionReader(9)
PluginSettings.UseMultisite = False
If Not dbRepID Is Nothing Then ' Do you mean check for DbNull here? "Nothing" is not the same thing
If dbRepID.Length > 0 Then
PluginSettings.UseMultisite = True
PluginSettings.MultisiteReplicaId = dbRepID
End If
End If
End If
dbVersionReader.Close()
This code returns zero rows count but there are 2 rows in appointment table.
The msgbox I commented was to check if the date is correct and format is correct and shows date as 2014/08/09. The appointment date in database is 2014/08/09 for 2 records (the only 2 records). Record count variable shows 0.
Table name (copied directly cut and paste) is Appointments and column is AppointmentDate.
The connectDatabase sub routine connects to the database successfully as I use it whenever I connect to database so it's correct as I connect to other tables correctly before I run this code using same sub routine.
Command.text contains
SELECT * FROM Appointments WHERE AppointmentDate = 2014/08/09
Don't know what other details to specify.
Private Sub frmAppointments_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'load appointments
LoadAppointments(dtpAppointmentDate.Value.Date)
End Sub
Public Sub LoadAppointments(whichdate As Date)
Dim sqlcmd As New OleDb.OleDbCommand
'set connection
ConnectDatabase()
With frmAppointments
'MsgBox(whichdate)
M_connDB.Open()
'fetch records from database
sqlcmd.Connection = M_connDB
sqlcmd.CommandText = "SELECT * FROM Appointments WHERE AppointmentDate = " & whichdate
.dataAdapterAppointments = New OleDb.OleDbDataAdapter(sqlcmd.CommandText, M_connDB)
'first clear data table to prevent duplicates
.dataTableAppointments.Rows.Clear()
.dataAdapterAppointments.Fill(.dataTableAppointments)
M_connDB.Close()
Dim rowindex As String
Dim iy As Long
Dim recordcount As Long
'check if any records exist
recordcount = .dataTableAppointments.Rows.Count
If Not recordcount = 0 Then
For iy = 0 To .dataTableAppointments.Rows.Count
For Each row As DataGridViewRow In .dtgrdAppointments.Rows
If row.Cells(0).Value = .dataTableAppointments.Rows(iy).Item(6) Then
rowindex = row.Index.ToString()
MsgBox(.dtgrdAppointments.Rows(rowindex).Cells(0).Value, vbInformation + vbOKOnly, "MSG")
Exit For
End If
Next
Next iy
Else
MsgBox("No Appointments for selected date.", vbInformation + vbOKOnly, "No Appoinments")
End If
End With
Use sql-parameters instead of string-concatenation. This should work in MS Access:
sqlcmd.CommandText = "SELECT * FROM Appointments WHERE AppointmentDate = ?"
sqlcmd.Parameters.AddWithValue("AppointmentDate", whichdate)
This prevents you from conversion or localization issues and -even more important- sql-injection.
2014/08/09 doesn't have quotes around it, making it a math expression (2014 divided by 8 divided by 9). Of course, your table has no rows with a date matching the result of that expression. But don't put in the quotes. Instead, add a parameter.
I don't VB, so I write it in C#. My point of view is it's better to specify the datatype too:
sqlcmd.CommandText = "SELECT * FROM Appointments WHERE AppointmentDate=#whichdate";
sqlcmd.Parameters.Add("#whichdate", SqlDbType.Date).Value = whichdate;
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