I have a program that creates 100 000 objects of class Client, puts them into array and then goes through that array 100 times, each time assigning each Client a different random number through Rnd() function:
Main sub:
Sub start()
Dim i As Long
Dim j As Long
Dim clientsColl() As Client
ReDim clientsColl(1 To 100000) As Client
For j = 1 To 100000
Set clientsColl(j) = New Client
clientsColl(j).setClientName = "Client_" & j
Application.StatusBar = "Getting client " & j
DoEvents
Next
Dim tempCount As Long
Dim clientCopy As Variant
For i = 1 To 100
tempCount = 0
For Each clientCopy In clientsColl
tempCount = tempCount + 1
clientCopy.generateRandom
'Application.StatusBar = "Calculating " & i & ": " & tempCount & "/" & 100000 '(1)
'DoEvents
Next
Application.StatusBar = "Calculating " & i
DoEvents
Next
MsgBox ("done")
End Sub
Client class:
Option Explicit
Dim clientName As String
Dim randomNumber As Double
Public Sub generateRandom()
randomNumber = Rnd()
End Sub
Public Property Get getClientName()
getClientName = clientName
End Property
Public Property Let setClientName(value As String)
clientName = value
End Property
The problem is, the execution time depends on whether or not line (1) is commented out. If it's executed, the statusbar gets renewed, but the execution time is very slow. If it's not executed, the program gets done really fast.
Why does this happen?
VBA is fast enough as long as you stay within. Whenever you turn to Excel, it may get much slower because Excel makes thousands of operations every time it gets control. You may consider turning off a few more services of Excel like I do in my applications:
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
... and as far as I know DoEvents is the best way to make Excel update the status bar when you turn off automatic updates.
Another timesaving workaround can be to display only every 100th or 1000th message from within the inner loop.
when doing a progressbar or statusbar, you need to use it wisely.
Basically the progress info needs to be refreshed only every 0.1 seconds or so.
Knowing your max number of loops , and the time it takes, you might want to update the info only every (in your case) , let's say, 100 iterations of the loop.
This is done like this: if j mod 100=0 then application.statusbar="..." : doevents
Usually i even go further by using doevents less than my progressbar (second if j mod).
Related
I have a macro that loops through an unknown number of times. The number of times varies based on a total number of rows in multiple tables in a reference document, and that number of rows will vary across reference documents that may be used. The relevant snippet of code for the loop is below:
For Each oRow In oTbl.Rows
p = p + 1
Helper.ProgressIndicator_Code (p)
strPhrase = Split(Trim(oRow.Range.Cells(1).Range.Text), vbCr)(0)
strRule = Split(Trim(oRow.Cells(2).Range.Text), vbCr)(0)
If strPhrase <> "" Then
If Not strStartWord = vbNullString Then
'Process defined sections
arrEndWords = Split(strEndWord, "|")
For lngIndex = 0 To UBound(arrEndWords)
Set oRng = GetDocRange(strStartWord, arrEndWords(lngIndex))
If Not oRng Is Nothing Then Exit For
Next lngIndex
Else
'Process whole document
Set oRng = m_oDocCurrent.Range
End If
If Not oRng Is Nothing Then
Set oRngScope = oRng.Duplicate
With oRng.Find
.Text = strPhrase
Do While .Execute
If Not oRng.InRange(oRngScope) Then Exit For
oRng.HighlightColorIndex = wdTurquoise
If strRule <> "" Then
Set oComment = m_oDocCurrent.Comments.Add(Range:=oRng, Text:=strUsr & ": " & strRule)
oComment.Author = UCase("WordCheck")
oComment.Initial = UCase("WC")
End If
Loop
End With
End If
End If
Next oRow
The progress bar is a classic progress bar for which a label field width is updated using the below code based on a value of p as updated in the above code:
Sub progress(pctCompl As Integer)
ProgressIndicator.Text.Caption = pctCompl & "% Completed"
ProgressIndicator.Bar.Width = pctCompl * 2
DoEvents
End Sub
Here's my problem: The value of p varies based on which reference document is used, so my progress bar is never even approximately accurate with respect to the processing of the VBA macro. The progress bar doesn't have to be exact, merely close and to indicate that progress is being made and nothing has hung.
I'm not looking for written code, just would be very grateful for suggestions or advice as to approaches for making my progress bar more accurate so that I can learn (e.g., I just ran the macro for three different reference documents - one gave me 25%, one gave 44%, and one gave 82%; none showed even close to 100% when completed). Essentially I need to divide i by an unknown number to get my percentage, which is clearly impossible, so some function for a close approximation is needed.
Edit: New code based on #macropod suggestion.
Dim strCheckDoc As String, docRef As Document, projectPath As String, _
j As Integer, i As Integer, k As Integer, oNumRows as Long
j = 1
For i = 0 To UBound(strUsr)
strCheckDoc = [path to reference document unique to each strUsr]
Set docRef = Documents.Open(strCheckDoc, ReadOnly:=True, Visible:=False)
For k = 1 To docRef.Tables.Count
oNumRows = oNumRows + docRef.Tables(i).Rows.Count
Next k
Next i
Then the code to update the progress bar is:
Dim pctCompl As Single
pctCompl = Round((p / oNumRows) * 100)
ProgressIndicator.Text.Caption = pctCompl & "% Completed"
ProgressIndicator.Bar.Width = pctCompl * 2
DoEvents
The progress bar now gets to 64% when complete (i.e., it should be at 100%). I'm also working on a way to make oNumRows only count a row if the row has content in the first column.
I'm having the code takes the input from my checkboxes and grab data from the related worksheet. I ran it line by line and found out that it always gets a runtime error at the If statement on the final loop. Is there something wrong in my code?
Option Explicit
Private Sub UserForm_Initialize()
Dim counter As Long
Dim chkBox As MSForms.CheckBox
''''Add checkboxes based on total sheet count
For counter = 1 To Sheets.count - 2
Set chkBox = Me.Frame1.Controls.Add("Forms.CheckBox.1", "CheckBox" & counter)
chkBox.Caption = Sheets(counter + 2).Name
chkBox.Left = 10
chkBox.Top = 5 + ((counter - 1) * 20)
Next
End Sub
Private Sub cmdContinue_Click()
Dim Series As Object
Dim counter As Long
'''Clear old series
For Each Series In Sheets(2).SeriesCollection
Sheets(2).SeriesCollection(1).Delete
Next
''Cycle through checkboxes
For counter = 1 To Sheets.count - 2
''If the box is checked then
If Me.Frame1.Controls(counter).Value = True Then ''Error here on 4th iteration
''Add new series
With Sheets(2).SeriesCollection.NewSeries
.Name = Sheets(counter + 2).Range("$A$1")
.XValues = Sheets(counter + 2).Range("$A$12:$A$25")
.Values = Sheets(counter + 2).Range("$B$12:$B$25")
End With
End If
Next counter
Me.Hide
End Sub
Also, a second problem is it always run on the wrong loop. If i check box 2 it'll run data for the box 1 sheet, 3 run for 2, 4 run for 3, and 1 run for 4. Can anyone explain the reason behind this?
EDIT: So as VincentG point out below, adding an explicit name "checkbox" in there did the trick (i didn't know you could do that). Index 1 was probably taken by one of the buttons or the frame in the user form, causing it to get off set.
I guess your main problem comes from the fact that the controls have to be accessed starting from index 0. So to loop over all controls, you would do something like
For counter = 0 To Me.Frame1.Controls.Count - 1
Debug.Print counter; Me.Frame1.Controls(counter).Name
Next counter
So, when you stick to you code, I assume you have to change the if-statement to
If Me.Frame1.Controls(counter-1).Value = True Then
Context
I have a big macro where I have declared lots of global constant in a dedicated module (i.e, a module which contains only Public Const declarations).
About 100 of those global constants are used to assign columns names to each column of my main Data worksheet :
Public Const columnName = "A"
Public Const columnCity = "B"
Public Const columnPhone = "C"
...
Public Const columnColor = "CX"
This let me reference to the columns (of my Data worksheet, from the 10 other modules) using .Range(columnColor & l) instead of using .Range("CX" & l) (where l is obviously the row number) . This is much easier to code (I don't need to search for the right column) or to update if I decide to insert a column before "F" (I only have to update my const module and not the 10 other code modules).
However, it looks like using .Range(columnCity & l) is notably slower than using .Range("A" & l). (SEE EDIT BELOW)
The most processors intensive tasks are done using big 2D arrays. But I'm still probably calling those global column variables 100 000 times in some subs, since I'm not only checking/updating values/formulas (which I could do on a 2D array) but also dealing with cell's .Interior.Color, .Comment.Text ...
Question
How bad an idea is it to use such global variables (Public Const columnName...) to reference columns ?
Is there some standard way of doing so ?
Edit
As pointed by Tim, I think I indeed spent time changing every .Cells(l, 1) to .Range(columnName & 1) when I refactored my code to use the column variable. That means that :
My problem probably comes from using .Range vs .Cells rather than from the global variables.
I should probably refactor back to .Cells(l, colIndexName).
There is no "standard" way to do this, so you should run some performance tests and figure out the most efficient method if performance is an issue for you.
For example, using a numeric constant and Cells() seems to be about twice as fast as using Range():
Option Explicit
Public Const columnName As String = "A"
Public Const colIndexName As Long = 1
Sub Tester()
Dim l As Long, v, t
t = Timer
With Sheet1
For l = 1 To 300000#
v = .Range(columnName & 1).Value
Next l
End With
Debug.Print Timer - t '>> approx. 1.3 sec
t = Timer
With Sheet1
For l = 1 To 300000#
v = .Cells(1, colIndexName).Value
Next l
End With
Debug.Print Timer - t '>> approx. 0.6 sec
End Sub
However, it's likely only twice as fast if that's all you're doing - as soon as you add in other tasks that difference may wash out.
Need an Answer to provide formatting and results, though this is more of a comment.
I have found no significant difference between .Range(columnCity & l) and .Range("A" & l). Can you provide more insight on how you came to this conclusion?
Here is my code for speed comparison:
Public Const p_sCol As String = "A"
Sub tgr()
Dim ws As Worksheet
Dim i As Long, l As Long
Dim sTemp As String
Dim dTimer As Double
Dim aResults(1 To 1, 1 To 2) As Double
Set ws = ActiveWorkbook.ActiveSheet
l = 1
dTimer = Timer
For i = 1 To 100000
sTemp = vbNullString
sTemp = ws.Range(p_sCol & l).Value
Next i
aResults(1, 1) = Timer - dTimer
dTimer = Timer
For i = 1 To 100000
sTemp = vbNullString
sTemp = ws.Range("A" & l).Value
Next i
aResults(1, 2) = Timer - dTimer
ws.Range("C1:D1").Value = aResults
End Sub
I ran the test 10 times, and the average result for the public variable concatenation over 100,000 iterations was 0.4375 seconds while the average result for hard coding the column letter was 0.429688 seconds, which is a difference of 0.007813 seconds. Sure, the hard coded method was slightly faster, but not noticeably and certainly not significantly.
I am using excel macro to validate data fields in a table. The data contains some fields that can contain one of the values listed in the dictionary.
When I tried to run the validation macro for 700,000 records, it literally gets stuck and takes a long time to complete. Can anyone help with improving the performance of this code?
The following is a sample code I am using for one of the fields to check the content of cells in a column against a list defined in the dictionary. This never completes when run over 700,000 column records, whereas takes around 30 seconds for 50,000 column records.
Sub Validate_Action_Type()
'Speed Up
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
'Speed Up end
'Define the variables
Dim DicActionType As New Scripting.Dictionary
Dim CountActionTypeErrors As Long
Dim StartTime As Double
Dim SecondsElapsed As Double
'Start the timer, used to calculate elapsed time
StartTime = Timer
'Create a dictionary of allowed marker type values
DicActionType.Add "Insert", 1
DicActionType.Add "Update", 2
DicActionType.Add "Delete", 3
'Check the Marker Type Column using the dictionery created
For Each d2 In Range(Range("C2"), Range("C2").End(xlDown))
If Not DicActionType.Exists(d2.Text) Then
d2.Interior.ColorIndex = 3
CountActionTypeErrors = CountActionTypeErrors + 1
Else
d2.Interior.ColorIndex = xlNone
End If
Next
'Calculate elapsed time
SecondsElapsed = Round(Timer - StartTime, 2)
'Pop-up the outcome message
MsgBox "Time taken in Seconds = " & SecondsElapsed _
& vbCrLf _
& "Total Errors = " & CountActionTypeErrors _
, , "Check Cells Highlighted RED"
'Restore state: undo the speed up settings
Application.ScreenUpdating = ScreenUpdateState
Application.DisplayStatusBar = statusBarState
Application.Calculation = calcState
Application.EnableEvents = eventsState
ActiveSheet.DisplayPageBreaks = displayPageBreaksState 'note this is a sheet-level setting
End Sub
Referencing cells is always very slow. As you want to color your cells, you need a reference, but only for coloring, not for checking. For checking you can use a much faster array.
In the following code I used an array to check the actions. On my machine its about 5 times faster having ~17% errors in my sample cells.
Sub Validate_Action_Type()
'Speed Up
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
'Speed Up end
'Define the variables
Dim DicActionType As New Scripting.Dictionary
Dim CountActionTypeErrors As Long
Dim StartTime As Double
Dim SecondsElapsed As Double
Dim ActionArr, I As Integer
ActionArr = Range(Range("D2"), Range("C2").End(xlDown)).Value
'Start the timer, used to calculate elapsed time
StartTime = Timer
'Create a dictionary of allowed marker type values
DicActionType.Add "Insert", 1
DicActionType.Add "Update", 2
DicActionType.Add "Delete", 3
'Check the Marker Type Column using the dictionery created
Columns("C").Interior.ColorIndex = xlNone
For I = 1 To UBound(ActionArr)
If Not DicActionType.Exists(ActionArr(I, 1)) Then
'ActionArr(I, 2) = 3
Cells(I + 1, 3).Interior.ColorIndex = 3
CountActionTypeErrors = CountActionTypeErrors + 1
Else
ActionArr(I, 2) = 0
End If
Next I
'Calculate elapsed time
SecondsElapsed = Round(Timer - StartTime, 2)
'Pop-up the outcome message
MsgBox "Time taken in Seconds = " & SecondsElapsed _
& vbCrLf _
& "Total Errors = " & CountActionTypeErrors _
, , "Check Cells Highlighted RED"
'Restore state: undo the speed up settings
Application.ScreenUpdating = ScreenUpdateState
Application.DisplayStatusBar = statusBarState
Application.Calculation = calcState
Application.EnableEvents = eventsState
ActiveSheet.DisplayPageBreaks = displayPageBreaksState 'note this is a sheet-level setting
End Sub
As promised I have investigated what the time is going on my laptop. Without knowing which statements are the slowest, you can easily spend your time optimising sections of your code that have little effect on the duration. There is a lot of bad advice available based on people assumptions about what is slow. Application.ScreenUpdating = False will save very significant amounts of time. Other changes are often of little significance. It appears there is some significant overhead with calling WorksheetFunction from VBA because I have yet to find one that is faster than VBA. Unless someone says “I have run timings”, don’t believe them.
I will explain my implementation of your code and then tell you what I discovered. You will need to concatenate the blocks of code below if you wish to perform similar tests on your computer.
Option Explicit
Const ColCrnt As Long = 3
Const NumNames As Long = 30
Const RowDataFirst As Long = 2
Const RowMax As Long = 700000
Const ErrorMax As Long = 70000
I have used constants to specify values I might wish to vary between test runs.
I never changed ColCrnt from 3 (= C) or RowDataFirst from 2 since I do not believe their values are relevant.
I tried much lower values for RowMax and ErrorMax at first but most of my timings were with the values shown. My tests were with 10% errors. I am sure your data is much better so my timings for worksheet updates should be much worse than yours.
I have called the values you place in a dictionary, “names” or “valid names” so NumNamesis the constant I have changed the most.
Sub CtrlCheckAll()
Call CtrlCheck1
Call CtrlCheck1
Call CtrlCheck1
End Sub
I created three variations of your code. This routine allowed me to call all three in one go. It became clear that variations 2 and 3 were not significantly faster or slower than variation 1. In the end, I just used this routine to call variation 1 three times.
When you look at the timings, you will see how much variation there is from run to run. Most of this variation is probably background processes (task manager, virus checkers and the like). However, Excel also has background tasks (such as garbage collection). I prefer much longer test runs because they give more stable timings. Apart from increasing the number of rows, I am not sure how to slow your routine down; I have settled for running it several times and averaging their separate durations.
Sub CtrlCheck1()
Dim CountActionTypeErrors As Long
Dim d2 As Variant
Dim ExistsCrnt As Boolean
‘Dim InxVn As Long
Dim ValidNames As New Dictionary
Dim TimeCheckStart As Single
Dim TimeExistsTotal As Single
Dim TimeStart As Single
Dim TimeWshtTotal As Single
Dim Wsht As Worksheet
Set Wsht = Worksheets("Data")
TimeStart = Timer
Call GenDic(ValidNames, NumNames)
Debug.Print "Create time " & Format(Timer - TimeStart, "00.00000")
'TimeStart = Timer
'For InxVn = 0 To ValidNames.Count - 1
' Debug.Print ValidNames.Keys(InxVn)
'Next
'Debug.Print "Access time " & Format(Timer - TimeStart, "00.00000")
TimeStart = Timer
Call GenWsht(Wsht:=Wsht, RowDataFirst:=RowDataFirst, ColCrnt:=ColCrnt, _
RowMax:=RowMax + RowDataFirst - 1, _
ErrorMax:=ErrorMax, Dic:=ValidNames)
Debug.Print "Build worksheet time " & Format(Timer - TimeStart, "00.000")
Application.ScreenUpdating = False
TimeExistsTotal = 0!
TimeWshtTotal = 0!
TimeCheckStart = Timer
You will recognise some of the variables while others have been introduced by me.
Your code accesses the active worksheet. This relies on the user having the correct worksheet active when the macro is started. You would not do this if you had ever had to fix the mess caused by a user running a macro against the wrong worksheet and not having a backup of the undamaged worksheet. Being explicit about the worksheet to be accessed makes your code clearer, reduces the opportunities for disaster and has no noticeable time penalty.
I have not given you the code for GenDic since you have your own real data. However, I will add the code if you want it. Notice, I have placed Timer around this call. I wanted to know if creating a dictionary was slow process. I discovered the duration was less than Timer can accurately record. Normally the duration was given as zero thousandths of a second although occasionally it was four thousandths of a second
I have commented out the code to list the keys in the dictionary because it was creating too many lines. Again my motive was to see if accessing keys was slow but again the duration was less than Timer can accurately record.
I have not given you the code for GenWsht since you have your own real data. Again, I will add the code if you want it.
The last three statements of the above block are the important ones. TimeCheckStart is used to calculate the total duration of the main block of code. TimeExistsTotal and TimeWshtTotalare used to accumulate the duration of the two statements of thought might be the most expensive in terms of time. I will explain them later.
With Wsht
For Each d2 In Range(.Cells(RowDataFirst, ColCrnt), _
.Cells(RowDataFirst, ColCrnt).End(xlDown))
TimeStart = Timer
ExistsCrnt = ValidNames.Exists(d2.Text)
TimeExistsTotal = TimeExistsTotal + Timer - TimeStart
If Not ExistsCrnt Then
TimeStart = Timer
d2.Interior.ColorIndex = 3
TimeWshtTotal = TimeWshtTotal + Timer - TimeStart
CountActionTypeErrors = CountActionTypeErrors + 1
End If
Next
End With
This is a slightly modified version of the critical part of your code. As explained earlier, I access cells in a named worksheet. I do not believe this could have a noticeable
I have split the test for a name existing out of the If statement so I can place Timer statements around the test. This probably has a minor effect on the duration but you cannot add timer statements without having an effect. Note that what these Timer statements are doing is accumulating the total duration of these tests. I have also places Timer statements around d2.Interior.ColorIndex = 3
Debug.Print "##### Check 1 #####"
Debug.Print " Number rows " & RowMax
Debug.Print " Number errors " & ErrorMax
Debug.Print " Valid names " & ValidNames.Count
Debug.Print " Total check time " & Format(Timer - TimeCheckStart, "00.000")
Debug.Print "Total exists time " & Format(TimeExistsTotal, "00.000")
Debug.Print " Total wsht time " & Format(TimeWshtTotal, "00.000")
End Sub
This is the final block of variation 1 of my code.
Two of my timings were:
Total check time 12.766 9.820
Total exists time 10.031 7.852
Total wsht time 2.152 1.543
The first issue to notice is the difference between the two sets of figures with the first run taking 33% more time than the second. This is typical of the variation you will get in the duration of short runs.
The second issue is that the total duration of the two timed statements is around .6 seconds less that the total duration. This is to be expected since all the other statement take some time; there is no expensive statement yet to find. Since your durations are so much longer than mine, you need to review your timings and perhaps test other statements if your unexplained duration is excessive.
The last issue is that the total duration of the worksheet update is so much less that the existence check. Since 10% of my data is faulty which I assume far exceeds your error rate, your worksheet update time should be much, much less than mine. Optimising the worksheet update would have minimal effect on the total duration.
With variation 2, the inner code is:
RowLast = .Cells(Rows.Count, ColCrnt).End(xlUp).Row
For RowCrnt = RowDataFirst To RowLast
TimeStart = Timer
ExistsCrnt = ValidNames.Exists(.Cells(RowCrnt, ColCrnt).Text)
TimeExistsTotal = TimeExistsTotal + Timer - TimeStart
If Not ExistsCrnt Then
TimeStart = Timer
.Cells(RowCrnt, ColCrnt).Interior.ColorIndex = 3
TimeWshtTotal = TimeWshtTotal + Timer - TimeStart
CountActionTypeErrors = CountActionTypeErrors + 1
End If
Next
Here I have used a For Loop instead of a For Each Loop. It is not clear from my timings which is faster. I will not bother to test further. I would use whichever I found more convenient even if I knew one was fractionally faster than the other.
For variation 3, I introduced a With statement for the cell:
With Wsht
RowLast = .Cells(Rows.Count, ColCrnt).End(xlUp).Row
For RowCrnt = RowDataFirst To RowLast
With .Cells(RowCrnt, ColCrnt)
TimeStart = Timer
ExistsCrnt = ValidNames.Exists(.Text)
TimeExistsTotal = TimeExistsTotal + Timer - TimeStart
If Not ExistsCrnt Then
TimeStart = Timer
.Interior.ColorIndex = 3
TimeWshtTotal = TimeWshtTotal + Timer - TimeStart
CountActionTypeErrors = CountActionTypeErrors + 1
End If
End With
Next
End With
This does look to be faster than variation 2, as expected, and perhaps a little faster than variation 1 but the evidence for these conclusions is poor.
Here are the timings:
In the bold lines, the timings are averages of the individual run times shown below.
The big reveal from this is that the size of the dictionary has no effect on run time.
My investigation has revealed nothing that would help you. I have explained how I created my timings and how I interpreted those timings so you can test what is different about your system.
Is it possible to write an infinite for loop in VB.NET?
If so, what is the syntax?
Do
Something
Loop
For i as Integer = 0 To 1 Step 0
If that's not hacky enough, can also write:
For i As Integer = 0 To 2
i -= 1
Next
or
while (true)
end while
ok, proper For answer:
Dim InfiniteLoop as Boolean = true;
For i = 1 to 45687894
If i = 45687893 And InfiniteLoop = true Then i = 1
End For
Aside from all the many answers given to make a loop run forever, this may just be the first that actually uses the value of Positive Infinity to cap the loop. Just to be safe though, I included an extra option to exit after a given number of seconds so it can measure the speed of your loop.
Sub RunInfinateForLoop(maxSeconds As Integer)
' Attempts to run a For loop to infinity but also exits if maxSeconds seconds have elapsed.
Dim t As Date = Now
Dim exitTime As Date = t.AddSeconds(maxSeconds)
Dim dCounter As Double
Dim strMessage As String
For dCounter = 1 To Double.PositiveInfinity
If Now >= exitTime Then Exit For
Next
strMessage = "Loop ended after " & dCounter.ToString & " loops in " & maxSeconds & " seconds." & vbCrLf &
"Average speed is " & CStr(dCounter / maxSeconds) & " loops per second."
MsgBox(strMessage, MsgBoxStyle.OkOnly, "Infinity Timer")
End Sub
What I do is add a timer then I change the interval to 1 and then I make it enabled then If I want it to constantly check something through the loop I just double click the timer for the timer_tick event then I type what I want. I usually use this for updating the settings if I want it to save every thing.