Results 1 to 23 of 23

Thread: Can't start timer.

  1. #1

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Question Can't start timer.

    I created my own File Copy dialog (had to) and I'm trying to use a timer to add a simple 5-frame animation.

    I created a form and placed a Timer object on it. The timer is set to Enabled with an interval of 150 (I also tried 15.)

    When I call the form, the timer never starts (I placed a breakpoint in the "_Tick" event to confirm.)

    I call the form from another form using:

    Code:
    frmCopy.Activate()
    frmCopy.Timer1.Enabled = True
    BackupData()                    ' Backup settings.
    BackupData() IS executed. It contains frmCopy.Show() & frmCopy.Refresh()

    frmCopy's code:

    Code:
    Public Class frmCopy
    
        Private Sub frmCopy_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Timer1.Enabled = True
            Timer1.Start()
        End Sub
    
        Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
            intTick += 1
            If intTick > 5 Then intTick = 1
            picCopyAni.Image = imgCopyAni.Images.Item(intTick)
        End Sub
    
    End Class
    A breakpoint in the form_Load confirms it loads just fine, but a breakpoint in the timer event is never tripped and I have no idea why.

    Any help is appreciated.
    Last edited by Mugsy323; May 5th, 2022 at 07:20 PM.

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Can't start timer.

    You haven't shown us your copy code but I'm betting that it's executed on the UI thread. In that case, the UI thread is too busy to maintain the UI. That's why you perform long-running tasks on secondary threads.

  3. #3

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Thx for the reply. Here is my Copy code:

    Code:
            For Each myFile In Directory.GetFiles(strPath & "Save", "Data*.*")
                Try
                    My.Computer.FileSystem.CopyFile(myFile, strBackupPath & My.Computer.FileSystem.GetName(myFile), True)   ' Must trim path from source file otherwise Crash. No idea why. Using Win copy dialog prompts to overwrite EVERY SINGLE FILE.
                    intCnt += 1
                    frmCopy.lblFileNum.Text = intCnt.ToString
                    frmCopy.lblSrcCopy.Text = myFile.ToString
                    frmCopy.Refresh()
                Catch
                    ' Attempting to copy "~Temp" files causes an error after the fact. Ignore.
                End Try
            Next
    I'm not sure how to perform a "secondary" thread.

  4. #4
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,047

    Re: Can't start timer.

    Take a look at the BackgroundWorker component. That was created for this kind of a situation, and is likely to be well suited to this task.

    The timer IS starting, it's just that it raises a tick event, which just gets queued up. The UI will get to it once it is free to do so. Normally, the UI is spending most of it's time waiting around, so it would get to the timer tick event about as quick as it happens. In this case, the UI thread is totally occupied with your copying loop, so everything else, including processing timer ticks, responding to user interaction, and so forth, waits until the loop has finished. By shifting the work onto a different thread, you are freeing up the UI to process messages and events.

    I'm a little surprised that your loop is taking all that long, though. That .Refresh could prove to be costly, and if there are a LOT of files, then that could prove to be costly, as well, but I'd want to do at least SOMETHING in that Catch block. Exceptions that are thrown, even if you do nothing with them, are terribly costly in terms of time, so I'm wondering if you aren't getting some exceptions that you would find interesting. Just writing the exception message to a List(of String) would be enough for you to at least see whether anything is happening once the loop has completed.

    Also, if you use the BGW, it has a ReportProgress method that raises a progress event on the UI thread. You'd want to be doing your updating of those labels in that event handler, not in the DoWork method of the BGW. Those are UI elements, so you want to update them on the UI thread. You shouldn't need the .Referesh, in that case, either, because the progress event would fire, you'd update the labels, then the event would end, which would allow the UI to process the Paint message, which would update the labels.
    My usual boring signature: Nothing

  5. #5

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Thx for the reply. I'm still stuck.

    I scoured Google for how to do a multi-threaded file copy using "Tasks", yet even after making extensive changes, my Timer still never runs (Breakpoint on first line of _Tick event.)

    Here is what I did to (supposedly) make the copy task multi-threaded so it does not consume ALL of my CPU time:

    Code:
    Imports System.IO
    Imports System.Threading.Tasks
    
    Public Class frmCopy
    
        Private Sub frmCopy_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.Show()
            Me.Refresh()
            Timer1.Enabled = True
            Timer1.Start()
            CopyFiles()
        End Sub
    
    Private Sub CopyFiles()
            Dim tasks As New List(Of Task)
            ...
    Code:
            Try
                For Each myFile In Directory.GetFiles("D:\Save\", "Accessories*.*")
                    intCnt += 1
                    lblFileNum.Text = intCnt.ToString
                    lblSrcCopy.Text = myFile.ToString
                    Refresh()
                    tasks.Add(Task.Factory.StartNew(Sub() File.Copy(myFile,
                     strBackupPath & My.Computer.FileSystem.GetName(myFile), True)))
                Next
            Catch
                ' Attempting to copy "~Temp" files causes an error after the fact. Ignore.
            End Try
    This also performs the Copy function, yet the timer event still never runs (I'm guessing this method is still doing things the same way.) This method also has the serious drawback (AFAICT) that it can't be used to copy folders.

    I tried commenting out all those "Refresh" statements, but the labels (showing the file name & number) never update w/o it. And the timer still didn't run.

    I'll look into using "BackgroundWorker". Thx.

  6. #6
    Addicted Member
    Join Date
    Jul 2017
    Location
    Exeter, UK
    Posts
    180

    Re: Can't start timer.

    If you use the BGW then you won't even need a Timer, just call your copy sub from BGW.Dowork().

  7. #7

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    The timer is used to add an animation of the copy process.

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Can't start timer.

    You may find this relevant/useful.

  9. #9
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,764

    Re: Can't start timer.

    How I'd do it (kinda) FWIW

    First how to instantiate FrmCopy,

    Code:
            Dim foo As New frmCopy
            foo.ShowDialog()
            foo.Dispose()
    The frmCopy

    Code:
    Public Class frmCopy
    
        'note Shown event used, not Load <<<<<<<<<<
        Private Sub frmCopy_Shown(sender As Object, e As EventArgs) Handles Me.Shown
            Timer1.Enabled = True
            Timer1.Start()
            Dim t As Task
            t = Task.Run(Sub()
                             CopyFiles()
                         End Sub)
        End Sub
    
        Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
            'didn't have real code
            Static ct As Integer = 0
            lblTmrTick.Text = ct.ToString("n0")
            ct += 1
        End Sub
    
        Private _cpyFrom As String = "D:\Save\"
        Private strBackupPath As String = "???????" ' <<<<<<<<<<<<<<<<<
        Private intCnt As Long = 0L
        Private Sub CopyFiles()
            intCnt = 0L
            Try
                Dim _files As New Concurrent.ConcurrentBag(Of String)(IO.Directory.GetFiles(_cpyFrom, "Accessories*.*"))
                Parallel.ForEach(_files, Sub(aFile As String)
                                             Dim toPath As String
                                             toPath = IO.Path.Combine(strBackupPath, IO.Path.GetFileName(aFile))
                                             Try
                                                 IO.File.Copy(aFile, toPath, True)
                                                 Threading.Interlocked.Increment(intCnt)
    
                                                 'get on UI thread
                                                 Me.BeginInvoke(Sub()
                                                                    lblSrcCopy.Text = aFile
                                                                    lblFileNum.Text = Threading.Interlocked.Read(intCnt).ToString
                                                                    ' Me.Refresh '???
                                                                End Sub)
                                             Catch ex As Exception
                                                 '?????
                                             End Try
                                         End Sub)
            Catch ex As Exception
                '?????
            End Try
        End Sub
    End Class
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  10. #10

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Thx. I'll try this tonight.

    One quick question though: What is the benefit of turning the form into it's own object ("foo")?

  11. #11
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,764

    Re: Can't start timer.

    Quote Originally Posted by Mugsy323 View Post
    Thx. I'll try this tonight.

    One quick question though: What is the benefit of turning the form into it's own object ("foo")?
    See this by jmcilhinney.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  12. #12
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Can't start timer.

    Quote Originally Posted by Mugsy323 View Post
    What is the benefit of turning the form into it's own object ("foo")?
    There is no benefit because that's not what is being done. Every form is an object. It's just a matter of where you get that object from. The default instance of a form, obtained via the class name, is still an object. Default instances are a nod to people who either can't (beginners) or won't (VB6 developers) understand proper OO principles. There are no default instances in C# and I've never heard anyone lament that. Most experienced VB developers don't use default instances because there's no need. Just treat forms like any other type/object because that's what they are.

  13. #13

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Great. Your code appears to work (though I am having a bit of difficulty integrating it into my existing code.) My integration has somehow resulted in the form being reloaded recursively (my problem, not yours.) I'll have to spend some time tracking it down.

    The new method has created a new problem though: Attempting to update any/all labels on my form now gives me the following error:

    Name:  lbl_error.jpg
Views: 215
Size:  28.0 KB

    I tried prefixing each label with "foo." but that didn't help. Same error. But at least now the Timer event is finally being triggered (breakpoint tripped.)

    Almost there. Thx.

  14. #14
    PowerPoster PlausiblyDamp's Avatar
    Join Date
    Dec 2016
    Location
    Pontypool, Wales
    Posts
    2,474

    Re: Can't start timer.

    Quote Originally Posted by Mugsy323 View Post
    Great. Your code appears to work (though I am having a bit of difficulty integrating it into my existing code.) My integration has somehow resulted in the form being reloaded recursively (my problem, not yours.) I'll have to spend some time tracking it down.

    The new method has created a new problem though: Attempting to update any/all labels on my form now gives me the following error:

    Name:  lbl_error.jpg
Views: 215
Size:  28.0 KB

    I tried prefixing each label with "foo." but that didn't help. Same error. But at least now the Timer event is finally being triggered (breakpoint tripped.)

    Almost there. Thx.
    That exception is exactly what Shaggy warned you about back in post #4, you want update the UI from a background thread.

  15. #15

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Quote Originally Posted by PlausiblyDamp View Post
    That exception is exactly what Shaggy warned you about back in post #4, you want update the UI from a background thread.
    Thanks. This whole "BackgroundWorker" and use of "Tasks" is completely new to me, so I haven't the foggiest on how to "update the UI from a background thread."

    I thought that's what adding the "foo." prefix would do, but clearly not. Is there another Event or Object I should be using?

    Thx.

  16. #16
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Can't start timer.

    Quote Originally Posted by Mugsy323 View Post
    I haven't the foggiest on how to "update the UI from a background thread."
    Follow the CodeBank link in my signature and check out some of the resources there. There is one thread specifically dedicated to updating the UI from a worker thread. You'll then see that that's what the provided code is doing when it calls BeginInvoke. There is also a thread on using a BackgroundWorker and that will show how and why you don't need to use that mechanism in that case. The BackgroundWorker exists specifically so that you don't have to and can just call methods and handle events, just as you've already many times. That same mechanism is still used but it's buried inside the BackgroundWorker object.

  17. #17

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    HALLELUJAH!

    (For anyone searching this in the future, I found your tutorial here.)

    There were a couple of details that I wasn't clear on (specifically, "SetTextBoxText" vs "SetTextBoxTextInvoker") but I was able to successfully integrate your code example (using the "SetControlTextInvoker" method so I could change different labels using the same function) but eventually go it to work so now the animation, the label updates, and the copy process all perform simultaneously.

    One last concern though: After the copy process was done, I still had to use your Invoker to change my labels ("Done!", etc.)

    How do I stop "multithreading" so I'm able to go back to update to updating objects "the old way"?

    Big thanks (I feel like I'm in college again.)

  18. #18
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,350

    Re: Can't start timer.

    Quote Originally Posted by Mugsy323 View Post
    There were a couple of details that I wasn't clear on (specifically, "SetTextBoxText" vs "SetTextBoxTextInvoker")
    SetTextBoxText is a method that sets the Text of a TextBox. SetTextBoxInvoker is a delegate that refers to that method. Delegates are basically objects that refer to methods, so that you can pass them around anywhere, just like other objects, and then invoke the method they refer to anywhere you like.
    Quote Originally Posted by Mugsy323 View Post
    One last concern though: After the copy process was done, I still had to use your Invoker to change my labels ("Done!", etc.)

    How do I stop "multithreading" so I'm able to go back to update to updating objects "the old way"?
    You don't stop multithreading. Code is executed on whatever thread it's executed on. Code that is executed on the UI thread can modify a control directly and code that is executed on a secondary thread cannot. It's that simple. That's why the BackgroundWorker was created to make such situations easier. The DoWork event handler is executed on a secondary thread so that's where you do your background work while the ProgressChanged and RunWorkerCompleted events are raised on the UI thread so that's where you update the UI.

  19. #19

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Thx. So if I understand you correctly, I must execute code "on the correct thread" to modify particular objects. And those threads will always exist once used.

    So there is no way to go back to (eg):

    MyLabel.Text = "sample"

    ...on the same form after using:

    SetControlText(MyLabel, "sample")

    Okay. Fortunately, it's not a big deal since the form isn't really needed once the process is done.

    Now that I have the copying of Files (with wildcards) working using your method, the only thing left is to figure out how to do the same thing with folders (with wildcards):

    My working loop for files:

    Code:
            Try
                Dim _files As New Concurrent.ConcurrentBag(Of String)(IO.Directory.GetFiles("Save\Data", "File*.*"))
                Parallel.ForEach(_files, Sub(aFile As String)
                                             Dim toPath As String
                                             toPath = IO.Path.Combine(strBackupPath, IO.Path.GetFileName(aFile))
                                             Try
                                                 SetControlText(lblFileNum, intCnt.ToString)
                                                 SetControlText(lblSrcCopy, aFile.ToString)
                                                 IO.File.Copy(aFile, toPath, True)
                                                 Threading.Interlocked.Increment(intCnt)
                                             Catch ex As Exception
                                                 ' Attempting to copy "~Temp" files causes an error after the fact. Ignore.
                                             End Try
                                         End Sub)
            Catch ex As Exception
                ' Attempting to copy "~Temp" files causes an error after the fact. Ignore.
            End Try
    This copies individual files just fine, but changing ".GetFiles" to ".GetDirectories" isn't enough to use the same method for folders (probably just a logic error.)

  20. #20
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,047

    Re: Can't start timer.

    No, at least parts of that are not correct. The UI thread exists as long as the program is running (there are some weird programs where that would not be the case, but if you get there, you'll know about it ahead of time, so you can ignore that for now). The UI thread, and any code executed on that thread, can access UI elements in the fashion you showed at any time:

    MyLabel.Text = "Some text"

    It is when you access controls from somewhere other than the UI thread that you have to do something different. However, those other threads exists as long as they have something to do, then they go away.

    Your loop will use threads, so if they access controls, then you do have to do something special, since those aren't the UI thread. For that reason, it is generally best to not interact with controls from any background thread. You don't always get that option, but there is usually a way to do that. In your case, much of the complexity of the code is based on the fact that you want to keep updating those labels.

    If you had written that using a BGW, then you could have raised the reported progress event, which would have worked on the UI thread and you wouldn't have had to worry about getting to controls across threads.

    When it comes to objects other than UI elements, the situation is more complicated. Some can be thread local, others are not. That doesn't appear to be something you need to think about in the current situation, though.
    My usual boring signature: Nothing

  21. #21

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Quick question.

    From JMC's example:

    Code:
    Private Sub SetTextBoxText(ByVal text As String)
        If Me.TextBox1.InvokeRequired Then
            Me.TextBox1.Invoke(New SetTextBoxTextInvoker(AddressOf SetTextBoxText), text)
            ...
    ...seems recursive. I'm glad it works, but when I don't understand the process, I won't be able to apply it to different situations.

    Thx.

  22. #22
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,543

    Re: Can't start timer.

    When you call it initially, the InvokeRequired will be set to true... so it drops into the Me.TextBox1.Invoke line ... which then calls the method on the UI tread... so yes, it's recursive... but on second entry, the InvokeRequired is NOT set - because now it's on the UI trhead - so it goes into the Else condition, where you can actually set the textbox itself.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  23. #23

    Thread Starter
    Lively Member Mugsy323's Avatar
    Join Date
    Oct 2014
    Posts
    83

    Re: Can't start timer.

    Great. I think we can label this Case Closed.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width