RazorKillBen
RazorKillBen

Reputation: 573

How can I process an "animation" on my Windows Form without locking up the UI?

I am currently using some primitive animations in vb.net to improve the GUI for the end client. It's a thin panel strip that loops to extend underneath the textbox when it's entered.

It works pretty well. The problem I have is that the animation has to complete before the textbox will accept text. This means that when a user tabs between the "username" and "password" text boxes, they sometimes start typing too quickly for the animation to complete and therefore miss the first few characters of their password.

I have tried adding the "animation" code to a background worker, but this completely freezes the UI for around 20 seconds when tabbing between the two textboxes (it works fine when clicking, but tabbing causes the worker thread to freeze).

I have done some research and despite the suggestion, I'm not sure if a background worker would work in this instance, as ultimately, it'll just be updating the UI controls and that seems like it's against what the background worker is there to do.

This is how I had things setup:

Private Sub bwUsernameLines_DoWork(sender As Object, e As DoWorkEventArgs) Handles bwUsernameLines.DoWork
        Dim x As Integer = 0

        pnlUsername_under.Width = x
        pnlUsername_under.Visible = True

        'loop speed 1
        Do Until x = 180
            pnlUsername_under.Width = x
            Threading.Thread.Sleep(5)
            pnlUsername_under.Refresh()
            x += 10
        Loop

        bwUsernameLines.CancelAsync()
End Sub

Private Sub txtUsername_Enter(sender As Object, e As EventArgs) Handles txtUsername.Enter
        bwUsernameLines.RunWorkerAsync()
        txtUsername.Text = vbNullString
End Sub

Tabbing into the textbox causes the UI to freeze whilst clicking into the textbox doesn't. Is there something I'm doing wrong to allow this to work, or is there a completely different way of doing this?

Essentially, all I want is for the loop animation to continue to play, whilst the textbox allows the entering of text still, rather than waiting for it to complete.

Upvotes: 0

Views: 748

Answers (1)

Steve
Steve

Reputation: 216293

First thing to do is to change where you handle your panel resize. Has you have correctly understood, changing UI objects from a NON-UI thread is problematic and BackgroundWorker has been created to solve this problem in a WinForms application.

So we need to set something on the BackgroundWorker to prepare it for its task.
(You can do these changes through the Winform Designer or in code in your Form constructor but after the InitializeComponent)

' Define the event handler that runs the resize in the UI thread
AddHandler bwUsernameLines.ProgressChanged, AddressOf bwUsernameLines_SizeChanged
' Make sure that the backgroundworker reports the progress
bwUsernameLines.WorkerReportsProgress = True

Now your DoWork can be changed to just

Private Sub bwUsernameLines_DoWork(sender As Object, e As DoWorkEventArgs) 
    Dim x As Integer = 0
    'loop speed 1
    Do Until x = 500
        Threading.Thread.Sleep(5)

        ' Raises the Progress_Changed event in the UI thread
        ' Notice that this overload takes an value that represent your progress so far
        bwUsernameLines.ReportProgress(x)
        x += 10
    Loop
End Sub

Finally just add the event handler for the Progress_Changed event

Private Sub bwUsernameLines_SizeChanged(sender As Object, e As ProgressChangedEventArgs)
   ' Get that x passed above from the property  ProgressPercentage
   pnlUsername_under.Width = e.ProgressPercentage

End Sub 

Upvotes: 1

Related Questions