VB: Check if Object has “Class” then Execute Code - vb.net

I am looking to find something in Visual Basic that basically states that if something collides with something with a certain class, it executes code. I may not be using correct terms, so when I say object I mean like a label or picture box, and a class is... well like a class in HTML, I guess, like an object with a trait.
The pseudocode would look something like this:
If object.Bounds.IntersectsWith([object with certain class]) Then execute code
Otherwise, the game's 'if' statements will overwhelm... Also I am new to Visual Basic language, so please keep it as simple as possible.
Here is what I have already:
If carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 1 Then
directiona = 0
carblue.Top += 2
ElseIf carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 2 Then
directiona = 0
carblue.Left += 2
ElseIf carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 3 Then
directiona = 0
carblue.Top -= 2
ElseIf carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 4 Then
directiona = 0
carblue.Left -= 2
End If
Where carblue is the object being controlled, boundary1 is the obstacle that stops the car from moving (on collision) and directiona is the value of the direction the car is travelling (1 is up, 2 is left, etc).
(Moved from S.A. Programmers site)

Without actual sample code to work with, it's not easy to provide a specific solution. I don't know how many controls you have moving around, is it 2? 10? 10,000? Assuming you have 10+ controls moving around, this is how I would handle it.
I would use a datatable to keep a record of the Bounds for each Control that moves or is collidable. After a control moves, update that row in the datatable, look for collisions, then check the object types to determine what kind of class the collided object is, and then run the code as needed.
Public MovingControls As DataTable
Sub Main()
'Build the DataTable
MovingControls = New DataTable("ControlBounds")
MovingControls.Columns.Add("Name", GetType(String))
MovingControls.Columns.Add("x1", GetType(Integer))
MovingControls.Columns.Add("x2", GetType(Integer))
MovingControls.Columns.Add("y1", GetType(Integer))
MovingControls.Columns.Add("y2", GetType(Integer))
End Sub
'Call this only when a control/object is created
Sub MovingControlAdded(sender As Control)
Dim Row As DataRow = MovingControls.NewRow
Row("Name") = sender.Name
Dim BoundsRect As Drawing.Rectangle = sender.Bounds
Row("x1") = sender.Bounds.Left
Row("x2") = sender.Bounds.Right
Row("y1") = sender.Bounds.Bottom
Row("y2") = sender.Bounds.Top
MovingControls.Rows.Add(Row)
End Sub
'Call this only when a control/object has moved
Sub MovingControlMoved(sender As Control)
'Update the location of this Control
Dim Row() As DataRow = MovingControls.Select("Name = '" & sender.Name & "'")
'Select returns an array of Rows but there should only be 1 row for each Control
Row(0)("x1") = sender.Bounds.Left
Row(0)("x2") = sender.Bounds.Right
Row(0)("y1") = sender.Bounds.Bottom
Row(0)("y2") = sender.Bounds.Top
'Collision check
Dim CollidedRows() As DataRow = MovingControls.Select("(" & sender.Bounds.Right & " >= x1)" &
"AND (" & sender.Bounds.Left & " <= x2)" &
"AND (" & sender.Bounds.Bottom & " <= y2)" &
"AND (" & sender.Bounds.Top & " >= y1)" &
"AND (Name <> '" & sender.Name & "'")
'Determine the object type and execute necessary code
For Each CollidedRow As DataRow In CollidedRows
Dim CollidedControl As Control = Me.Controls.Item(CollidedRow("Name"))
If CollidedControl.GetType = GetType(Label) Then
'Do stuff for labels
ElseIf CollidedControl.GetType = GetType(Button) Then
'Do stuff for buttons
End If
Next
End Sub
Caveat: This assumes 1 control is moving at a time. If Control A moves into Control B but Control B moves away at the same time, this code will still call a collision even though the collision was avoided. If you have multiple controls moving, you may want to split the MovingControlMoved method into 2 methods, one for updating the table and one for collision checks. Handle all movement first, then handle all collisions.
Depending on the complexity, you may want to create custom classes for collidable controls that inherit an interface for collisions. You can use System.Reflection to call RunMeOnCollision. This would eliminate the list of If statements.
Interface iCollidableBase
Sub RunMeOnCollision()
End Interface
Public Class CollidableLabel
Inherits Label
Implements iCollidableBase
Public Sub RunMeOnCollision() Implements iCollidableBase.RunMeOnCollision
Me.Text = "I have been collided"
End Sub
End Class
Public Class CollidableButton
Inherits Button
Implements iCollidableBase
Public Sub RunMeOnCollision() Implements iCollidableBase.RunMeOnCollision
Me.Text = "Ouch, that collision hurt!"
End Sub
End Class
Again, not knowing the full context here, I can't test my solution against your code. If you can post some additional details, I might be able to help more.
-E

I found what I needed from my programming teacher, using arrays.
In the public sub:
Dim walls(17) As PictureBox
Where 17 is the number of (In this case) boundaries that your form has. Also change PictureBox to the object you are using (for example labels).
Then in your form load:
For i = 1 To 17
walls(i) = Me.Controls("boundary" & i)
Next
I'm honestly not 100% sure about this, but the "boundary" string is a section of the name of my PictureBoxes, that are acting as boundaries. There names are boundary1, boundary2, etc. So you might change "boundary" to what your object's names are.
Next, when you want to check for something using the boundaries, declare
For i = 1 To 17
Then when you want to close checking for it,
Next
For the checking of collisions, use this:
If object.Bounds.IntersectsWith(walls(i).Bounds) Then
So in this case, the If statement would go between the For and Next lines.

Related

How can i check for a character after certain text within a listbox?

How can i check for a character after other text within a listbox?
e.g
Listbox contents:
Key1: V
Key2: F
Key3: S
Key4: H
How do I find what comes after Key1-4:?
Key1-4 will always be the same however what comes after that will be user defined.
I figured out how to save checkboxes as theres only 2 values to choose from, although user defined textboxes is what im struggling with. (I have searched for solutions but none seemed to work for me)
Usage:
Form1_Load
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
Which textbox1.text would then contain V / whatever the user defined.
I did try something that kind of worked:
Form1_Load
Dim UsersKey as string = "V"
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
but i'm not sure how to add additional letters / numbers to "V", then output that specific number/letter to the textbox. (I have special characters blocked)
Reasoning I need this is because I have created a custom save settings which saves on exit and loads with form1 as the built in save settings doesn't have much customization.
e.g Can't choose save path, when filename is changed a new user.config is generated along with old settings lost.
Look at regular expressions for this.
Using the keys from your sample:
Dim keys As String = "VFSH"
Dim exp As New RegEx("Key[1-4]: ([" & keys& "])")
For Each item As String in ListBox1.Items
Dim result = exp.Match(item)
If result.Success Then
TextBox1.Text = result.Groups(1).Value
End If
Next
It's not clear to me how your ListBoxes work. If you might find, for example, "Key 2:" inside ListBox1 that you need to ignore, you will want to change the [1-4] part of the expression to be more specific.
Additionally, if you're just trying to exclude unicode or punctuation, you could also go with ranges:
Dim keys As String = "A-Za-z0-9"
If you are supporting a broader set of characters, there are some you must be careful with: ], \, ^, and - can all have special meanings inside of a regular expression character class.
You have multiple keys, I assume you have multiple textboxes to display the results?
Then something like this would work. Loop thru the total number of keys, inside that you loop thru the alphabet. When you find a match, output to the correct textbox:
Dim UsersKey As String
For i As Integer = 1 To 4
For Each c In "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
UsersKey = c
If ListBox1.Items.Contains("Key" & i & ": " & UsersKey) Then
Select Case i
Case 1
TextBox1.Text = UsersKey
Case 2
TextBox2.Text = UsersKey
Case 3
TextBox3.Text = UsersKey
Case 4
TextBox4.Text = UsersKey
End Select
Exit For 'match found so exit inner loop
End If
Next
Next
Also, you say your settings are lost when the filename is changed. I assume when the version changes? The Settings has an upgrade method to read from a previous version. If you add an UpgradeSettings boolean option and set it to True and then do this at the start of your app, it will load the settings from a previous version:
If My.Settings.UpgradeSettings = True Then
My.Settings.Upgrade()
My.Settings.Reload()
My.Settings.UpgradeSettings = False
My.Settings.Save()
End If
Updated Answer:
Instead of using a listtbox, read the settings file line by line and output the results to the correct textbox based on the key...something like this:
Dim settingsFile As String = "C:\settings.txt"
If IO.File.Exists(settingsFile) Then
For Each line As String In IO.File.ReadLines(settingsFile)
Dim params() As String = Split(line, ":")
If params.Length = 2 Then
params(0) = params(0).Trim
params(1) = params(1).Trim
Select Case params(0)
Case "Key1"
Textbox1.Text = params(1)
Case "Key2"
Textbox2.Text = params(1)
End Select
End If
Next line
End If
You can associate text box with a key via its Name or Tag property. Lets say you use Name. In this case TextBox2 is associated with key2. TextBox[N] <-> Key[N]
Using this principle the code will look like this [considering that your list item is string]
Sub Test()
If ListBox1.SelectedIndex = -1 Then Return
Dim data[] As String = DirectCast(ListBox1.SelectedItem, string).Split(new char(){":"})
Dim key As String = data(0).Substring(3)
Dim val As String = data(1).Trim()
' you can use one of the known techniques to get control on which your texbox sits.
' I omit this step and assume "Surface1" being a control on which your text boxes sit
DirectCast(
(From ctrl In Surface1.Controls
Where ctrl.Name = "TextBox" & key
Select ctrl).First()), TextBox).Text = val
End Sub
As you can see, using principle I just explained, you have little parsing and what is important, there is no growing Select case if, lets say, you get 20 text boxes. You can add as many text boxes and as many corresponding list items as you wish, the code need not change.

How do I display the result of a loop on a new line in a text box?

Basically, how do I write a new line in a text box, keeping the existing information as well.
If I have for loop,
For i As Integer = 1 To 10
Dim result = i
i = i + 1
textbox1.text = result
Next
This will display '10' in the textbox. I want it to be like:
1
2
3
4
...
First, your TextBox must allow multiple lines. This is a property of the textbox control that you can set from the designer or from the code. You may want to ensure that a scroll bar is there to scroll in case the height is not large enough.
If you want to set the properties from code, use this code in the Load event of the form.
' Set the Multiline property to true.
textBox1.Multiline = True
' Add vertical scroll bars to the TextBox control.
textBox1.ScrollBars = ScrollBars.Vertical
' Change the height of the textbox so that it could accomodate the lines
TextBox1.Height = 120
Now, your approach had a major problem in this line:
textbox1.text = result
The way you coded it, every new value of i, would overwrite the old value. What you want to do is to first construct a string, then send the entire string to the TextBox control. This is not required had you been using Console.WriteLine method.
Method 1
Dim s as string
s=""
For i As Integer = 1 To 10
s = s & Environment.Newline & i.ToString() 'we use Environment.NewLine to force new line
Next i
textbox1.text = s
Method 2
.NET offers a class to handle strings better than the way we did before. It won't matter in your case but it is the efficient way to handle concatenation when volume is large and/or performance matters
Dim s as new System.Text.StringBuilder() 'Initialize stringbuilder instance
For i As Integer = 1 To 10
s.AppendLine (i.ToString()) 'We use stringbuilder to concat. and inser line feed
Next i
textbox1.text = s.ToString()
Note: If you want double spacing then you need to add a linefeed (using & ) to both of the above methods.
Something like this should work:
For i As Integer = 1 To 10
if i = 1 then
textbox1.text = i
else
textbox1.text &= vbcrlf & i
end if
Next
For i = 1 To 10
textbox1.AppendText(vbNewLine & i)
Next

Reading ListView from BackgroundWorker

I've tried doing some searchs but for the life of me I don't seem to be able to find the answer, or a suggested solution that works. Its probably my understanding, but hopefully asking my own question will give me an answer that works :o)
I have a Windows Form Application which consists of one item, ListView1
This ListView has items added to it from a file via a Drag / Drop which is done on the main UI Thread, no background worker, it consists of around 1500 rows.
I'm trying to get a background worker now to read this ListView, but I'm getting a Cross Threading error as ListView1 was not created on the same thread.
The error comes on the simplest of pieces of code, but I don't seem to be able to think of a way around it or implementing an invoke etc.
For i = 0 To Me.ListView1.Items.Count - 1
ValueStatement = ValueStatement & "(" & Me.ListView1.Items(i).SubItems(0).Text
If i = Me.ListView1.Items.Count - 1 Or counter = 500 Then
CommaTerminate = ";"
Else
CommaTerminate = ","
End If
For y = 0 To Me.ListView1.Columns.Count - 1
ValueStatement = ValueStatement & "'" & Me.ListView1.Items(i).SubItems(y).Text & "'"
If y = Me.ListView1.Columns.Count - 1 Then
ValueStatement = ValueStatement & ")"
Else
ValueStatement = ValueStatement & ","
End If
Next
ValueStatement = ValueStatement & CommaTerminate & vbNewLine
If counter = 500 Then
SQLStatement = "INSERT INTO RAW_CLI_DATA_" & GlobalVariables.CDR_Company & " VALUES " & vbNewLine & ValueStatement
GenericDatabaseRequest(SQLStatement, "Loading RAW table with data..")
counter = 0
ValueStatement = ""
End If
counter = counter + 1
Next
The error comes on the line ValueStatement = ValueStatement & "(" & Me.ListView1.Items(i).SubItems(0).Text
Thanks for any help!
It sounds like you went down the wrong road early on. The ListView is supremely illsuited for database ops:
Everything is contained as String which means somewhere you will have code to convert it to other types
It does not support databinding which means you have to manually create rows...
... then later iterate them to fish the data back out.
A DataGridView and DataTable would be simpler: When the user enters data into the control and it would be stored in the table and as the proper type. Setting the DataTable as the Datasource, the DataGridView would create the display (rows and columns) for you.
Some of the time it takes will be consumed by SQLite to perform the INSERT, but it also looks like you are spending a lot of time iterating and concatenating SQL. It's usually better to work with the data than the user's View of it anyway, so extract and pass the data to the worker.
First, suck the data out of the ListView into a String()() container:
Dim data = lv.Items.
Cast(Of ListViewItem).
Select(Function(s) New String() {s.SubItems(0).Text,
s.SubItems(1).Text,
s.SubItems(2).Text}).
ToArray()
Then pass it to the BackGroundWorker:
bgw.RunWorkerAsync(data)
The DoWork Event:
Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgw.DoWork
' unbox the data
Dim dataToInsert = CType(e.Argument, String()())
For n As Int32 = 0 To 2
Console.WriteLine("[{0}], [{1}], [{2}]", dataToInsert(n)(0),
dataToInsert(n)(1),
dataToInsert(n)(2))
Next
End Sub
Results:
[Patient Tempest], [Lorem ipsum dolor sit], [Swordfish]
[Sour Priestess], [hendrerit nibh tempor], [Perch]
[Frozen Justice], [Interdum ex felis], [Swordfish]
It correctly prints the random data I put into the LV.
This will allow you to process the ListView Data in the BackGroundWorker but it wont really save any time, it just keeps the UI unlocked. The real problem is elsewhere, probably in the DB ops.

Keeping Dim value after if statement

I've been programming for about two months now. I have a problem with keeping a dim value after an if statement. I have a loop that if doesn't add up to 100 (it is for a percentage) stops entering in a radgrid. My work around for this is to store the value in a textbox but this hardly seems efficient. So how do i store this value in memory as opposed to in a textbox. Here is the code.
Dim p As Integer = RadGridView1.CurrentRow.Index - 1
Try
For int1 = p To 0 Step -1
If RadGridView1.Rows(Row).Cells(1).IsCurrent AndAlso Me.RadGridView1.IsInEditMode AndAlso RadGridView1.ActiveEditor.Value <> 0 Then
If RadGridView1.Rows(int1).Cells(1).Value <> Nothing Then Exit For
Dim total As String = 0
total += RadGridView1.Rows(int1).Cells("PC").Value
Dim int2 As Integer
int2 += RadGridView1.Rows(int1 - 1).Cells("PC").Value
`this is my problem here i want this dim value after the if statement
Dim percenttotal = int2 + RadGridView1.Rows(Row - 1).Cells("PC").Value
'my workaround is to keep the value in a textbox
TextBox3.Text = int2 + RadGridView1.Rows(Row - 1).Cells("PC").Value
End If
Next
If RadGridView1.Rows(Row).Cells(1).IsCurrent AndAlso Me.RadGridView1.IsInEditMode AndAlso RadGridView1.ActiveEditor.Value <> 0 Then
' i want this code to be the above dim value not to come from a textbox
If TextBox3.Text <> 100 Then
MsgBox("Your % above does not equal 100", MsgBoxStyle.Critical, "Value Error")
e.Cancel = True
End If
End If
You are having a problem with Scope. I like to think of Scope as a set of boxes, like those nesting Russian dolls. You can keep smaller boxes inside other boxes. A new Scope gets created and placed INSIDE the previous scope every time code enters a Sub , function , loop , and a few other cases. When you declare a variable it is placed in the current scope. Your code will be able to access the variables from any scope that it is currently inside, but it can't see any variables that are declared inside the smaller scopes inside it. Your code "can see out, but not in".
The problem that you are having is that you are declaring your variable inside the "If" scope, and when it hits the "End If" that scope goes away, taking your variable with it. The solution is to declare your variable at a higher scope so that it sticks around for the entire time you need it. Try declaring your variable BEFORE the "If" and setting it's value inside.
BTW, this is also why the form controls work for storing your data. They exist at a very high scope, the lifetime of the form instance.
You can read more about how scope works in VB.NET here https://msdn.microsoft.com/en-us/library/1t0wsc67.aspx
You are likely running into variable scoping issues.
Consider the following code:
Public Class Form1
Dim counter As Integer
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim i As Integer
i += 1
counter += 1
MsgBox("i: was clicked " & i & " times!" & vbCrLf & "counter: was clicked " & counter & " times!")
End Sub
End Class
i is defined within the function, and therefore will have its value reset each time the function is called.
counter is defined as the class or module level, so it retains its value in between function calls and its value is persistent until the form closes.
See: https://msdn.microsoft.com/en-us/library/ms973875.aspx
Edit: The other folks are more correct in that a variable defined with a "block" like If, For, While, etc. will have the scope of that block.
See: https://msdn.microsoft.com/en-us/library/1t0wsc67.aspx

Set parent Control to another control

I need to set a parent Control to another control using the VBA code.
Actually i am looping to create differents controls dynamically and i want now to link them by child-parent.
Do someone has an idea ?
Here is the function where i create a new control and i set some values. And the last assignment is where i want to set the parent
Public Function apply_change(ihm_f, oNode, iMyName$, project$)
Dim new_elem
Dim oSubNodes As IXMLDOMNode
If oNode.Attributes.getNamedItem("Type").Text <> "Page" Then
If (oNode.Attributes.getNamedItem("Type").Text = "RefEdit") Then
Set new_elem = ihm_f.Designer.Controls.Add("RefEdit.Ctrl", oNode.nodeName, True)
Else
Set new_elem = ihm_f.Designer.Controls.Add("Forms." & oNode.Attributes.getNamedItem("Type").Text & ".1", oNode.nodeName, True)
End If
With new_elem
On Error Resume Next
.Width = oNode.Attributes.getNamedItem("Width").Text
.Top = oNode.Attributes.getNamedItem("Top").Text
.Left = oNode.Attributes.getNamedItem("Left").Text
.Height = oNode.Attributes.getNamedItem("Height").Text
Set .Parent = get_parent(oNode.ParentNode.nodeName, oNode, ihm_f)
End With
If oNode.Attributes.getNamedItem("Type").Text = "MultiPage" Then
Call new_elem.Pages.Remove(0)
Call new_elem.Pages.Remove(0)
For Each oSubNodes In oNode.ChildNodes()
Call new_elem.Pages.Add(oSubNodes.BaseName, oSubNodes.Attributes.getNamedItem("Caption").Text, oSubNodes.Attributes.getNamedItem("Index").Text)
Next oSubNodes
End If
End If
Set apply_change = ihm_f
End Function
The getparent function return a Controle which can be anything .. like textbox or combo box etc..
You provide so little information in your question that it is difficult to guess your objective.
However, I am guessing that you want to record that Control Xxxxx is the parent of control Yyyyy where the definition of “parent” has nothing to do with Excel’s definition of parent. I am further guessing you do not know how to access controls by number.
The macro below lists the name, type and top position of every control on a form by its index number within the collection Controls. Any property of a control is accessible in this way. If control Xxxxx is the parent of control Yyyyy, you can scan the collection to find their index numbers when the form loads and record this information for use when required.
Private Sub UserForm_Initialize()
Dim InxCtrl As Long
Dim LenNameMax As Long
Dim LenTypeMax As Long
LenNameMax = 0
For InxCtrl = 0 To Controls.Count - 1
If LenNameMax < Len(Controls(InxCtrl).Name) Then
LenNameMax = Len(Controls(InxCtrl).Name)
End If
If LenTypeMax < Len(TypeName(Controls(InxCtrl))) Then
LenTypeMax = Len(TypeName(Controls(InxCtrl)))
End If
Next
Debug.Print PadR("Name", LenNameMax) & "|" & PadR("Type", LenTypeMax) & "| Top"
For InxCtrl = 0 To Controls.Count - 1
Debug.Print PadR(Controls(InxCtrl).Name, LenNameMax) & "|" & _
PadR(TypeName(Controls(InxCtrl)), LenTypeMax) & "|" & _
PadL(Format(Controls(InxCtrl).Top, "#,###.00"), 8)
Next
End Sub