I am having trouble sorting a listbox the way I am expecting it to work even using the sorted = true property. Assuming i have files named as such, when i sort by "Name" in a Windows Folder view (outside of vb) it sorts like so:
1180741
1179715
1162371
1141511
1131750
1117362
1104199
1082698
1062921
1043875
991514
970621
963154
952954
948067
917669
904315
899902
892398
882024
This is how i need it to be sorted in my list. However, with the sorted = true property, vb.net decides to sort like this:
1043875
1062921
1082698
1104199
1117362
1131750
1141511
1162371
1179715
1180741
882024
892398
899902
904315
917669
948067
952954
963154
970621
991514
I cannot understand why windows sorts the list correctly but VB does not. Any help would be most appreciated.
*This example assumes that each object in the listbox is a string representation of an integer, if in the case that it is not, the item will be skipped, you can modify this to handle such a case in another way if you want....
Example:
Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
Dim r As New Random(Now.Millisecond)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'We simulated a populated listbox
For I As Integer = 0 To 1000
Dim n As Integer = r.Next(0, Integer.MaxValue)
ListBox1.Items.Add(n.ToString)
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SortListBoxNumbers(ListBox1)
End Sub
Sub SortListBoxNumbers(listbox As ListBox)
Dim numbers As New List(Of Integer)
For Each item As Object In listbox.Items
Dim n As Integer = 0
If item.GetType = GetType(String) Then
If Integer.TryParse(DirectCast(item, String), n) Then
numbers.Add(n)
End If
End If
Next
listbox.Items.Clear()
numbers = SortNumbers(numbers)
For Each n As Integer In numbers
listbox.Items.Add(n.ToString)
Next
End Sub
Function SortNumbers(numbers As List(Of Integer)) As List(Of Integer)
Dim result As New List(Of Integer)
Dim count As Integer = numbers.Count
Do
Dim highN As Integer = Integer.MinValue
For Each n As Integer In numbers
If n > highN Then highN = n
Next
numbers.Remove(highN)
result.Insert(result.Count, highN)
Loop Until result.Count = count
result.Reverse()
Return result
End Function
End Class
Related
I'm trying to call this function into a procedure and am confused on why this is not working, I've looked up how to format a function call and don't understand my mistakes.
Structure Stock
Dim category As String
Dim price As Integer
Dim size As String
Dim sku As String
Dim color As String
End Structure
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim stockArray() As String =
IO.File.ReadAllLines("stockInventory.txt")
End Sub
Private Sub btnNewSave_Click(sender As Object, e As EventArgs) Handles btnNewSave.Click
Dim answer
answer = check(sku, stockArray)
End Sub
Function check(sku, stockArray) As Boolean
Dim flag As Boolean
Dim numVar = -1
numVar = Array.IndexOf(stockArray, txtSKU.Text)
If numVar = -1 Then
flag = False
End If
If numVar <> -1 Then
flag = True
End If
Return flag
End Function
It gives me an error saying,
sku is not declared. It may be inaccessible due to its protection
level
I have a structure where sku is defined as being a string. Do I have to declare it again in this sub for it to work?
You can fine-tune this code a bit:
Function check(ByVal stockArray as String()) As Boolean
Return Array.IndexOf(stockArray, txtSKU.Text) >= 0
End Function
It'll return if the element is in the Array.
If your file is very very large and you are doing many searches, I recommend storing in a HashSet instead of an array to get O(1) searching, instead of the O(n) you are getting.
I notice your stock array, has to be declared at the class level, not the method level, to be accessible by other methods.
so the code i ma trying to run is:
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
Dim size2 As Integer = ListBox1.Items.Count / 2
Dim size1 As Integer = ListBox1.Items.Count - size2
ListBox2.Items.AddRange(ListBox1.Items.GetRange(0, size1))
ListBox3.Items.AddRange(ListBox1.Items.GetRange(size1, size2))
End Sub
i have imported System.Linq but error occers getrange is not a member of listbox.objectcollection
There's no such thing as a GetRange() extension in Linq. The List(Of T) class has such a method, however. So you could use the Cast(Of TResult) extension to convert the collection into an IEnumerable(Of T), then use the ToList() extension to convert that into a List(Of T):
Dim ItemsList As List(Of Object) = ListBox1.Items.Cast(Of Object).ToList()
ListBox2.Items.AddRange(ItemsList.GetRange(0, size1))
ListBox3.Items.AddRange(ItemsList.GetRange(size1, size2))
Is it possible to get the OPCItem from a Group using the ClientHandle value?
Pseudo example:
Dim Server As OPCServer
Dim Groups As OPCGroups
Dim WithEvents Group1 as OPCGroup
Dim ItemGroup as OPCItems
Dim Item as OPCItem
Private Sub Form1_Load(sender as Object, e as EventArgs) Handles MyBase.Load
Server.Connect("MyServer")
Group1 = New OPCGroup()
Group1.Name = "Group1"
Group1.IsSubscribed = True
Group1.OPCItems.Add("Item1", 1)
Server.OPCGroups.Add(Group1)
End Sub
Private Sub Group1_DataChange(TransactionID As Integer, NumItems As Integer, ByRef ClientHandles As Array, ByRef ItemValues As Array, ByRef Qualities As Array, ByRef TimeStamps As Array) Handles Group1.DataChange
Dim Browser as OPCBrowser = Server.CreateBrowser()
Browser.ShowBranches()
Browser.DataType = vbInteger
Browser.ShowLeafs()
Dim qualityValue as Integer
For q As Integer = 1 To Qualities.Length
qualityValue = Qualities.GetValue(n)
If qualityValue = 192 Then
'HERE is where I want to get the OPCItem by using the ClientHandles
'I can use the ClientHandle to get the Value, but I'd also like to get the ItemID to do validation against.
Dim itemClientHandle as Integer = Convert.ToInt32(ClientHandles.GetValue(n))
Dim itemValueByClientHandle as String = ItemValues.GetValue(n).ToString()
End If
Next
'It is possible to use the OPCBrowser to get the Item Names but how do I correlate the two?
Dim itemNames as New List(Of String)
For n As Integer = 1 To OPCBrowser.Count
itemNames.Add(OPCBrowser.Item(i))
Next
' Do More Stuff
End Sub
There is a request to enhance an existing client application to be paused when a value is sent from the client to the OPC Server and wait for the bit tag to switch as in indication the process has finished. This part is built; however, there are multiple items (tags) in the group and there needs to be a way of identifying the correct items bit value was changed.
I have a code in VB.NET like this:
' This code will sort array data
Public Sub SelectionSort(ByVal array as ArrayList)
For i as Integer = 0 To array.Count -1
Dim index = GetIndexMinData(array, i)
Dim temp = array(i)
array(i) = array(index)
array(index) = temp
Next
End Sub
Public Function GetIndexMinData(ByVal array As ArrayList, ByVal start As Integer) As Integer
Dim index As Integer
Dim check As Integer = maxVal
For i As Integer = start To Array.Count - 1
If array(i) <= check Then
index = i
check = array(i)
End If
Next
Return index
End Function
' This code will sort array data
Public Sub SelectionSortNewList(ByVal array As ArrayList)
Dim temp As New ArrayList
' Process selection and sort new list
For i As Integer = 0 To array.Count - 1
Dim index = GetIndexMinData(array, 0)
temp.Add(array(index))
array.RemoveAt(index)
Next
End Sub
Private Sub btnProcess_Click(sender As System.Object, e As System.EventArgs) Handles btnProcess.Click
Dim data as new ArrayList
data.Add(3)
data.Add(5)
data.Add(1)
SelectionSort(data)
SelectionSortNewList(data)
End Sub
When I run this code, in the btnProcess event click, variable "data" is array = {3,5,1}. By SelectionSort(data) procedure, variable data is changed. Item in variable data have sorted by that procedure, so when SelectionSortNewList(data) is run, array "data" have sorted became {1,3,5}. Why does it happen?
Although I have used "Byval parameter" in SelectionSort and SelectionSortNewList, I don't want variable data to be changed when it pass to SelectionSort.
Even if you use a ByVal on an object, the properties of the object can be modified.
The instance of the object cannot be modified but not its properties.
Example:
Public Class Cars
Private _Make As String
Public Property Make() As String
Get
Return _Make
End Get
Set(ByVal value As String)
_Make = value
End Set
End Property
End Class
If I pass the class as ByVal;
Private sub Test(ByVal MyCar as Car)
MyCar.Make = "BMW"
End Sub
The value of the property will change as you're pointing to the same object and modifying its property.
In my Windows Forms application at runtime I will be resizing an array each time I add an element. So first I must resize to the size + 1, and then add a member to this index. How do I do this?
You could use the ReDim statement, but this really isn't your best option. If your array will be changing sizes often, especially as it sounds like you're just appending, you should probably use a generic List(Of T) or similar collection type.
You can use it just like you use an array, with the addition that adding an item to the end is as easy as MyList.Add(item)
To use a generic list, add Imports System.Collections.Generics to the top of the file. Then, you would declare a new integer list like this:
Dim MyList As New List(Of Integer)()
or a string list like this:
Dim MyList As New List(Of String)()
You should get the idea.
The suggested ReDim's need the Preserve keyword for this scenario.
ReDim Preserve MyArray(n)
Using a generic list is (as suggested) the best idea. If you however want to change the size of an Array, you can use Array.Resize(ByRef arr, newSize).
ReDim is not a good (pretty bad) idea (VB specific legacy, extremely slow).
I would prefer some type of collection class, but if you WANT to use an array do it like this:
Dim arr() As Integer
Dim cnt As Integer = 0
Dim ix As Integer
For ix = 1 To 1000
cnt = cnt + 1
ReDim arr(cnt)
arr(cnt - 1) = ix
Next
You can also make your own collection class. A good programming exercise for new programmers.
Public Class MyList
Private Items() As String
Private No As Integer = 0
Public Sub Add(ByVal NewItem As String)
''Create a temporary new string array
Dim CopyString(No) As String
''Copy values from Global Variable Items() to new CopyString array
For i As Integer = 0 To No - 1
CopyString(i) = Items(i)
Next
''Add new value - NewItem - to CopyString
CopyString(No) = NewItem
''Increment No to No + 1
No += 1
''Copy CopyString to Items
Items = CopyString
'Discard CopyString
CopyString = Nothing
End Sub
Public Sub Show(ByVal index As Integer)
MsgBox(Items(index))
End Sub
End Class
''Now create a form with a TextBox name - txt, Button1 and Button2
Public Class Form1
''Declare txts as a new MyList Class
Private txts As New MyList
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
''Add text to txts which is a MyList Class
txts.Add(txt.Text)
txt.Text = ""
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
''Display value at a specific index
txts.Show(Convert.ToInt16(txt.Text))
txt.Text = ""
End Sub
End Class
Use the ReDim command to specify the new size.
ReDim MyArray(MyArray.Length + 1)
As Joel says, use a list.
Dim MyList As New List(Of String)
Don't forget to change Of String to be Of whichever datatype you're using.
This work for me
Dim Table1 As New DataTable
' Define columns
Table1.Columns.Add("Column1", GetType(System.String))
Table1.Columns.Add("Column2", GetType(System.Int32))
Table1.Columns.Add("Column3", GetType(System.Int32))
' Add a row of data
Table1.Rows.Add("Item1", 44, 99)
Table1.Rows.Add("Item2", 42, 3)
Table1.Rows.Add("Item3", 42, 3)
Table1.Rows.Add("Item4", 42, 3)
Dim arr(-1) As String
For Each dr As DataRow In Table1.Rows
ReDim Preserve arr(arr.Length)
arr(arr.Length - 1) = dr("Column1")
Next