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
Related
I'm trying to retrieve data from a SysListView32 object with the code below but it's returning an empty string.
The elements that I need to retrieve are those highlighted in red, as well as the others contained in the other ControlType.ListItem elements, according to the Inspector's print.
Can some one check what's wrong with my code?
Msgbox("Position the mouse cursor on the screen and press ENTER.")
Dim pt As POINTAPI
GetCursorPos(pt)
Dim hwnd As IntPtr = WindowFromPoint(pt)
Dim hwnd As IntPtr = 67202
Dim el As AutomationElement = AutomationElement.FromHandle(hwnd)
Dim walker As TreeWalker = TreeWalker.ContentViewWalker
Dim i As Integer = 0
Dim child As AutomationElement = walker.GetFirstChild(el)
While child IsNot Nothing
'
Dim k As Integer = 0
Dim child2 As AutomationElement = walker.GetFirstChild(child)
While child2 IsNot Nothing
Console.WriteLine(child2.Current.ToString)
child2 = walker.GetNextSibling(child2)
End While
child = walker.GetNextSibling(child)
End While
The SysListView32 may not provide the information requested if its current view state is not LV_VIEW_DETAILS, so we should temporarily (if the current view state is different), use the MultipleViewPattern of its AutomationElement to verify the view state and change it if necessary using the MultipleViewPattern.SetCurrentView() method.
The SetCurrentView() method uses the same values of the Win32 Control.
Then use the AutomationElement FindAll() method of the to find all child elements of type ControlType.DataItem or ControlType.ListItem (using an OrCondition).
For each of them, get all child items of type ControlType.Edit and ControlType.Text (using another OrCondition).
The position of each Item in the list is retrieved using the Item's GridItemPattern, to access the Item's Row property.
Finally, we restore the previous View State if we had to change it.
The code in the example fills a Dictionary(Of Integer, ListViewItem) (named sysListViewItems here), containing all the Items extracted from the SysListView32.
The Integer Key represents the position of an Item in the original ListView
The ListViewItem is a .Net object generated from the array of values (as strings) extracted from each item.
If you don't need ListViewItem objects, you can just store the List(Of String), represented by the itemsText object, instead of creating a ListViewItem here:
sysListViewItems.Add(gridPattern.Current.Row, New ListViewItem(itemsText.ToArray())).
The Handle of a SysListView32 can also be acquired enumerating the children of its Top Level Window by ClassName.
AutomationElement.RootElement provides the current Desktop Element:
Dim parentWindow = AutomationElement.RootElement.FindFirst(
TreeScope.Children, New PropertyCondition(AutomationElement.NameProperty, "[Window Caption]"))
Dim sysListView32 = parentWindow.FindAll(
TreeScope.Subtree, New PropertyCondition(AutomationElement.ClassNameProperty, "SysListView32"))
If more than one SysListView32 is found, filter by Header content, direct parent ControlType or ClassName or anything else that allows to single it out.
UI Automation requires a reference to the UIAutomationClient and UIAutomationTypes assemblies.
Imports System.Windows.Automation
' Find the ListView Handle as described or the ListView UI Element directly
Dim sysListViewHandle = [GetSysListView32Handle()]
Dim sysListViewElement = AutomationElement.FromHandle(sysListViewHandle)
If sysListViewElement Is Nothing Then Return
Dim sysListViewItems = New Dictionary(Of Integer, ListViewItem)()
Dim mulView As MultipleViewPattern = Nothing
Dim pattern As Object = Nothing
Dim currentView As Integer = -1
If sysListViewElement.TryGetCurrentPattern(MultipleViewPattern.Pattern, pattern) Then
mulView = DirectCast(pattern, MultipleViewPattern)
currentView = mulView.Current.CurrentView
If currentView <> ListViewWState.LV_VIEW_DETAILS Then
mulView.SetCurrentView(ListViewWState.LV_VIEW_DETAILS)
End If
End If
Dim childItemsCondition = New OrCondition(
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem),
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem))
Dim childItems = sysListViewElement.FindAll(TreeScope.Children, childItemsCondition)
If childItems.Count = 0 Then Return
Dim subItemsCondition = New OrCondition(
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit),
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text))
For Each item As AutomationElement In childItems
Dim itemsText = New List(Of String)()
Dim subItems = item.FindAll(TreeScope.Children, subItemsCondition)
For Each subItem As AutomationElement In subItems
itemsText.Add(subItem.Current.Name)
Next
Dim gridPattern = DirectCast(subItems(0).GetCurrentPattern(GridItemPattern.Pattern), GridItemPattern)
sysListViewItems.Add(gridPattern.Current.Row, New ListViewItem(itemsText.ToArray()))
Next
If mulView IsNot Nothing Then
mulView.SetCurrentView(currentView)
End If
Friend Enum ListViewWState
LV_VIEW_ICON = &H0
LV_VIEW_DETAILS = &H1
LV_VIEW_SMALLICON = &H2
LV_VIEW_LIST = &H3
LV_VIEW_TILE = &H4
End Enum
Apologies if this has already been asked. If so, I am unable to find a simple solution. I am trying to allow a user to copy/paste multiple records in a DataGridView (the in memory copy of the data, to be saved later when the user clicks the save button) and cannot find anything that works. It probably is because there is something I do not understand about all of this.
I set up a standard edit form with Visual Studio's drag/table into a form, so it's using a BindingSource control and all the other controls that come with doing that. It works just fine when manually entering something in the new row one by one, so it seems to be set up correctly, but when it comes to adding a record (or multiples) using code, nothing seems to work.
I tried a few things as outline in the code below. Could someone please at least steer me in the right direction? It cannot be that difficult to paste multiple records.
I run this when the user presses Control-V (the clipboard correctly holds the delimited strings):
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item(1) = Items(0)
drv.Item(2) = Items(1)
drv.Item(3) = Items(2)
drv.Item(4) = Items(3)
'Error on next line : Cannot add external objects to this list.
AdjustmentsBindingSource.Add(drv)
Next
End If
End Sub
EDIT
(the bindingsource is bound to a dataadapter, which is bound to a table in an mdb file, if that helps understand)
I adjusted the inner part of the code to this:
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
It kinda works, but it also adds a blank row before the 2 new rows. Not sure where that is coming from, as I have even included your RowHasData() routine...
I would think that “attemp3” SHOULD work, however, it is unclear “what” the AdjustmentsBindingSource’s DataSource is. Is it a List<T> or DataTable?
If I set the BinngSource.DataSource to a DataTable, then attempt 3 appears to work. Below is an example that worked.
Private Sub PasteClipboard2()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
Next
End If
End Sub
This appears to work in my tests. I added a small function (RowHasData) to avoid malformed strings causing problems. It simply checks the size (at least 5 items) and also checks to make sure a row actually has “some” data. If a row is just empty strings, then it is ignored.
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
I am guessing it would be just as easy to add the new rows “directly” to the BindingSource’s DataSource. In the example below, the code is adding the row “directly” to the DataTable that is used as a DataSource to the BindingSource. I am confident you could do the same thing with a List<T> by simply adding a new object to the list. Below is a complete example using a BindingSource and a DataTable. This simply adds the rows to the bottom of the table.
Dim gridTable1 As DataTable
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PasteClipboard()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
gridTable1 = GetTable()
FillTable(gridTable1)
AdjustmentsBindingSource.DataSource = gridTable1
AdjustmentsDataGridView.DataSource = AdjustmentsBindingSource
End Sub
Private Function GetTable() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("FontName", GetType(String))
dt.Columns.Add("FontSize", GetType(String))
dt.Columns.Add("LetterCombo", GetType(String))
dt.Columns.Add("Adjustment", GetType(String))
dt.Columns.Add("HorV", GetType(String))
Return dt
End Function
Private Sub FillTable(dt As DataTable)
For index = 1 To 10
dt.Rows.Add("Name_" + index.ToString(), "Size_" + index.ToString(), "Combo_" + index.ToString(), "Adjust_" + index.ToString(), "HorV_" + index.ToString())
Next
End Sub
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
Try
Dim dataRow As DataRow
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
dataRow = gridTable1.NewRow()
dataRow("FontName") = Items(0)
dataRow("FontSize") = Items(1)
dataRow("LetterCombo") = Items(2)
dataRow("Adjustment") = Items(3)
dataRow("HorV") = Items(4)
gridTable1.Rows.Add(dataRow)
End If
Next
Catch ex As Exception
MessageBox.Show("Error: " + ex.Message)
End Try
End If
End Sub
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
Hope the code helps…
Last but important, I am only guessing that you may have not “TESTED” the different ways users can “SELECT” data and “how” other applications “copy” that selected data. My previous tests using the WIN-OS “Clipboard” can sometimes give unexpected results. Example, if the user selects multiple items using the ”Ctrl” key to “ADD” to the selection, extra rows appeared in the Clipboard if the selection was not contiguous. My important point is that using the OS clipboard is quirky IMHO. I recommend LOTS of testing on the “different” ways the user can select the data. If this is not an issue then the code above should work.
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 ...)
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)
I am using 3 unbound DataGridView controls to display certain information. To load the information into those DGVs, I am pulling the information from an encrypted file, decrypting it, parsing the information, then trying to fill the DGVs with that information. The loading from the file is called by the menu item click. Here is what I have so far:
Private Sub miCLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles miCLoad.Click
Dim FilePath As String = "C:\FList\CList.clt"
Dim LoadFile As New SaveandLoad.SaveAndLoad
Dim FileRead As New Simple3Des("MyPassword")
Dim FileString As String = FileRead.ReadFile(FilePath)
With LoadFile
.WhichList = dgCourses
.FilePath = FilePath
.DecryptedString = FileRead.DecryptData(FileString)
.dgList = dgCourses
End With
Call LoadFile.LoadFile()
End Sub
Public Class SaveandLoad
Public Property WhichList As New DataGridView
Public Property FilePath As String
Public Property DecryptedString As String
Public Property EncryptedString As String
Public Property dgList As Control
Public Sub LoadFile()
Dim dgRow As DataGridViewRow
Dim dgCell As DataGridViewTextBoxCell
Dim Lines() As String = DecryptedString.Split(vbLf)
Dim LinesList As List(Of String) = Lines.ToList
LinesList.RemoveAt(Lines.Length - 1)
For Each Line As String In LinesList
Dim Fields() As String = Line.Split(",")
dgRow = New DataGridViewRow
For x = 0 To (WhichList.Columns.Count - 1) Step 1
dgCell = New DataGridViewTextBoxCell
dgCell.Value = Fields(x).ToString
dgRow.Cells.Add(dgCell)
Next
WhichList.Rows.Add(dgRow)
Next
Select Case WhichList.Name
Case "dgCourses"
frmFacultyList.dgCourses = WhichList
frmFacultyList.dgCourses.Refresh()
WhichList.Dispose()
Case "dgFList"
frmFacultyList.dgFList = WhichList
frmFacultyList.dgFList.Refresh()
WhichList.Dispose()
Case "dgSList"
frmFacultyList.dgSList = WhichList
frmFacultyList.dgSList.Refresh()
WhichList.Dispose()
End Select
MsgBox("List Successfully Loaded", vbOKOnly, "Load")
End Sub
I want to be able to reference (or fill) a DGV without using 'select case' or 'if-then' statements. This will be too inefficient once I start adding the many other DGVs, that will be added in the future. Therefore, the title is the main question. I am using VS Express 2010.
I don't know VB too much, however, I'll post my solution in C# (may be helpfull in some way....)
DataGridView myDGV;
foreach (var item in this.Controls)
{
if (item.GetType() == typeof(DataGridView))
{
if (((DataGridView)item).Name == WhichList.Name)
{
//Cannot assing to 'item' here, because it is a 'foreach iteration variable'
//However you can save the variable for later use.
myDGV = (DataGridView)item;
}
}
}
myDGV = WhichList;
// different approach
DataGridView myDGV = (DataGridView)this.Controls.Find(WhichList.Name, false).First();
myDGV = WhichList;
Here is what worked for me in VB.NET:
Dim FormControls As New frmFacultyList.ControlCollection(frmFacultyList)
For Each DGV As DataGridView In FormControls
If WhichList.Name = DGV.Name Then
DGV = WhichList
DGV.Refresh()
End If
Next
Make an instance of the control collection then search specifically for DGVs using For Each. Simple and efficient.