how to observe changes in an array in vb .net - arrays

i have an array A, i just want to monitor the changes in that array, return the changed position of that array.
myOldTextBox = myTextBox
myTextBox = New TextBox() {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5}
Dim i As Integer
For i = 0 To myTextBox.Length - 1
If myTextBox(i).Text <> myOldTextBox(i).Text Then
Dim fs As Integer
fs = farray.Length
farray(fs) = i
End If
Next i
i am newbie in vb .net. Thank you.

I don't believe you can do that.
For a regular variable I would suggest using get/set.
For an array, I would suggest creating a method to update the values, instead of setting the values directly (you can enforce this by making the array private and only giving it access through a get and set method).
In that method you can then do anything you want.
Pseudo code:
private _array
Public Function GetArray(ByVal key As String) As String
return _array(key)
End Function
Public Function SetArray(ByVal key As String, ByVal val As String) as String
_array(key) = val
return val;
End Function

add a listbox, and whenever the if condition is satisfied, add content to the listbox
myTextBox = New TextBox() {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5}
Dim i As Integer
For i = 0 To myTextBox.Length - 1
If myTextBox(i).Text <> myOldTextBox(i).Text Then
Dim fs As Integer
fs = farray.Length
farray(fs) = i
Listbox1.items.add(Format(now,"yyyy-MM-dd hh:mm:ss") & _
" array number changed: " & i
End If
Next i

EDITED: (Didn't notice you were doing what I posted)
I would change the For loop to a Do While loop, because I have found issues when comparing substring in a For Loop, try it and see if that doesn't fix your problem...
(I've even tried Microsoft code that failed in for Loops w/ substrings)
Also, I'd HIGHLY suggest you use a MessageBox.Show(sMsg) or Debug.WriteLine(sMsg) to ENSURE that data is correct...

Related

How Do You Return True If A String Contains Any Item In An Array Of String With LINQ in VB.NET

I could not find this question on stack overflow but if it is here, please let me know and I will take it down.
Using LINQ in VB.NET, how do you return True if a string contains one of the items in an array of strings?
This is this is the code in multiple lines. How do you do this in one line with LINQ in VB.NET?
Sub Main
Dim endPointTimeoutText As Array = {"endpoint timeout", "endpoint is not available"}
Dim strResult As String = "endpoint is not available sample text."
Dim booleanResult As Boolean = False
For Each item As String In endPointTimeoutText
If strResult.Contains(item) Then
booleanResult = True
Exit For
End If
Next
Console.WriteLine(booleanResult) 'Only included this for the example
End Sub
The expected result would be 'True' or 'False' depending on if the string (strResult) contained one of the values in the Array Of Strings (endPointTimeoutText)
You turn it around, mentally - don't ask "for this string X, which of these things in this array are in that string", you ask "for this array of strings, which of them are in this one string X":
Dim whichStringsArePresent = endPointTimeoutText.Where(Function(ett) strResult.Contains(ett))
Dim firstImeoutStringFound = endPointTimeoutText.FirstOrDefault(Function(ett) strResult.Contains(ett))
Dim wasATimeout = endPointTimeoutText.Any(Function(ett) strResult.Contains(ett))
etc
By the way it would make your code read more nicely if you make it so that Collections of things have plural names. Consider something more like this:
Dim wasATimeout = endPointTimeoutTexts.Any(Function(ett) strResult.Contains(ett))
It's subtle, but significant in terms of readability
Thank you Caius Jard for your help on this. I am going to post the complete program for what I'm going to use as the answer below.
I needed to use a List instead of an Array so that I could use the 'Any()' method. Thanks again Caius, I really appreciate it!
Sub Main
Dim endPointTimeoutText As String = "endpoint timeout,endpoint is not available"
Dim endPointTimeoutList As New List(Of String)
Dim strResult As String = "endpoint is not available sample text."
endPointTimeoutList = endPointTimeoutText.Split(",").ToList()
Dim areAnyStringsPresent As Boolean
areAnyStringsPresent = endPointTimeoutList.Any(Function(itemInEndPointTimeoutList) strResult.Contains(itemInEndPointTimeoutList))
Console.WriteLine(areAnyStringsPresent)
'This code produces the following output:
'True
End Sub

Storing & recovering 2-dimensional Array in a Class

I just recently moved from VB6 to VB.NET and I'm recoding an old app. So I'm pretty unexperienced with .NET so far.
I have multiple (lets say 4 in this code example) twodimensional string arrays (or actually an array of arrays) which I want to store as a ComboBox items ie. one twodimensional array is one item.
Public Class MyItem
Private sName As String
Private sArr As Array()
Public Sub New(ByVal Name As String, ParamArray Arr As Array())
sName = Name
sArr = Arr
End Sub
Public Property Arr() As Array()
Get
Return sArr
End Get
Set(ByVal sValue As Array())
sArr = sValue
End Set
End Property
Public Overrides Function ToString() As String
Return sName
End Function
End Class
---
Dim sMyArray as Array()
For i As Integer = 0 to 3
sMyArray = Nothing ' resetting the array before refilling it
'
' No code here but filling sMyArray by reading a text file, each line
' number as dim 1 and splitted each line into dim 2 with ";" using Split(sRead, ";")
' so Debub.Print(sMyArray(0)(0)) prints the beginning of the first line until first ";" <- this works fine
'
' Then passing sMyArray to a ComboBox item
'
ComboBox.Items.Add(New MyItem("item" & i, sMyArray))
Next i
The problem is that when recovering the arrays from ComboCox items only the last ComboBox item has array data. So for example
Dim sMyNewArray As Array() = ComboBox.Items.Item(0).Arr
Debug.Print(sMyNewArray(0)(0))
throws an error while
Dim sMyNewArray As Array() = ComboBox.Items.Item(3).Arr
Debug.Print(UBound(sMyNewArray(UBound(sMyNewArray))))
does not and prints the last item's last row's ubound
Can anyone figure out what is it I'm missing or tell me a better way to do this? I'm pretty sure there is one..
I'm not 100% sure, but I think the problem is in this section:
Dim sMyArray as Array()
For i As Integer = 0 to 3
sMyArray = Nothing ' resetting the array before refilling it
Arrays are technically reference types, but like strings, there's some extra compiler magic to make them feel at times more like value types, and I have a sense in this case the actual sMyArray reference was used (perhaps because of a ParamArrays optimzation), such that setting it to Nothing broke things. The more idiomatic way to write this code for .Net it like this:
For i As Integer = 0 to 3
Dim sMyArray as Array()
.Net has a much more sophisticated garbage collector than was available for VB6. We don't often set variables to Nothing any more, but instead just re-assign them or let them fall out of scope. In fact, setting a variable to Nothing can in rare cases be actively harmful. Moreover, we want to see the Dim keyword inside the loop, so you're working with a different variable on each iteration, with the smallest possible scope.
While I'm here, in .Net we pretty much never use the base Array type. Instead of this:
Private sArr As Array()
You pretty much always do this:
Private arr As String()()
or this, for true two-dimensional (non-jagged) arrays:
Private arr As String(,)
or, best of all, this:
Private arr As New List(Of String())
Since VB.Net has more collection types than just array.
Also, I don't have the link handy, but Microsoft's coding guidelines now explicitly ask you not to use hungarian warts for variable and class names (so sArr can just be arr). This is a change from the VB6 era because of changes to the language where the type is more likely to be implicit with the variable and improvements to the tooling, where the prefixes usually no longer add much utility and have been shown to hurt readability.
Not really sure why you have a 2 dimensional array, but here is a small sample NOT using the Array type. It uses just plain strings and string arrays. Let me know if this helps. This splits a few strings, then reads out the results after populating.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim sMyArray()() As String
Dim line1 As String = "a;b;c;d 1;2;3;4;5"
Dim line2 As String = "z;x;y;w 99;65;32;21;18"
sMyArray = ParseString(line1)
cboBox1.Items.Add(New MyItem("System0", sMyArray))
sMyArray = ParseString(line2)
cboBox1.Items.Add(New MyItem("System1", sMyArray))
For i As Integer = 0 To cboBox1.Items.Count - 1
For j As Integer = 0 To UBound(cboBox1.Items(i).arr)
For k As Integer = 0 To UBound(cboBox1.Items(i).arr(j))
Debug.Write(cboBox1.Items(i).arr(j)(k) & " ")
Next
Next
Debug.WriteLine("")
Next
End Sub
Private Function ParseString(s As String) As String()()
Dim rows As String() = s.Split(" ")
Dim matrix As String()() = New String(rows.Length - 1)() {}
For i As Integer = 0 To rows.Length - 1
matrix(i) = rows(i).Split(";")
Next
Return matrix
End Function
End Class
Public Class MyItem
Private sName As String
Private sArr As String()()
Public Sub New(ByVal Name As String, ByVal ParamArray Arr As String()())
sName = Name
sArr = Arr
End Sub
Public Property Arr() As String()()
Get
Return sArr
End Get
Set(ByVal sValue As String()())
sArr = sValue
End Set
End Property
Public Overrides Function ToString() As String
Return sName
End Function
End Class

Adding Item to Array (VB 2008)

The objective of the program is to interpret hockey statistics from a file using StreamReader and then display an added column of points. The following code kinda does so, however it’s ineffective in the sense that it doesn’t add the points value to the array - it separately outputs it. Looking for assistance as to how it would be possible to incorporate the points value into aryTextFile();
Dim hockeyFile, LineOfText, aryTextFile() As String
Dim i As Integer
Dim nameText(), NumberText(), goalsText(), assistsText(), GamesWonText() As String
Dim IntAssists(), IntGoals(), PointsText() As Single
hockeyFile = "C:\Users\Bob\Downloads\hockey.txt" 'state location of file
Dim objReader As New System.IO.StreamReader(hockeyFile) 'objReader can read hockeyFile
For i = 0 To objReader.Peek() <> -1 'reads each line seperately, ends when there is no more data to read
LineOfText = objReader.ReadLine 'stores seperate lines of data in HockeyFile into LineofText
aryTextFile = LineOfText.Split(",") 'takes lines and converts data into array
Name = aryTextFile(0) 'first piece of data in lines of text is the name
nameText(i) = aryTextFile(0)
If nameText(0) = "Name" Then
TextBox1.Text = LineOfText & ", Points." & vbCrLf 'displays first line fo text and adds "Points" label
End If
If Name <> "Name" Then 'when second line starts, then begin to intepret data
NumberText(i) = aryTextFile(1)
assistsText(i) = aryTextFile(2) 'assists are in third value of array
goalsText(i) = aryTextFile(3) 'goals are in fourth value of array
GamesWonText(i) = aryTextFile(4)
IntAssists(i) = Val(assistsText(i)) 'since our assists value is a string by default, it must be converted to a integer
IntGoals(i) = Val(goalsText(i)) 'since our goals value is a string by default, it must be converted to a integer
PointsText(i) = (IntGoals(i) * 2) + (IntAssists(i)) 'goals are two points, assists are one point
TextBox1.Text = TextBox1.Text & NumberText(i) & assistsText(i) & goalsText(i) & GamesWonText(i) & PointsText(i) & vbCrLf 'Displays points as last value in each line
End If
Next i
This should get you pretty close:
It'll need extra validation. It doesn't take into account whatever value you have between the name and the goals.
Private Sub ProcessHockeyStats()
Try
Dim inputFile As String = "c:\temp\hockey.txt"
Dim outputFile As String = "c:\temp\output.txt"
If Not File.Exists(inputFile) Then
MessageBox.Show("Missing input file")
Return
End If
If File.Exists(outputFile) Then
File.Delete(outputFile)
End If
Dim lines() As String = File.ReadAllLines(inputFile)
Dim output As List(Of String) = New List(Of String)
Dim firstLine As Boolean = True
For Each line As String In lines
Dim values() As String = line.Split(","c)
Dim points As Integer
If firstLine Then
output.Add("Name, Assists, Goals, Points")
firstLine = False
Else
'needs validation for values
points = CInt(values(1) * 2) + CInt(values(2))
output.Add(String.Concat(line, ",", points))
End If
Next
File.WriteAllLines("c:\temp\outfile.txt", output)
Catch ex As Exception
MessageBox.Show(String.Concat("Error occurred: ", ex.Message))
End Try
End Sub
VS2008 is ancient, especially when later versions of Visual Studio are free. I felt like showing an implementation using more-recent code. Like others, I strongly support building a class for this. The difference is my class is a little smarter, using the Factory pattern for creating instances and a Property to compute Points as needed:
Public Class HockeyPlayer
Public Property Name As String
Public Property Number As String
Public Property Assists As Integer
Public Property Goals As Integer
Public Property Wins As Integer
Public ReadOnly Property Points As Integer
Get
Return (Goals * 2) + Assists
End Get
End Property
Public Shared Function FromCSVLine(line As String) As HockeyPlayer
Dim parts() As String = line.Split(",")
Return New HockeyPlayer With {
.Name = parts(0),
.Number = parts(1),
.Assists = CInt(parts(2)),
.Goals = CInt(parts(3)),
.Wins = CInt(parts(4))
}
End Function
End Class
Dim hockeyFile As String = "C:\Users\Bob\Downloads\hockey.txt"
Dim players = File.ReadLines(hockeyFile).Skip(1).
Select(Function(line) HockeyPlayer.FromCSVLine(line)).
ToList() 'ToList() is optional, but I included it since you asked about an array
Dim result As New StringBuilder("Name, Number, Assists, Goals, Wins, Points")
For Each player In players
result.AppendLine($"{player.Name}, {player.Number}, {player.Assists}, {player.Goals}, {player.Wins}, {player.Points}")
Next player
TextBox1.Text = result.ToString()
I was gonna give you VS 2008 version afterward, but looking at this, the only thing here you couldn't do already even by VS 2010 was string interpolation... you really should upgrade.
Parallel arrays are really not the way to handle this. Create a class or structure to organize the data. Then create a list of the class. The list can be set as the DataSource of a DataGridView which will display your data in nice columns with headings matching the names of your properties in the Hockey class. You can easily order your data in the HockeyList by any of the properties of Hockey.
Public Class Hockey
Public Property Name As String
Public Property Number As String
Public Property Goals As Integer
Public Property Assists As Integer
Public Property Points As Integer
Public Property GamesWon As Integer
End Class
Private HockeyList As New List(Of Hockey)
Private Sub FillListAndDisplay()
Dim path = "C:\Users\Bob\Downloads\hockey.txt"
Dim Lines() = File.ReadAllLines(path)
For Each line As String In Lines
Dim arr() = line.Split(","c)
Dim h As New Hockey()
h.Name = arr(0)
h.Number = arr(1)
h.Assists = CInt(arr(2).Trim)
h.Goals = CInt(arr(3).Trim)
h.GamesWon = CInt(arr(4).Trim)
h.Points = h.Goals * 2 + h.Assists
HockeyList.Add(h)
Next
Dim orderedList = (From scorer In HockeyList Order By scorer.Points Ascending Select scorer).ToList
DataGridView1.DataSource = orderedList
End Sub

Function caller code VBA

Problem
When you want to look directly at the arguments of your UDF (not their values, which can be passed directly, but the formula that gave these values), you can use Application.Caller.Formula and parse out the arguments to find out.
Is there any way to see the line of VBA code which called a Function, so that you can parse out its arguments in a similar way?
Background
A while ago I created a UDF which was essentially another approach to array functions*. What I wanted to do was take some statement which evaluates to True/False
LEN(A1)>LEN(B1)
And evaluate it over an array. So say the above function was placed in cell A1, then to evaluate over the array A1:A100 would be the same as creating the array
{LEN(A1)>LEN(B1),LEN(A2)>LEN(B2),[...]} 'you may recognise this as an array formula ={LEN(A1:A100)>LEN(B1:B100)}
*For context, this was before I knew about array formulae
I was frustrated with the syntax of certain array-handling Excel functions, like COUNTIF, which takes the arguments in the following form
COUNTIF(range_To_Evalueate_Over, "string_Representing_Boolean_Test")
The string argument presents the following limitations
Not any boolean returning statement can be used as a test; there is no way of looking at properties of the range which you evaluate over other than their values
So you can't use functions like LEN() to get more data about the range
You can not reference other cells relative to the range (Like B1 relative to A1)
The string is static at runtime, you cannot step-into the function to see what the string will evaluate to for a given cell from the range you are evaluating
I much prefer the versatility of the conditional formatting formulae. They take the form of array formulae, where any offsets (B1 relative to A1) are calculated relative to the TL cell of the range that the conditional formatting is applied to.
That prompted me to create a UDF which has a structure like this
evaluateOverRange(range_to_evalute_over As Range, boolean_test_on_TL_Cell As Boolean) As Boolean() 'returns an array equal in size to the evaluate range
Used like
evaluateOverRange(A1:A100,LEN(A1)<LEN(B1))
Note
Boolean test is not a string, so can be evaluated step by step in Excel
Boolean test is guaranteed to be Boolean thanks to type declaration
Boolean test is relative to the first cell (A1) in the evaluate range (A1:A100)
I.e. B1 is replaced with A1.Offset(0,1)
Since boolean_test_on_TL_Cell is not a string, it tells us nothing about the actual test, it just passes the result of the test on the A1, it is actually useless within the UDF so is ignored
To obtain the test string "LEN(A1)<LEN(B1)", the Application.Caller.Formula is read, and the relevant argument of evaluateOverRange is parsed out
In order to evaluate some worksheet function over an array in VBA, you can use the Evaluate method
Dim colA As Range: Set colA = [A1:A100] 'range_to_evaluate_over in my udf
Dim cellA As Range
Dim cellB as Range
Dim outputArray(1 To 100) As Boolean
For i = 1 To 100
Set cellA = colA(i)
Set cellB = cellA.Offset(0,1) 'all cells that arent the TL cell in colA (i.e., not A1) are set relative to the top left cell
outputArray(i) = Evaluate("LEN(" & cellA.Value & ")>LEN(" & cellB.Value ")")
Next i
Right, so all that was for worksheet functions, and somewhat pointless given array functions do the same thing. But now I want to use the same approach within VBA.
Specifically, I want to filter an array of custom classes based on some function of their properties, using actual VBA Boolean returning code rather than a string.
Sub FilterMyClassArray() 'Prints how many items in arrayToFilter whose properties match certain conditions
Dim arrayToFilter(1 To 100) As New myClass
Dim filteredArray() As myClass
Dim tlClass As myClass 'pretend class used only for intellisense and to create
boolean test
Set filteredArray = filterClassArray(arrayToFilter, tlClass.PropertyA > 3 And
tlClass.PropertyB = "hat")
Debug.Print "Number left after filtering:" ; Ubound(filteredArray)
End Sub
Function filterClassArray(ByVal inutArray() As myClass, classTest As Boolean) As myClass 'returns an output array which is equal to the input array filtered by some test
'Somehow get what classTest actually was
'Evaluate classTest over each item in inputArray
'If boolean test evaluates to true, add to output array, otherwise skip
End Function
I imagine some manipulation of the code modules will be required (both to get the string of code which represents the test, and to actually evaluate it), but I want to check feasibility before I dig too deep.
I've been having a think about this and a solution might be possible if you'd be prepared to use something approximating a Linq syntax.
If I understand the requirements correctly, you need to:
obtain a string value for each property name,
record the evaluation and ultimately run it as a string,
have intellisense access to the properties, and
have the ability to debug the evaluation on each iteration.
Regarding #1 and #3, the only way of doing this in VBA would be to code the values manually. If you code them in your class then the class can become cumbersome and some might say it compromises the single responsibility principle (https://en.wikipedia.org/wiki/Single_responsibility_principle). If you code them in a separate 'container' (eg class, type, collection, etc.), then there's a risk of some being missed or of corruption if you change the property names. An Interface class might mitigate these issues.
For #2, I can't see any way around it: the evaluation must be entered as a string. An enum (and associated intellisense) might alleviate things a bit though.
Item #4 is purely a coding architecture issue.
First the syntax
I'm sure there are VBA solutions on the internet which implement a pretty decent mock-up of Linq, but further down is a skeleton version to give you the idea. The end result is that your query syntax could look like this:
Dim query As cLinq
Dim p As INameable
Dim arrayToFilter(1 To 100) As INameable
Dim filteredArray() As INameable
Set query = New cLinq
With query
.SELECT_USING_INTERFACE p
.FROM arrayToFilter
.WHERE p.PropertyA, EQUAL_TO, 3
.AND_WHERE p.PropertyB, EQUAL_TO, "hat"
filteredArray = .EXECUTE
End With
The interface
As far as VBA is concerned an interface is really just a class module with a list of properties and methods that you want a class to implement. In your case, I've created a class and called it INameable, with the following sample code to match your example:
Option Explicit
Public Property Get PropertyA() As Long
End Property
Public Property Let PropertyA(RHS As Long)
End Property
Public Property Get PropertyB() As String
End Property
Public Property Let PropertyB(RHS As String)
End Property
Your MyClass class then implements this interface. For the sake of consistency, I've called the class cMyClass:
Option Explicit
Implements INameable
Private mA As Long
Private mB As String
Private Property Let INameable_PropertyA(RHS As Long)
mA = RHS
End Property
Private Property Get INameable_PropertyA() As Long
INameable_PropertyA = mA
End Property
Private Property Let INameable_PropertyB(RHS As String)
mB = RHS
End Property
Private Property Get INameable_PropertyB() As String
INameable_PropertyB = mB
End Property
I've created a second class, called cNames, which also implements the interface, and this one produces the string names of the properties. As a quick and dirty method it just stores the name of the last property used:
Option Explicit
Implements INameable
Private mName As String
Private Property Let INameable_PropertyA(RHS As Long)
End Property
Private Property Get INameable_PropertyA() As Long
mName = "PropertyA"
End Property
Private Property Let INameable_PropertyB(RHS As String)
End Property
Private Property Get INameable_PropertyB() As String
mName = "PropertyB"
End Property
Public Property Get CurrentName() As String
CurrentName = mName
End Property
You wouldn't have to use an interface and some might argue it's not necessary or even correct to do so, but at least it gives you an idea of how it could be implemented if you went this route.
The Linq class
The final class is really just a helper class to create the intellisense syntax you need and to process the evaluation. It's by no means thorough, but might get you started if the idea appealed to you. I've called this class cLinq:
Option Explicit
'Enumerator to help with intellisense.
Public Enum Operator
EQUAL_TO
GREATER_THAN
LESS_THAN
GREATER_OR_EQUAL_TO
LESS_OR_EQUAL_TO
NOT_EQUAL_TO
End Enum
Private mP As cNames
Private mQueries As Collection
Private mByAnd As Boolean
Private mFromArray As Variant
Public Sub SELECT_USING_INTERFACE(p As INameable)
'Insantiate the name of properties class.
Set mP = New cNames
Set p = mP
End Sub
Public Sub FROM(val As Variant)
'Array containing objects to be interrogated.
mFromArray = val
End Sub
Public Sub WHERE(p As Variant, opr As Operator, val As Variant)
'First query.
Set mQueries = New Collection
AddQuery opr, val
End Sub
Public Sub AND_WHERE(p As Variant, opr As Operator, val As Variant)
'Subsequent query using AND.
mByAnd = True
AddQuery opr, val
End Sub
Public Sub OR_WHERE(p As Variant, opr As Operator, val As Variant)
'Subsequent query using OR.
mByAnd = False
AddQuery opr, val
End Sub
Public Function EXECUTE() As Variant
Dim o As Object
Dim i As Long
Dim result As Boolean
Dim matches As Collection
Dim output() As Object
'Iterate the array of objects to be checked.
Set matches = New Collection
For i = LBound(mFromArray) To UBound(mFromArray)
Set o = mFromArray(i)
result = EvaluatedQueries(o)
If result Then matches.Add o
Next
'Transfer matched objects to an array.
ReDim output(0 To matches.Count - 1)
i = LBound(output)
For Each o In matches
Set output(i) = o
i = i + 1
Next
EXECUTE = output
End Function
Private Function EvaluatedQueries(o As Object) As Boolean
Dim pep As Variant, val As Variant
Dim evalString As String
Dim result As Boolean
For Each pep In mQueries
'Obtain the property value by its string name
val = CallByName(o, pep(0), VbGet)
'Build the evaluation string.
evalString = ValToString(val) & pep(1)
'Run the evaluation
result = Evaluate(evalString)
'Exit the loop if AND or OR conditions are met.
If mQueries.Count > 1 Then
If (mByAnd And Not result) Or (Not mByAnd And result) Then Exit For
End If
Next
EvaluatedQueries = result
End Function
Private Sub AddQuery(opr As Operator, val As Variant)
Dim pep(1) As Variant
'Create a property/evaluation pair and add to collection,
'eg pep(0): "PropertyA", pep(1): " = 3"
pep(0) = mP.CurrentName
pep(1) = OprToString(opr) & ValToString(val)
mQueries.Add pep
End Sub
Private Function OprToString(opr As Operator) As String
'Convert enum values to string operators
Select Case opr
Case EQUAL_TO
OprToString = " = "
Case GREATER_THAN
OprToString = " > "
Case LESS_THAN
OprToString = " < "
Case GREATER_OR_EQUAL_TO
OprToString = " >= "
Case LESS_OR_EQUAL_TO
OprToString = " <= "
Case NOT_EQUAL_TO
OprToString = " <> "
End Select
End Function
Private Function ValToString(val As Variant) As String
Dim result As String
'Add inverted commas if it's a string.
If VarType(val) = vbString Then
result = """" & val & """"
Else
result = CStr(val)
End If
ValToString = result
End Function

Array of pictureboxes and resource images

Hey all i have the following code:
Dim radarStrengthImages() As PictureBox = ({imgRadar_Strength1, imgRadar_Strength2, imgRadar_Strength3, imgRadar_Strength4, imgRadar_Strength5, imgRadar_Strength6, imgRadar_Strength7, imgRadar_Strength8})
Dim radarStrengthResourcesON() As Bitmap = ({My.Resources.radarON_16, My.Resources.radarON_17, My.Resources.radarON_18, My.Resources.radarON_19, My.Resources.radarON_20, My.Resources.radarON_21, My.Resources.radarON_22, My.Resources.radarON_23})
Dim radarStrengthResourcesOFF() As Bitmap = ({My.Resources.radar_16, My.Resources.radar_17, My.Resources.radar_18, My.Resources.radar_19, My.Resources.radar_20, My.Resources.radar_21, My.Resources.radar_22, My.Resources.radar_23})
The imgRadar_StrengthX is the name of the pictureboxes on the form itself and My.Resources.radar_XX is the image for the pictureboxes.
However when i use this code:
Dim intX As Integer = 0
Do Until intX = 8
radarStrengthImages(intX).Image = radarStrengthResourcesON(intX)
intX += 1
Loop
I get an error of:
Object reference not set to an instance of an object
and that happens on this like:
radarStrengthImages(intX).Image = radarStrengthResourcesON(intX)
This kind of code can't work, initialization order is always an important detail. The variables you use don't get a value until after the InitializeComponent() method runs. But the arrays are initialized before that happens. So you just initialize them with Nothing, nada, zippo. "Object reference not set" is the zippo exception you'll get.
You'll have to do it later, that requires moving the initializer for the array into the constructor. Generic syntax for a sample form with textboxes:
Public Class Form1
Dim boxes As TextBox()
Public Sub New()
InitializeComponent()
boxes = New TextBox() {TextBox1, TextBox2, TextBox3}
End Sub
End Class
issue is this array start at index 0 and you have 8 items
change the loop to
Do Until intX = 7
and it should now work
or if the array will change in time, use a variable to handle the max
Module Module1
Sub Main()
Dim intX As Integer = 0
Dim test(7) As Integer '8 item
Dim max = test.Length - 1
Do Until intX = max
intX += 1
Loop
Console.WriteLine("intX: " & intX)
Console.ReadKey()
End Sub
End Module

Resources