How to check the controls (display name and type) in the GroupBox by recursion and display on a DataGridView? - vb.net

How to read controller properties, automatically added to a table assigned to a DataGridView?
Dim dt As DataTable = New DataTable
dt.Columns.Add("Name")
dt.Columns.Add("Type")
Dim n As Integer = Me.Controls.Count
For i As Integer = 0 To n - 1
dt.Rows.Add(Me.Controls(i).Name.ToString, Me.Controls(i).GetType.ToString)
Next
DataGridView1.DataSource = dt
The above is the check for controls in the Form, it only displays Name and type of GroupBoxes, help me use recursive function to check the controls in GroupBox.
Below is my idea, but it was not working:
Public Sub V_gr(ByVal _Obj As Object)
dt.Columns.Add("Name")
dt.Columns.Add("Type")
If (_Obj.Controls.count > 0) Then
Dim i As Integer = _Obj.Controls.count - 1
dt.Rows.Add(_Obj.Controls(i).Name.ToString, _Obj.Controls(i).GetType.ToString)
DataGridView1.DataSource = dt
End If
End Sub
Use a temporary table assigned to DataGridView and display the control information checked on it with 2 columns Name and Type

You can split the DataTable creation and the Controls enumeration in two different methods:
The first method is the public one, which can be called just passing the Parent control from which to start the enumeration.
This method just creates a DataTable, then calls the private method to fill it with the results of the enumeration
The private method creates a new DataRow for each Control it finds and add it to the DataTable.
You could also modify the private method to return a List of objects that can be transformed to a DataTable after.
I've added a Column, named "Parent", which references the Parent of the control. It may be useful to know which are the Parents of these Controls.
' Find all Controls in the current Form
DataGridView1.DataSource = ControlsListToDataTable(Me)
Private Function ControlsListToDataTable(parent As Control) As DataTable
If (parent Is Nothing) OrElse (Not parent.HasChildren) Then Return Nothing
Dim dt As DataTable = New DataTable("ParentControls")
dt.Columns.AddRange({
New DataColumn() With {.ColumnName = "Name", .DataType = GetType(String)},
New DataColumn() With {.ColumnName = "Type", .DataType = GetType(String)},
New DataColumn() With {.ColumnName = "Parent", .DataType = GetType(String)}
})
GetAllControls(parent, dt)
Return dt
End Function
Private Sub GetAllControls(parent As Control, dt As DataTable)
For Each ctl As Control In parent.Controls.OfType(Of Control)
dt.Rows.Add({ctl.Name, ctl.GetType().FullName, ctl.Parent.Name})
If ctl.HasChildren Then GetAllControls(ctl, dt)
Next
End Sub
To find a Control in the DataTable, you can use the DataTable.DefaultView Sort and FindRows methods:
[DataTable].DefaultView.Sort = "Name"
Dim result = [DataTable].DefaultView.FindRows("TextBox1")
Or use a LINQ method:
Dim control = [DataTable].Rows.OfType(Of DataRow)().
FirstOrDefault(Function(dr) dr(0).ToString().Equals("TextBox1"))
Where [DataTable] can be the original DataTable returned by the public method or the DataGridView.DataSource:
Dim dt = CType(DataGridView1.DataSource, DataTable)
Dim control = dt.Rows.OfType(Of DataRow)(). (... etc ...)

Related

How to get IEnumerable(Of DataRow) From DataTable query

All the examples I have found of complex grouping of DataTable results that use linq query commands look to have no problems getting an IEnumerable(Of DataRow) object as the result.
However I seem to only get a AnonymousType Enumerator return that I cannot cast to DataTable.
I have workarounds, but would prefer to convert the results to a DataTable, as it looks possible and I may be doing something wrong.
It's a simple table with Many ClientID and ClientName columns and other columns with login timestamps.
Dim dtMatrix As DataTable = New DataTable()
... (populate DataTable)
Dim qClients = From row In dtMatrix
Group row By client = New With {Key .ClientID = row("ClientID"), Key .ClientName = row("ClientName")} Into Group
Select New With {Key .ClientID = client.ClientID, Key .ClientName = client.ClientName}
This returns the generic Enumerator result, however
Dim qClients As IEnumerable(Of DataRow) = From row In dtMatrix
Group row By client = New With {Key .ClientID = row("ClientID"), Key .ClientName = row("ClientName")} Into Group
Select New With {Key .ClientID = client.ClientID, Key .ClientName = client.ClientName}
Throws an exception
Unable to cast object of type... to type
'System.Collections.Generic.IEnumerable`1[System.Data.DataRow]'.
I will be happy to paste the whole error message if it will add more clarity.
My base assumption is that the DataTable should allow the cast to occur inherently as it is the object being queried. However this does not seem to be the case. Have I constructed my query incorrectly? (Framework 4.6.2)
You can use OfType on the Rows property of the DataTable:
Dim dtMatrix As DataTable = New DataTable()
'' Populate code goes here...
Dim dtRows As IEnumerable(Of DataRow) = dtMatrix.Rows.OfType(Of DataRow)()
The Rows property returns a DataRowCollection, that implements (through inheritance) the IEnumerable interface but not the IEnumerable(Of T) interface, that's why you can't use most of linq over it directly.
The following extension uses Reflection to create a new DataTable and create DataColumns in it that match the properties and fields of the type passed in. In general, if you are creating anonymous types in LINQ, you can't just convert to a DataRow which must be tied to a DataTable which must already have matching columns. I went ahead and wrote a second extension to DataTable that adds an IEnumerable<T> with matching field/property names to it.
Public Module Ext
<Extension()>
Public Function GetValue(member As MemberInfo, srcObject As Object) As Object
If TypeOf member Is FieldInfo Then
Return DirectCast(member, FieldInfo).GetValue(srcObject)
ElseIf TypeOf member Is PropertyInfo Then
Return DirectCast(member, PropertyInfo).GetValue(srcObject)
Else
Throw New ArgumentException("MemberInfo must be of type FieldInfo or PropertyInfo", Nameof(member))
End If
End Function
<Extension()>
Public Function GetMemberType(member As MemberInfo) As Type
If TypeOf member Is FieldInfo Then
Return DirectCast(member, FieldInfo).FieldType
ElseIf TypeOf member Is PropertyInfo Then
Return DirectCast(member, PropertyInfo).PropertyType
ElseIf TypeOf member Is EventInfo Then
Return DirectCast(member, EventInfo).EventHandlerType
Else
Throw New ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo or EventInfo", Nameof(member))
End If
End Function
<Extension()>
Public Function ToDataTable(Of T)(rows As IEnumerable(Of T)) As DataTable
Dim dt = New DataTable
If (rows.Any()) Then
Dim rowType = rows.First().GetType()
Dim memberInfos = rowType.GetProperties.Cast(Of MemberInfo)().Concat(rowType.GetFields).ToArray()
For Each info In memberInfos
dt.Columns.Add(New DataColumn(info.Name, info.GetMemberType()))
Next
For Each r In rows
dt.Rows.Add(memberInfos.Select(Function (i) i.GetValue(r)).ToArray())
Next
End If
Return dt
End Function
<Extension()>
Public Function AddObjects(Of T)(dt As DataTable, rows As IEnumerable(Of T))
If (rows.Any()) Then
Dim rowType = rows.First().GetType()
Dim memberInfos = rowType.GetProperties().Cast(Of MemberInfo)().Concat(rowType.GetFields()).ToArray()
For Each r In rows
Dim newRow = dt.NewRow()
For Each memberInfo In memberInfos
newRow(memberInfo.Name) = memberInfo.GetValue(r)
Next
dt.Rows.Add(newRow)
Next
End If
Return dt
End Function
End Module
Note that I write in C# and translated this from my C# extension. It is untested but compiles.
Using the extension, you should be able to get a DataTable from your qClients by:
Dim dtClients = qClients.ToDataTable()

Display DataGridView values in text boxes based on matching values - vb.net

I have an SQL query that returns 2 columns of values:
country | number
NA | 1
IN | 2
CN | 3
DE | 4
And so on.
I am trying to do one of the following:
Assign these values to variables I can copy to an excel workbook
Or just use the DGV as a medium to copy values to text boxes.
For example, I have a form with country labels and textboxes next to them. I would want to click a button and have the data copied to the matching text box.
DGV number value where DGV row value = CN would be 3 and that value would be copied to the CN value text box.
If you are only using the DGV as a medium, but not actually displaying it, use a dictionary instead. Output the SQLDataReader using the SQLcommand(cmd) with the country code being the key and the number being the value. Then its as simple as:
Dim dc As Dictionary(Of String, String) = New Dictionary(Of String, String)
Dim cmd As SqlCommand = "your query string"
cmd.Connection.ConnectionString = "your con string"
Using sqlrdr As SqlDataReader = cmd.ExecuteReader()
While (sqlrdr.Read())
dc.Add(sqlrdr(0), sqlrdr(1))
End While
End Using
Then just output to the textbox:
txtNA.Text = dc.Item("NA")
If the countries are fixed as your question refers, then you could use something like this:
First, use the names of the countries to name the TextBoxes (txtNA, txtIN, txtCN,...)
Then you can put this code:
Try
For i = 0 To DataGridView1.RowCount - 1
Me.Controls("txt" & DataGridView1.Rows(i).Cells(0).Value.ToString).Text = _
DataGridView1.Rows(i).Cells(1).Value.ToString()
Next
Catch
End Try
The following will not work 'as is' if using classes via dragging tables from the data source window in the ide. We would need to cast objects not to a DataTable but to the class definition in a TableAdapter, DataSet and BindingSource.
Here is a method which reads from SQL-Server database table, places data into a DataTable, data table become the data source for a BindingSource which becomes the data source for the DataGridView. Using a BindingSource we now use the BindingSource to access row data rather than the DataGridView and it's always best to go to the data source rather than the user interface.
BindingSource.Current is a DataRowView, drill down to Row property then field language extension method to get strongly typed data for the current row if there is a current row as you will note I am using two forms of assertions, first, do we have a data source and is the data source populated then we see if there is indeed a current row.
From here we can set variable, properties or control text to the field values of the current row.
Note in form load I seek a specific country (totally optional) and then if found go to that row.
Least but not last, I like using xml literals when doing SQL in code so there is no string concatenation and we can format the statement nicely.
Public Class Form1
''' <summary>
''' Permits obtaining row data in DataGridView
''' </summary>
''' <remarks></remarks>
Dim bsCountries As New BindingSource
Public Property Country As String
Public Property CountryNumber As Integer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt As New DataTable
Using cn As New SqlClient.SqlConnection With
{
.ConnectionString = My.Settings.KarenDevConnectionString
}
Using cmd As New SqlClient.SqlCommand With {.Connection = cn}
' xml literal to make command text
cmd.CommandText =
<SQL>
SELECT [ID],[Country],[Number]
FROM [Countries]
</SQL>.Value
cn.Open()
dt.Load(cmd.ExecuteReader)
dt.Columns("ID").ColumnMapping = MappingType.Hidden
bsCountries.DataSource = dt
DataGridView1.DataSource = bsCountries
' let's try and move to a country
Dim index As Integer = bsCountries.Find("Country", "CN")
If index > -1 Then
bsCountries.Position = index
End If
End Using
End Using
End Sub
''' <summary>
''' Put field values into TextBoxes
''' </summary>
''' <remarks></remarks>
Private Sub DoWork()
If bsCountries.DataSource IsNot Nothing Then
If bsCountries.Current IsNot Nothing Then
Dim row As DataRow = CType(bsCountries.Current, DataRowView).Row
TextBox1.Text = row.Field(Of String)("Country")
TextBox2.Text = row.Field(Of Integer)("Number").ToString
' we can also do this
Me.Country = row.Field(Of String)("Country")
Me.CountryNumber = row.Field(Of Integer)("Number")
End If
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
DoWork()
End Sub
End Class

Merge two identical DataTables results in DataRowState.Modified

Am I wrong assuming that if two identical DataTables are merged the state of each row will be preserved?
Take a look at this simple example. It creates two identical tables and merge the updated table with original table. But the returned table in original.GetChanges() is not Nothing as expected. Also, the state of each row in the original table are changed to Modified.
So what am I missing? Do I really have to create my own merge method to achieve this?
Public Sub Test()
Dim original As DataTable = Me.CreateTableWithData()
Dim updated As DataTable = Me.CreateTableWithData()
Dim preserveChanges As Boolean = True
Dim msAction As MissingSchemaAction = MissingSchemaAction.Ignore
original.Merge(updated, preserveChanges, msAction)
Dim changes As DataTable = original.GetChanges()
MessageBox.Show(String.Format("Count={0}", If((changes Is Nothing), 0, changes.Rows.Count)), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information)
If (Not changes Is Nothing) Then changes.Dispose() : changes = Nothing
updated.Dispose() : updated = Nothing
original.Dispose() : original = Nothing
End Sub
Private Function CreateTableWithData() As DataTable
Dim table As New DataTable("TEST")
table.Columns.Add("ID", GetType(Integer))
table.Columns.Add("VALUE", GetType(String))
table.PrimaryKey = New DataColumn() {table.Columns(0)}
table.Rows.Add(1, "Value 1")
table.Rows.Add(2, "Value 2")
table.AcceptChanges()
Return table
End Function
Output: Count=2
Edit - The workaround
The following code is a workaround for this strange(?) behavior.
Private Shared Sub Merge(target As DataTable, source As DataTable, preserveChanges As Boolean, msa As MissingSchemaAction)
target.Merge(source, preserveChanges, msa)
Dim row As DataRow
Dim column As DataColumn
Dim acceptChanges As Boolean
For Each row In target.Rows
If ((row.RowState = DataRowState.Modified) AndAlso ((row.HasVersion(DataRowVersion.Original)) AndAlso (row.HasVersion(DataRowVersion.Default)))) Then
acceptChanges = True
For Each column In target.Columns
If (Not Object.Equals(row.Item(column, DataRowVersion.Original), row.Item(column, DataRowVersion.Default))) Then
acceptChanges = False
Exit For
End If
Next
If (acceptChanges) Then
row.AcceptChanges()
End If
End If
Next
acceptChanges = Nothing
column = Nothing
row = Nothing
End Sub
After some time of working with DataTable merge I found the best solution to merging data, preserving changes and not setting the RowState to Modified for all of the existing rows.
What I discovered is that all of the rows in the original DataTable would have their RowState set to Modified if you use the DataTable Merge and pass True as the preserve changes property. If you pass false instead, the RowStates remain the same.
Going back to the documentation for the DataTable.Merge(DataTable, Boolean, MissingSchemaAction) Method I found this:
...In this scenario, the GetChanges method is first invoked. That method returns a second DataTable optimized for validating and merging. This second DataTable object contains only the DataTable and DataRow objects objects that were changed, resulting in a subset of the original DataTable...
From there I started to realize that the this merge is not really intended to be used with the original data directly... instead you should merge against the table returned by the GetChanges method (passing true in preserving changes) and then merge the changes table into the original source passing false for the preserving changes parameter.
To demonstrate this I have created the following class:
Class TableManger
Implements ComponentModel.INotifyPropertyChanged
Private _table1 As System.Data.DataTable
Private _table2 As System.Data.DataTable
Private _changesDetected As Integer = 0
Public ReadOnly Property Table1
Get
Return _table1
End Get
End Property
Public ReadOnly Property ChangesDetected As Integer
Get
Return _changesDetected
End Get
End Property
Public Sub New()
_table1 = CreateTableWithData()
_table1.AcceptChanges()
AddHandler _table1.RowChanged, New System.Data.DataRowChangeEventHandler(AddressOf Row_Changed)
End Sub
Public Sub MergeTables()
_table2 = _table1.Clone
Dim tableRows As New List(Of System.Data.DataRow)
For Each r In _table1.Rows
Dim dr2 = _table2.NewRow
For Each col As System.Data.DataColumn In _table1.Columns
dr2(col.ColumnName) = r(col.ColumnName)
Next
_table2.Rows.Add(dr2)
tableRows.Add(dr2)
Next
_table2.AcceptChanges()
If _table2.Rows.Count > 0 Then
_table2.Rows(0)(1) = "TB2 Changed"
End If
If _table1.Rows.Count > 0 Then
'_table1.Rows(0)(1) = "TB1 Change"'
_table1.Rows(1)(1) = "TB1 Change"
End If
_changesDetected = 0
Dim perserveChanges As Boolean = True
Dim msAction As System.Data.MissingSchemaAction = System.Data.MissingSchemaAction.Ignore
Dim changes As System.Data.DataTable = _table1.GetChanges()
If changes IsNot Nothing Then
changes.Merge(_table2, perserveChanges, msAction)
_table1.Merge(changes, False, msAction)
Else
_table1.Merge(_table2, False, msAction)
End If
MessageBox.Show(String.Format("Changes in Change Table: {0} {1}Changes Detected: {2}", If((changes Is Nothing), 0, changes.Rows.Count), System.Environment.NewLine, _changesDetected), "Testing")
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("Table1"))
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("ChangesDetected"))
End Sub
Private Sub Row_Changed(ByVal sender As Object, ByVal e As System.Data.DataRowChangeEventArgs)
Select Case e.Action
Case System.Data.DataRowAction.Change
If e.Row.RowState <> System.Data.DataRowState.Unchanged Then
_changesDetected += 1
End If
End Select
End Sub
Private Function CreateTableWithData() As System.Data.DataTable
Dim newTable As New System.Data.DataTable
Dim columnID As New System.Data.DataColumn("ID", GetType(Guid))
Dim columnA As New System.Data.DataColumn("ColumnA", GetType(String))
Dim columnB As New System.Data.DataColumn("ColumnB", GetType(String))
newTable.Columns.AddRange({columnID, columnA, columnB})
newTable.PrimaryKey = {newTable.Columns(0)}
For i = 0 To 5
Dim dr = newTable.NewRow
dr("ID") = Guid.NewGuid
dr("ColumnA") = String.Format("Column A Row {0}", i.ToString)
dr("ColumnB") = String.Format("Column B Row {0}", i.ToString)
newTable.Rows.Add(dr)
Next
Return newTable
End Function
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
So, in the MergeTables method, I make a change to the first row in _table2 and I make a change in the second row of _table1.
Because I made a change the first row in _table1, the _table1.GetChanges method returns a DataTable with all of the changed rows (just the first row in this case).
I then merge the table containing the changes with _table2 and indicate that I want to preserve changes.
Once that merge is completed I know that the results are preserving the changes that I had made before the merge and that the table will contain the new data as well (so long as there were no conflicts). The result of merging the incoming data into the changes table will resolution of any conflicts in data.
After I have that resolved table I can safely merge into the original _table1 table indicating that preserve change = false. Because passing false as the preserve changes parameter results in no RowState changes for the original data everything works perfectly fine! My changes are preserved And the RowStates are not modified!
Happy Coding!
-Frinny

Make DataRepeater bound to List(Of Object) update?

What is the correct way to bind a List(Of Object) to a DataRepeater? Can you provide example code for this?
I have been racking my brains on this and while I can get an already filled list to show in the repeater, subsequent changes to the list have no effect on the DataRepeater.
Ultimately I hope to use this to bind to a dictionary, if that is possible, but I cannot even get the basics working here.
The data repeater is added on the form design surface, with 3 labels and a progress bar in the ItemTemplate. The code I have attempted (where DutData is the DataRepeater) to setup the list and repeater is then:
Public Class BurnIn
Public Shared currentDuts As New Dictionary(Of UInteger, DeviceUnderTest) ' Collection of all current DUTs.
Dim bs As New BindingSource
Dim testTemp As Boolean = False
Dim testList As New List(Of DeviceUnderTest)
Private Sub BurnIn_Load() Handles Me.Load
'...
' Add two items to the dictionary and populate them
currentDuts.Add(0, New DeviceUnderTest(Me.user, 0))
currentDuts.Item(0).RackBay = "012345678901"
currentDuts.Item(0).AssemblySerial = "123456789"
currentDuts.Item(0).SetProgram(1, "Program1")
currentDuts.Add(currentDuts.Count, New DeviceUnderTest(Me.user, 1))
currentDuts.Item(1).RackBay = "109876543210"
currentDuts.Item(1).AssemblySerial = "1319A5126"
currentDuts.Item(1).SetProgram(1, "Program1")
' Copy the items to the test list.
testList.Add(currentDuts.Item(0))
testList.Add(currentDuts.Item(1))
testTemp = True
' Setup the binding source, data source and data bindings.
bs.DataSource = testList
LocationLabel.DataBindings.Add("Text", bs, "RackBay")
DutLabel.DataBindings.Add("Text", bs, "AssemblySerial")
ProgramLabel.DataBindings.Add("Text", bs, "Program")
DutProgress.DataBindings.Add("Value", bs, "Progress")
DutData.DataSource = testList
'...
Me.Show()
End Sub
Then to test adding or removing list items:
Private Sub Button1_Click() Handles Button1.Click
If testTemp = False Then
' Add an item to the dictionary and populate it.
currentDuts.Add(currentDuts.Count, New DeviceUnderTest(Me.user, 1))
currentDuts.Item(1).RackBay = "109876543210"
currentDuts.Item(1).AssemblySerial = "1319A5126"
currentDuts.Item(1).SetProgram(1, "Program1")
' Copy the item to the test list.
testList.Add(currentDuts.Item(1))
testTemp = True
Else
' Remove the item from the dictionary and the list.
currentDuts.Remove(1)
testList.Remove(testList.Item(1))
testTemp = False
End If
End Sub
End Class
First thing is to replace your List with a BindingList
Dim testList As New BindingList(Of DeviceUnderTest)

how do i initialize my arraylist

I have a function that adds items to my arraylist. my problem is that it only holds one item at a time since it is reinitializing the array lit every time I click my button. what is the syntax in VB to only initialize the array if it has not been created yet?
Dim itemSelectAs New ArrayList()
Dim Quantities As New ArrayList()
Dim itemQtyOrdered As Integer
Public Sub ShtickDataList_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.ListViewCommandEventArgs) Handles ShtickDataList.ItemCommand
If e.CommandName = "addToCart" Then
Dim itemQuantity As DropDownList = e.Item.FindControl("QuantityDropDown")
itemQtyOrdered = itemQuantity.SelectedValue
ItemSelect.Add(e.CommandArgument)
Quantities.Add(itemQtyOrdered)
Session("itemInCart") = ItemSelect
Session("quantities") = Quantities
viewInvoice()
End If
End Sub
Protected Sub viewInvoice()
Dim itemSelected As ArrayList = DirectCast(Session("itemInCart"), ArrayList)
Dim QuantityofItem As ArrayList = DirectCast(Session("quantities"), ArrayList)
Dim conn As SqlConnection
Dim comm As SqlCommand
Dim reader As SqlDataReader
Dim purimConnection2 As String = ConfigurationManager.ConnectionStrings("Purim").ConnectionString
conn = New SqlConnection(purimConnection2)
comm = New SqlCommand("SELECT ProductName FROM Products WHERE ProductID = #ProductID", conn)
Dim i As Integer
For i = 0 To ItemSelect.Count - 1
comm.Parameters.Add("#ProductID", Data.SqlDbType.Int)
comm.Parameters("#ProductID").Value = (ItemSelected.Count - 1)
'Next
Try
conn.Open()
reader = comm.ExecuteReader()
ViewCartlink.Text = "View Cart: (" & ItemSelected.Count & ")"
Finally
conn.Close()
End Try
End Sub
First you need to dimension your array list.
Dim array_list as ArrayList()
Then you can instantiate one
array_list = new ArrayList
Or you can combine it into one step:
Dim array_list = new ArrayList()
After that you can add and remove elements from your array list with
array_list.add(obj)
and remove with
array_list.remove(obj)
It looks like your problem is related to accessing the members of an arraylist. New items are always added to the end of an arraylist. To access them directly, you will need their index. If you know the index of the item you want to access use
array_list(i)
If you don't you will need to iterate over the array. To do this you have two options. You can use "for each" or you can use a normal for loop and use array_list.count as your upper bound.
You're recreating your two session values every time you call your button click menu. You need to pull them out of the Session variable and put them in local variables and put them back into the session variable.
Your button method should be:
Public Sub ShtickDataList_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.ListViewCommandEventArgs) Handles ShtickDataList.ItemCommand
if isNothing(itemSelect) Then itemSelect = New ArrayList()
if isNothing(itemQtyOrdered) Then itemQtyOrdered= New ArrayList()
If e.CommandName = "addToCart" Then
Dim itemQuantity As DropDownList = e.Item.FindControl("QuantityDropDown")
itemQtyOrdered = itemQuantity.SelectedValue
ItemSelect.Add(e.CommandArgument)
Quantities.Add(itemQtyOrdered)
Session("itemInCart") = ItemSelect
Session("quantities") = Quantities
viewInvoice()
End If
End Sub
And change your Global calls to:
Dim itemSelect As ArrayList() = Session("itemInCart")
Dim Quantities As New ArrayList() = Session("quantities")
Define your array outside the button click event. (Form level)
Then in the button click event try this:
If myArrayList Is Nothing then
'initializes the array list only if that hasn't happened yet
myArrayList = new ArrayList
End If
'adds the item to the existing list without causing it to reintialize
myArrayList.add(item)
That way it is initialized if it hasn't been, but not if it already has. If it is initialized at the form level ie... its declared as new already then you can just add to it.
Basically make sure that you aren't calling New for the arrayList in the button click event.
Editing for web form:
You should probably check where you initialize your arrayList. Like in the Page_Load:
If Not Page.IsPostBack Then
myArrayList = New ArrayList
End If
MSDN Postback