Results 1 to 10 of 10

Thread: Making the Paint routine fire?

  1. #1

    Thread Starter
    Member
    Join Date
    May 2002
    Posts
    55

    Question Making the Paint routine fire?

    I am writing a simple Visual Basic application. The application create a form, and in the form_Load routine, another subroutine is called. The subroutine is defined using Async because ‘Await’ is used within the sub. The subroutine ends with Me.Refresh. The problems are these:
    1. form_Paint is being called before the subroutine has executed and the subroutine creates information that is needed by the form_Paint routine.
    2. The command Me.Refresh does not cause form_Paint to be called. (I am wondering if this issue is caused by form_Paint’s having been called already but without the needed info.)

    Suggestions?

  2. #2
    Hyperactive Member
    Join Date
    Jan 2013
    Posts
    472

    Re: Making the Paint routine fire?

    I would try form1_show instead of form1_load
    I would also try putting application create a form in its own sub.
    I have had trouble putting things in the form.load.
    Last edited by georgesutfin; Jan 20th, 2022 at 04:48 PM.

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    37,332

    Re: Making the Paint routine fire?

    I wouldn't be calling Refresh at all. What that does is force a redraw. It's a bit rude. You do that when you have to interrupt something else to do a redraw. That doesn't sound appropriate for what you described. Invalidate should be sufficient. With Invalidate, painting will happen when the UI thread has some time to process messages, in this case, the paint message. That time will come once the sub is done, but not while the sub is still running. Refresh will force the drawing right away, before the sub completes.

    However, you might consider a few other points. There is the constructor, the Load event, and the Shown event. I'd say that you probably chose the worst one, but I'm not quite sure about that. The constructor is called when the form is created. The other two fire as the form is shown. The creation of the form could be done well before the form is shown, depending on how your program works. If the form is the startup form, then it will be shown right after creation, and there's not much you can do about that. If you are showing the form later, then you might be able to create it long before you show it.

    It sounds like the sub might be a good candidate to put in the constructor (Sub New), rather than the Load event.

    Additionally, the load event is happening before the form is shown. Does it really make sense to do either invalidate OR refresh at that point? Perhaps those could be moved later...or left out entirely. If you are doing the work early on, what's to invalidate? The form hasn't even drawn the first time, let alone had a need to repaint.
    My usual boring signature: Nothing

  4. #4
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,518

    Re: Making the Paint routine fire?

    If the sub is Asynchronous, I assume that means it runs on its own thread. Normally non-GUI threads shouldn't try to perform GUI actions and I would think Me.Refresh would qualify as a GUI action.
    You may have to Invoke the call, but I haven't worked with async subs, so may be mistaken.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  5. #5
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    7,404

    Re: Making the Paint routine fire?

    Quote Originally Posted by groston View Post
    I am writing a simple Visual Basic application. The application create a form, and in the form_Load routine, another subroutine is called. The subroutine is defined using Async because ‘Await’ is used within the sub. The subroutine ends with Me.Refresh. The problems are these:
    1. form_Paint is being called before the subroutine has executed and the subroutine creates information that is needed by the form_Paint routine.
    2. The command Me.Refresh does not cause form_Paint to be called. (I am wondering if this issue is caused by form_Paint’s having been called already but without the needed info.)

    Suggestions?
    I would suggest that you look into how Await/Async actually works. It doesn't work the way you may think it does.

    Await actually breaks a method into discrete executable blocks to be executed at different times. When an Await is performed, it will actually yield to the application's message loop which among other things will allow events like Paint to fire.

    You can test this for yourself using this code:-
    Code:
    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
            Debug.WriteLine("Entered Form_Load")
            Debug.WriteLine("Exiting Form_Load")
    
        End Sub
    
        Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    
            Debug.WriteLine("Form_Paint")
    
        End Sub
    End Class
    The above code is pretty predictable. It will output this:-
    Code:
    Entered Form_Load
    Exiting Form_Load
    Form_Paint
    However if you did this:-
    Code:
    Public Class Form1
        Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
            Debug.WriteLine("Entered Form_Load")
            Await Task.Delay(500)
            Debug.WriteLine("Exiting Form_Load")
    
        End Sub
    
        Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    
            Debug.WriteLine("Form_Paint")
    
        End Sub
    End Class
    You will get this output:-
    Code:
    Entered Form_Load
    Form_Paint
    Exiting Form_Load
    The Paint event gets executed before Form_Load has exited because the Await stops execution of the current method at that point and yields to the application's message loop which is why the Paint event is fired before Form_Load is finished executing. This is the fundamental problem with your code.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  6. #6
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    37,332

    Re: Making the Paint routine fire?

    I think we might all be making assumptions based on not seeing the code and interpreting what was written in different ways. I felt that Load called a Sub, which had an Await somewhere in it, but also ended with a call to .Refresh. If so, that would suggest that the Sub itself is on the UI thread, though it launches something on a different thread. I also thought that the Refresh was coming at the end of the Sub, so whether the messages could be processed would be irrelevant. Both of those are just interpretation, though, and both could be wrong.
    My usual boring signature: Nothing

  7. #7
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    7,404

    Re: Making the Paint routine fire?

    Eh....I think he was very clear about what he was describing. Lets look:-
    form_Load routine, another subroutine is called.
    The subroutine is defined using Async because ‘Await’ is used within the sub.
    The subroutine ends with Me.Refresh.
    That is pretty clear to me. Here is a mock up of what those 3 quotes above describe:-
    Code:
    Public Class Form1
        Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Await SomeSub()
        End Sub
    
        Private Async Function SomeSub() As Task
    
    
            'Lets pretend this is some long running task
            Await Task.Delay(1000)
    
            Me.Refresh()
        End Function
    End Class
    Now let's look at the problem he has:-
    form_Paint is being called before the subroutine has executed and the subroutine creates information that is needed by the form_Paint routine.
    If we test this using this code:-
    Code:
    Public Class Form1
        Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Debug.WriteLine("Form_Load entered")
    
            Await SomeSub()
    
            Debug.WriteLine("Form_Load exit.")
        End Sub
    
        Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
            Debug.WriteLine("Form_Paint")
        End Sub
    
    
        Private Async Function SomeSub() As Task
            Debug.WriteLine("SomeSub entered")
    
            'Lets pretend this is some long running task
            Await Task.Delay(1000)
            Debug.WriteLine("SomeSub exit")
            Me.Refresh()
        End Function
    End Class
    We get this output:-
    Code:
    Form_Load entered
    SomeSub entered
    Form_Paint
    SomeSub exit
    Form_Paint
    Form_Load exit.
    Highlighted in red is exactly the problem he describes. The Form's Paint event is being fired before his asynchronous subroutine returns. He makes it very clear that his subroutine, represented by SomeSub in the mock up, creates data that Paint depends on. This means that he would not want the Paint event to be raised before the subroutine has finished doing it's thing.

    As for his second problem:-
    The command Me.Refresh does not cause form_Paint to be called.
    It actually does call Form_Paint. I honestly don't know why he thinks it didn't.

    Quote Originally Posted by Shaggy Hiker View Post
    If so, that would suggest that the Sub itself is on the UI thread, though it launches something on a different thread.
    Not quite. You see, he specifically said that his subroutine has an Await inside it. What this means is that whatever the is being Awaited is what is probably what is running on a different thread but the sub that called Await, is going to be signed up as a continuation by the compiler which will be executed on the UI thread which is why you can safely call Refresh in that sub.

    I think you fundamentally misunderstand how Async/Await works. Await doesn't magically make a sub or function multithreaded. It's actually just syntactic sugar. The compiler re-arranges the method into a series of continuations when it encounters Awaits. For example if I did something like this:-
    Code:
    Public Class Form1
        Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim l As New Label() With {.Left = 0, .Top = 0}
    
            Me.Controls.Add(l)
    
            l.Text = "1"
    
            Await Task.Delay(700)
    
            l.Text = "2"
    
            Await Task.Delay(700)
    
            l.Text = "2"
    
            Await Task.Delay(700)
    
            l.Text = "3"
    
            Await Task.Delay(700)
    
            l.Text = "4"
    
            Await Task.Delay(700)
    
            l.Text = "5"
    
            Await Task.Delay(700)
    
            l.Text = "6"
    
            Await Task.Delay(700)
    
            l.Text = "7"
    
        End Sub
    End Class
    You would notice than you can freely move the Form while Load is still executing. It can leave the impression that Form_Load is multithreaded but it's actually not. As proof, notice I can update the Label directly. That produces no error because it's not a cross thread call. The compiler breaks up Form_Load into a series of continuations that are executed in sequential by the UI thread itself. Note that, Task.Delay itself could be running on another thread. I don't think it actually does but lets pretend it is since a quite a few Tasks actually do. Even in such a case it's not a problem because the Task itself is not what is trying to update the UI, it's the code in-between the Awaits that does this and this code is executed on the UI thread.

    Still though, you do raise a great point and we are still assuming. Perhaps I got it all wrong and OP's problem is completely different. He should come and clear things up. But if I go by what he said, his problem is pretty clear to me.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  8. #8
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    37,332

    Re: Making the Paint routine fire?

    That seems like a pretty clear description.

    I didn't think that Paint would be called at all that early, but I've certainly never looked at it. What's the point of a paint message before the form is visible? There probably isn't a point, aside from consistency.
    My usual boring signature: Nothing

  9. #9
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    7,404

    Re: Making the Paint routine fire?

    Quote Originally Posted by Shaggy Hiker View Post
    What's the point of a paint message before the form is visible?
    Actually, the Form would become visible as soon as Form_Load executes the first Await. You can test with this:-
    Code:
    Public Class Form1
        Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim l As New Label() With {.Left = 0, .Top = 0}
    
            Me.Controls.Add(l)
    
            l.Text = "Hello world."
    
            'The Form is shown as soon as this is executed
            Await Task.Delay(5000)
    
            l.Text = "Exiting Form_Load"
    
        End Sub
    End Class
    If you executed the above code, you would see Form shown and fully operational before Form_Load has actually finished executing. This is because the compiler has actually split the Form_Load sub into two parts and the Await statement is the dividing line. So in a sense Form_Load has finished executing after Await is called. The rest of the Form_Load method which is whatever comes after Await is signed up as a continuation that is executed after Task.Delay has completed.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  10. #10
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    37,332

    Re: Making the Paint routine fire?

    Guess I haven't played around with Asynch Await enough.
    My usual boring signature: Nothing

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