Cannot get Progress Bar to Update while running tasks in WPF

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Not sure what I am doing wrong, but I can't get it to work. I am doing a CPU intensive task where I am creating a large number of players for a football game I am making, and I am using the Task.Factory.StartNew method to run it on. I then create a separate task for the Progress updater but I can't seem to get it to work.

I have a WPF progressbar which I have bound to an INotifyPropertyChanged Property, however it does not update and the UI appears frozen, which I thought tasks were supposed to eliminate...

VB.NET:
    TimeIt(Sub() ReallyGenNewPlayers())

    Private Sub TimeIt(MyAction As Action)
        Dim SW As New Stopwatch
        SW.Start()
        MyAction()
        Console.WriteLine($"Total Time Generating Players: {SW.Elapsed} seconds")
        SW.Stop()
    End Sub


    Private Sub ReallyGenNewPlayers()
        Task.Factory.StartNew(Sub() GenNewPlayersASync()).Wait()
    End Sub


     Private Sub GenNewPlayersASync()
        Dim x As Integer = 0
        'Generate the Players on an Async Thread

        For i As Integer = 1 To NumPlayers
            x = i
            Task.Factory.StartNew(Sub() CollegePlayers.GenDraftPlayers(x, MyDraft, DraftDT, DraftClass, PosCount)).Wait()
            Dim mytask As Task(Of Double) = Task(Of Double).Factory.StartNew(Function() UpdateProgressBar(x, NumPlayers))
            ProgBarValue = mytask.Result
        Next i

    End Sub

    Private Function UpdateProgressBar(ByVal playernum As Integer, ByVal TotalPlayers As Integer) As Double
        Dim myval As Double
        Return myval = (playernum / TotalPlayers) * 100
    End Function

Any help would be appreciated, and if you could let me know the how's and why's I would appreciate it as well so I can learn...
 

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Why aren't you copying and pasting your actual code?

I did, but I must have changed it in the program after pasting it once it threw an error and then forgot to change it here.

Anyways, I have somewhat figured out what is going wrong.

I have a third moving part involved.

I have a GeneratePlayers Page, the ViewModel and then the ProgressBarDialog Window

Basically here is what I want to happen. When the user clicks the button to Generate Players from the GeneratePlayers page, that page gets disabled and it Opens up a ProgressBarDialong Window on top of it saying "Generating Players...." and then the progress bar updates while its generating players until its finished, at which point it closes itself and control returns back to the GeneratePlayers Page. I got the opening and closing, etc working but not the updating of the progress bar.

Here is what I need to happen:

1) GeneratePlayers page opens up a new instance of the ProgressBarDialog Window once the button is clicked
2) ProgressBarDialog instance stays open and updates itself from the ViewModel Properties that its bound to.
3) ViewModel however is getting its values from the GeneratePlayers Page, which I believe is making it so two separate instances of the ViewModel are being used...Ie, the GeneratePlayers instance is updating the values, but the ProgressBarDialong instance is reading its own values which are not being updated and are always 0. Do I need to make the ViewModel NotInheritable and its Properties/Methods as Shared? Make it a Module? VB is such a pain in the ass compared to C# where I could just make it static....

Whats the easiest way to make these 3 parts work together the way they need to?

UPDATE: OK, I added some code to help me see what is going on...

I have it printing to the console the System.Threading.Thread.CurrentThread.ManagedThreadID
So the original GeneratePlayers Thread is 8 for example, then I call:

--->Prog.Show() --->On Thread 8
--->Window.GetWindow(Prog) ----> On Thread 8

VB.NET:
Await Task.Run(Sub()


                           Console.WriteLine($"This is the current thread 2: { System.Threading.Thread.CurrentThread.ManagedThreadId}")
                           TimeIt(Sub() GenNewPlayersASync(MyVM, Prog))
                       End Sub)

Which along with declaring the ButtonClick as Async frees up the Progress Bar Window so I can move it around, etc when it pops up(its not stuck like it was before).
This and everything past this becomes Thread 10...

and checking Prog.Dispatcher.CheckAccess() inside Thread 10 is returning False(which makes sense since its on Thread 8)...which means I need to run Prog.Dispatcher.BeginInvoke to gain access to it and then update the UI from within it, correct?


I've also seen where they say to open the window inside the thread you need it to be in but then it throws an error saying the method needs to declared as an <STAThread because many UI components require this...so I'm not sure which way to go about this...
However, I do that and it doesn't do anything...getting closer, but seems like I'm still far away...
 
Last edited:

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
FINALLY!!!! Got it working---I called Prog.Dispatcher.BeginInvoke(Sub() LoopPercent(byval x as integer, byval MyVM as ProgressBarDialogViewmodel) inside the player generation loop...don't have it bound via UI, but its working by directly updating the progress.Value inside the Loop...now I need to figure out how to get the binding to work properly...

VB.NET:
    Private Sub GenNewPlayersASync(ByVal MyVM As ProgressBarDialogViewModel, ByVal Prog As ProgressBarDialog)


        Dim x As Integer = 0
        'Generate the Players on an Async Thread
        Console.WriteLine($"This is the current thread 4: {System.Threading.Thread.CurrentThread.ManagedThreadId}")
        Console.WriteLine($"Progress Bar Has Access? {Prog.Dispatcher.CheckAccess()}")


        For x = 1 To MyVM.TotalPlayers
            CollegePlayers.GenDraftPlayers(x, MyDraft, DraftDT, DraftClass, PosCount)
            Dim i As Integer = x
            MyVM.PlayerCount = i
[B]            If MyVM.PlayerCount > 1 And Not Prog.Dispatcher.CheckAccess() Then[/B]
[B]                Prog.Dispatcher.BeginInvoke(Sub() Prog.LoopPercent(x, MyVM))[/B]
[B]            End If[/B]
        Next x
    End Sub

Inside the ProgressBar Model itself:

VB.NET:
[B]    Public Sub LoopPercent(ByVal x As Integer, ByVal MyVM As ProgressBarDialogViewModel)[/B]
[B]        progress.Value = CInt(((x / MyVM.TotalPlayers) * 100))[/B]
[B]    End Sub[/B]

Damn...2 days but I got it figured out!
 
Last edited:
Top Bottom