So if I have lets say 10 textboxes I need to fill I have to repeat a loop 10 times and each time add to a different text box. Right now I have something like this:
If i = 0 Then
Shift0 = endTime - startTime
textStart0.text = startTime
textEnd0.text = endTime
chkBox0.checked = True
End If
I have I repeating like that 8 more times to make 9. I want to make it so that the loop would increase the number from 0-9 every time it goes through
If i = (x) Then
Shift(x) = endTime - startTime
textStart(x).text = startTime
textEnd(x).text = endTime
chkBox(x).checked = True
End If
x = x + 1
How can I put it in the loop so that the number in the name of the object increased with every loop?
Control arrays are a thing of the past, from the VB6 days, unfortunately, as you've discovered, they can still have their uses!
Try this for your loop;
For i = 0 to 9
Shift0 = endTime - startTime ' Is Shift0 a control!?
FindControl("textStart" & i).Text = startTime
FindControl("textEnd" & i).Text = endTime
FindControl("chkBox" & i).Checked = True
Next
With this function to help...
Private Function FindControl(pName As String) As Control
Dim vMatches = Me.Controls.Find(pName, True)
If vMatches IsNot Nothing AndAlso vMatches.Length > 0 Then Return vMatches(0)
Throw New Exception("Could not find the specified control!")
End Function
Having said all that, I would strongly recommend re-thinking how your form and application work to avoid this!
Something like this would work
For x = 0 to 9
Shift(x) = endTime - startTime
textStart(x).text = startTime
textEnd(x).text = endTime
chkBox(x).checked = True
next x
You can use the Controls property of a Control with an index. If your form contains exactly and only 10 textboxes, this will work fine:
For i as Integer = 1 to 10
Form1.Controls(i).Text = "Box " + i.ToString()
Next
If you have other controls in the form you have no guarantee over the index (you can't reply on 1 to 10 being the textboxes as your design progresses). Therefore, I'd recommend you put them inside a panel and refer to this panel's Controls:
For i as Integer = 1 to 10
Panel1.Controls(i).Text = "Box " + i.ToString()
Next
To learn more about loops in VB.NET, start here: http://www.tutorialspoint.com/vb.net/vb.net_loops.htm
Related
I was doing an exam algorithm earlier (Just started vb 5 days ago so be nice ;) ) and I realised after testing that for whatever reason my statement If 0 < CInt(Num(x)) < 10 was not working which completely baffled me as I would thinkk that of all things THIS would be the easiest but no. No matter what I did, it just would always be true even if I made Num(X) = 90 or 9000000 it wouldn't matter, so if anyone knows what obvious mistake I have made whether its because I made it a string or what let me know :)
Dim Num(2), INPUT As String
Dim Cond As Boolean = False
Dim Valid As Boolean = False
Dim Isnum As Boolean
Dim x, Number, IsTen As Integer
While Cond = False
Do Until IsTen = 3 And Number = 3
IsTen = 0
Number = 0
For x = 0 To 2
MsgBox("Please enter a number")
Num(x) = (Console.ReadLine)
Isnum = IsNumeric(Num(x))
If Isnum = True Then
IsTen = IsTen + 1
End If
If Isnum = True Then
If 0 < CInt(Num(x)) < 10 Then
Number = Number + 1
End If
End If
Next
If Number <> 3 Then
MsgBox("Sorry all three numbers werent between 0 and 10, Please enter again")
End If
If IsTen <> 3 Then
MsgBox("Sorry all three inputs werent numbers, Please enter again")
End If
Loop
MsgBox("All three numbers are valid")
End While
First thing I would advise is setting Option Strict On at the top of your code file. You can also make this the default via Visual Studio options. This will warn you about the implicit type conversion that is causing your problem.
Changing the line
If 0 < CInt(Num(x)) < 10 Then
I'm assuming the intent here is to ensure the number is greater than 1 but less than 10
To something like this:
If Isnum = True Then
Dim val As Integer = Integer.Parse(Num(x))
If (val > 0) AndAlso (val < 10) Then
Number = Number + 1
End If
End If
Notice the use of Integer.Parse() to type cast the string to Int, it's a little more robust but there is also the Integer.TryParse() which is another step up again, might be worth reading up on. Also notice AndAlso usage, this is a short circuit operator so if the First condition is false, doesn't bother evaluating the next.
I have a problem with my university project
It's a little game, 6 buttons for each players and 2 players so 12 buttons
There is number in each buttons, if a player has his 6 buttons at 0, he can't play
I have try some Public Function and i'm actually working with a very simple one but i think this is not the problem
My function is here
And in my form, the problem is here, i've tried many things but i don't know how do to that ... I read my lesson and I'm searching on the internet, i have no idea ..
If possible is True you don't re-enable the button.
You can simplify things.
Public Function PeutJouer(ByVal joueur As Integer) As Boolean
Dim sum As Integer
Dim start As Integer = (joueur - 1) * 7
For i As Integer = start To start + 5
sum += tableau(i)
Next
Return sum <> 0
End Function
Then
Btn1P1.Enabled = PeutJouer(1)
Did you show all the relevant code? You are declaring Dim tableau(12) As Integer but the array is never filled with values. Probably tableau should be declared at the form level and not locally in this function. If you already have both, remove the local declaration, because it hides the one at form level. You also need to return the result from the function. I don't see this in your function.
Note that this
If x <> 0 Then
booleanVariable = True
Else
booleanVariable = True
End If
can be simplified to
booleanVariable = x <> 0
i.e., the condition is an expression yielding the Boolean result True or False already and you can use this value directly. When working with numeric values you don't write If x + y = 1 Then r = 1 Else If x + y = 2 Then r = 2 .... You simply write r = x + y.
I'm pulling in datetime information from SQL, converting it to a string then using substring to chop it in date, hour, and minute variables. In the database, the date is shown as MM/dd/YYYY, and the month/day always use two digits (such as 09/04/2016) but after the query it shows up in my application as (9/4/2016) which throws off my substring count. How do I get my query to keep the 0 in front when necessary?
Private Sub pullDate()
pulledDate = dgvTimeManager.SelectedItem.TimeIn.ToString
pulledDate = pulledDate.Substring(0, 10)
datepickerEdit.SelectedDate = Date.Parse(pulledDate)
End Sub
Private Sub pullStartTime()
pulledStartTime = dgvTimeManager.SelectedItem.TimeIn.ToString
If pulledStartTime.Length = 22 Then
pulledStartHour = pulledStartTime.Substring(11, 2)
pulledStartMinute = pulledStartTime.SubString(14, 2)
pulledStartAMPM = pulledStartTime.SubString(20, 2)
ElseIf pulledStartTime.Length = 21 Then
pulledStartHour = pulledStartTime.SubString(11, 1)
pulledStartMinute = pulledStartTime.SubString(13, 2)
pulledStartAMPM = pulledStartTime.SubString(19, 2)
End If
If pulledStartAMPM = "AM" Then
comboStartEditAMPM.SelectedIndex = 0
ElseIf pulledStartAMPM = "PM" Then
comboStartEditAMPM.SelectedIndex = 1
End If
txtStartHrEdit.Text = pulledStartHour
txtStartMinEdit.Text = pulledStartMinute
End Sub
dgvtimemanger is the datagrid that's displaying my time table, the starttime.length conditions are to take into account the hour being displayed as one digit or two (the same thing i'm dealing with now for the date)
Instead of converting to a string use the properties on the DateTime object. For example:
pulledStartHour = pulledStartTime.Hour.ToString("00")
will return the hour (in 24 hour format) which you then convert to a string.
You could also use .ToString(format) instead.
pulledStartHour = pulledStartTime.ToString("hh")
pulledStartAMPM = pulledStartTime.ToString("tt")
Which gives you the hours (in 12 hour format) and the AM/PM.
You can also combine the string foramts:
pulledStartTime.ToString("hh:mm tt")
https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx
You're spending way too much effort working with strings. Use the DateTime value you're given, and this code gets much simpler:
Private Sub pullDate()
datepickerEdit.SelectedDate = dgvTimeManager.SelectedItem.TimeIn.Date
End Sub
Private Sub pullStartTime()
Dim pulledStartTime As DateTime = dgvTimeManager.SelectedItem.TimeIn
txtStartHrEdit.Text = pulledStartTime.ToString("hh")
txtStartMinEdit.Text = pulledStartTime.ToString("mm")
comboStartEditAMPM.SelectedIndex = If(pulledStartTime.Hour >= 12, 1, 0)
End Sub
Additionally, I'd tend to write these kind of methods to accept an argument:
Private Sub SetStartTime(DateTime timeToSet)
datepickerEdit.SelectedDate = timeToSet.Date
txtStartHrEdit.Text = timeToSet.ToString("hh")
txtStartMinEdit.Text = timeToSet.ToString("mm")
comboStartEditAMPM.SelectedIndex = If(timeToSet.Hour >= 12, 1, 0)
End Sub
And then call them like this:
SetStartTime(dgvTimeManager.SelectedItem.TimeIn)
Note how the first one is so simple it's not even worth building the method anymore, and so I instead include the code as part of setting the time.
I'm trying to write a macro using VBA that will ask a given number of simple arithmetic questions, with each question being available for answer only for a specified amount of time.
I wish to specify somewhere in the worksheet where I can set the parameters of the game (e.g. 20 questions with 12 seconds to answer each) and then click a go button that will continually throw up input boxes every 12 seconds (and remove the previous one at the end of the 12 seconds OR when the person clicks "next" or "ok") until the 20 question quota has been reached, all the while recording the answers and time taken.
I'm quite new to VBA but have made a lot of headway using mostly this site.
So far I have the following which allows me to generate the questions and record the answers:
Sub Rand_Arith()
Dim practice As Workbook
Dim answers As Worksheet
Dim start As Worksheet
Set practice = ActiveWorkbook
Set answers = Sheets("answers")
Set start = Sheets("Start")
Dim x As Variant
Dim a As Variant
Dim b As Variant
Dim answer As Variant
Dim myanswer As Variant
Dim i As Integer
Dim correct As Integer
Dim qs As Integer 'the number of questions we want
Dim spq As Integer 'time in seconds per question
Dim totaltime As Integer
Dim operator As String
qs = start.Cells(3, 3)
spq = start.Cells(2, 3)
totaltime = spq * qs
correct = 0
If answers.Range("A1").Value = "" Then
i = answers.Range("A2").End(xlDown).Row
Else
i = 1
End If
Do Until i = qs + 1
x = Rnd
op = Int(x * 4) '0+ 1- 2* 3/
If x < 0.25 Then
a = Round(Rnd * 10, 2)
b = Round(Rnd * 10, 2)
theanswer = Round(calc(a, b, op), 2)
ElseIf x < 0.5 Then
a = Round(Rnd * 10, 2)
b = Round(Rnd * 10, 2)
theanswer = Round(calc(a, b, op), 2)
ElseIf x < 0.75 Then
a = Round(Rnd * 10, 0)
b = Round(Rnd * 10, 0)
theanswer = Round(calc(a, b, op), 1)
Else:
a = Round(Rnd * 10, 0)
b = Round(Rnd * 10, 0)
theanswer = Round(calc(a, b, op), 1)
End If
theanswer = Round(calc(a, b, op), 2)
operator = s_op(op)
myanswer = Application.InputBox("What is " & a & operator & b & " = ?")
If theanswer = myanswer Then
correct = correct + 1
End If
runningscore = correct / i
answers.Cells(i + 1, 1) = i
answers.Cells(i + 1, 2) = a
answers.Cells(i + 1, 3) = operator
answers.Cells(i + 1, 4) = b
answers.Cells(i + 1, 5) = theanswer
answers.Cells(i + 1, 6) = myanswer
answers.Cells(i + 1, 7) = runningscore
i = i + 1
Loop
End Sub
These are the function's i've made calls to:
Function calc(n1, n2, op)
If op = 0 Then calc = n1 + n2
If op = 1 Then calc = n1 - n2
If op = 2 Then calc = n1 * n2
If op = 3 Then calc = n1 / n2
End Function
Function s_op(op)
If op = 0 Then s_op = "+"
If op = 1 Then s_op = "-"
If op = 2 Then s_op = "*"
If op = 3 Then s_op = "/"
End Function
One problem I am having is that the answer that i put into the input box returns a string which, even when the values are the same, doesn't seem to register as the same as the actual answer calculated by theanswer. As a result the correct and runnningscore variables remain as 0 throughout.
I can't work out how to do the timing part. I've experimented with the following countdown timer which I've taken more or less word for word from this video: https://www.youtube.com/watch?v=sbJeGG_Xv8M
First piece of code:
Sub starttimer()
Application.OnTime Now + TimeValue("00:00:01"), "nexttick"
End Sub
Second Piece
Sub nexttick()
Dim practice As Workbook
Dim start As Worksheet
Set practice = ActiveWorkbook
Set start = Sheets("Start")
If start.Range("c4").Value = 0 Then Exit Sub
start.Range("c4").Value = start.Range("c4").Value - TimeValue("00:00:01")
If start.Range("c4").Value <= TimeValue("00:00:05") Then
start.Shapes("TextBox 3").Fill.ForeColor.RGB = RGB(255, 0, 0)
ElseIf start.Range("c4").Value <= TimeValue("00:00:10") Then
start.Shapes("TextBox 3").Fill.ForeColor.RGB = RGB(0, 0, 255)
ElseIf start.Range("c4").Value <= TimeValue("00:00:15") Then
start.Shapes("TextBox 3").Fill.ForeColor.RGB = RGB(0, 255, 0)
End If
starttimer
End Sub
Lastly:
Sub stoptimer()
On Error Resume Next
Application.OnTime Now + TimeValue("00:00:01"), "nexttick", , False
End Sub
I've not had much success here. I feel as though the countdown macros do not allow for another macro to be run alongside it?
I've also been trying to work with the Application.Wait and Application.OnTime but can't really workout how to combine the question generator I've already made with something that does even something remotely like what what I want to do.
I'm worried that I've dived into this and perhaps its not possible to do what I want with VBA (or that it is actually very difficult).
In any case, and help/constructive criticism would be great!
Furthermore, any more general opinion/advice on my approach to the problem/technique would be greatly appreciated! As mentioned, I'm only just starting out and have no experience in other languages either.
Denis,
I'm going to give you some general advise about how to complete your project, then I'll try to answer some of the specific problems you're having. Before I do that, rest assured that VBA is more than capable accomplishing your task, and, based on the code you've presented you're not in over your head.
General Advice
What you really need here Denis is a UserForm. In your IDE (the place where your code is) go to Insert > UserForm. If you cannot already see it, go to View > Toolbox. There you will see a number of controls (like buttons and text boxes) that you can drag on to your form. You can edit the code behind each control by double clicking it. So, for instance, double clicking the button control will allow you to write code that executes when the user clicks the button.
I'll leave you to figure out the rest of the controls, but using a form will give you the type of control over user interactions that I think you're after. Is it possible to do it without a Userform? Sure, but I wouldn't recommend it.
General Layout
Years ago I wrote a project very similar to yours. I set it up with a Textbox (or label) for the question, a Textbox for the user's answer, a Submit button, and a Cancel button to quit. Clicking the submit button performed the calculations and also triggered the next question.
Timing
VBA has a built in DateDiff function:
DateDiff("s", "06/27/2016 16:42:51", Now())
The first parameter, "s", denotes the unit of time. In this case seconds. In my project the only time I was interested in was the time it took the user to answer the question. For that I stored Now() in a variable at the time the question was presented and compared that to the time when the user clicked Submit using the DateDiff function. You could use it just as easily in your case to limit the duration of the quiz.
Integer Comparisons
VBA is what we call a loosely typed language, meaning that more often than not the interpreter will figure out for you whether your variables are strings or integers, or something else. That being said, some things to keep in mind.
1) It's good practice and safer to declare your variables: Dim theanswer as Integer or Dim theanswer as Double , which ever the case. Often, that will help eliminate the possibility that VBA is treating theanswer as a string and myanswer as an integer and trying to compare the two.
2) If you're pulling values from a cell in your worksheet to compare, make sure and pull Cells(x,y).value.
3) Make sure to use Debug.Print from time to time to check that the values being compared are actually what you expected. For example, Debug.print theanswer. The output from that statement can be seen in the "Immediate" window which can be enabled from the View menu.
Hopeful this will help give you some guidance with the rest of your project. I'll keep an eye on this post, so feel free to post follow up questions as they come to you. Good luck!
I am developing an application using Visual Basic 2010 for hydraulic calculations of a pipe network.
This application uses a lot of iterations and loops, depending on the user input and size of network. Most of the results have to be saved temporarily to be used for the next step of calculations.
Firstly, I used a DataGridView to save the results but as the number of iterations increased, the application became very slow.
Now I am trying to create a DataTable, then populate it with some initial results (this part was successful). The obtained DataTable has some columns that are not populated like so:
22 24 10
3 16 22 9 15
16 12 24 13
14 21 10 23 12 1
24 18 23 2 1
Other calculations are performed and a certain value (X) is obtained.
Now I am trying to loop through the columns of a specific row to check if the calculated value (X) equals to one of the values in those columns.
My question is: How can I loop through only the columns that have values (avoiding the columns containing NULL values) for a specific row?
I am a beginner in VB.net. I hope my question is clear as I didn't provide any code.
Thanks in advance for you help.
This is the initial code I used:
Results.DGVInitial.Rows.Clear()
Results.DGVFinal.Rows.Clear()
For m As Integer = 0 To NetworkLayout.DGVNetworkLayout.Rows.Count - 1
Results.DGVInitial.Rows.Add()
Next
Dim I As Integer = NetworkLayout.DGVNetworkLayout.Rows.Count - 1
Dim Sec(I), Ini(I) As Integer
Dim Hyd(I), Dia(I), Len(I) As Single
Dim Qsec(I), Qini(I), Vsec(I) As Single
Dim U(I), Y(I) As Single
Do
I = I - 1
Sec(I) = NetworkLayout.DGVNetworkLayout.Rows(I).Cells(0).Value
Ini(I) = NetworkLayout.DGVNetworkLayout.Rows(I).Cells(1).Value
Hyd(I) = NetworkLayout.DGVNetworkLayout.Rows(I).Cells(6).Value
Dia(I) = NetworkLayout.DGVNetworkLayout.Rows(I).Cells(4).Value
Len(I) = NetworkLayout.DGVNetworkLayout.Rows(I).Cells(3).Value
Dim V As Integer
V = Results.DGVRandomGen.Rows(TotalNum_Runs - 1).Cells(I).Value
Qsec(I) = 0
Dim q As Single = 0
For n As Integer = 0 To Results.DGVInitial.Rows.Count - 1
If Results.DGVInitial.Rows(n).Cells(1).Value = Sec(I) Then
q = Results.DGVInitial.Rows(n).Cells(0).Value
Qsec(I) = Qsec(I) + q
Else
Qsec(I) = Qsec(I)
End If
Next
If V = 1 Then ' if the hydrant is open
Qini(I) = Hyd(I) + Qsec(I)
Else ' if the hydrant is close
Qini(I) = Qsec(I)
End If
Results.DGVInitial.Rows(I).Cells(0).Value = Qini(I)
Results.DGVInitial.Rows(I).Cells(1).Value = Ini(I)
Results.DGVSectionDischarges.Rows(TotalNum_Runs - 1).Cells(I).Value = ini(I).ToString("F2")
Now instead of using
V = Results.DGVRandomGen.Rows(TotalNum_Runs - 1).Cells(I).Value
I would like to replace the "DGVRandomGen" with a DataTable called "DT_Random"
Like I said I am a beginner so I am not sure how to code it but it will be something like this:
For DT_Random.Rows (TotalNum_Runs - 1)
For Each col As DataColumn In DT_Random.Columns
If DT_Random.Rows(TotalNum_Runs - 1).Item(col) = I Then
Qini(I) = Hyd(I) + Qsec(I)
Else
Qini(I) = Qsec(I)
End If
Next
But I want to avoid Null values as not all columns are populated
Thanks
Maybe this will help you:
Dim myXvalue = 24
Dim myDataTable As New DataTable
myDataTable.Columns.Add("Col1")
myDataTable.Columns.Add("Col2")
myDataTable.Columns.Add("Col3")
myDataTable.Columns.Add("Col4")
myDataTable.Rows.Add(22, 24, 10, DBNull.Value)
myDataTable.Rows.Add(3, 16, 22, DBNull.Value)
myDataTable.Rows.Add(24, 18, DBNull.Value, 24)
For Each column As DataColumn In myDataTable.Columns
If IsDBNull(myDataTable.Rows(0).Item(column)) Then
MsgBox("DB Null Found At: " & column.ColumnName)
Continue For
End If
If myDataTable.Rows(0).Item(column) = myXvalue Then
MsgBox("Match: " & myDataTable.Rows(0).Item(column) & " found at " & column.ColumnName)
End If
Next column
Just a quick example, you may need to restructure it a bit, but at least it shows you how to access the values in your datatable by columns. I would do a function that passes a row index as a parameter and returns a boolean. Create two booleans inside the sub, one for dbnull existing in the row, and one for finding a matching value. If dbnull bool is false, and match value is true, then return true. Just make sure you loop all the columns and dont exit early.
If you need me to elaborate let me know.