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...
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,717
Location
Sydney, Australia
Programming Experience
10+
Any changes you make to a control must be done on the UI thread. Yes, use a background task to perform your long-running operation but then any updates to the UI during that task must be marshalled to the UI thread to be performed. Here's a WPF-specific example I added to a thread specifically on updating the UI from a secondary thread:

Accessing Controls from Worker Threads
 

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Any changes you make to a control must be done on the UI thread. Yes, use a background task to perform your long-running operation but then any updates to the UI during that task must be marshalled to the UI thread to be performed. Here's a WPF-specific example I added to a thread specifically on updating the UI from a secondary thread:

Accessing Controls from Worker Threads

Isn't Creating another task for the ProgressBar giving it its own thread? Or is it simply adding another task to the same thread?

Is it possible to do this via a Task instead of a background worker?

Is it not possible to simply do this via a UI Binding to a INotififyPropertyChanged Property without using a background worker or a task?
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,717
Location
Sydney, Australia
Programming Experience
10+
Isn't Creating another task for the ProgressBar giving it its own thread? Or is it simply adding another task to the same thread?
A Task is executed on a thread pool thread. If you create a new Task then you're still executing it on a secondary thread. As I said, any modification of a control must be done specifically on the UI thread. It doesn;t matter which secondary thread you use, a secondary thread is not the UI thread.
Is it possible to do this via a Task instead of a background worker?
Of course. The use of the BackgroundWorker was simply convenient for the example but is irrelevant to the principle. The principle is that if you are executing code on a secondary thread and you want to update the UI then you need to marshal a method call to the UI thread to do that. A Task and a BackgroundWorker are simply two different mechanisms to execute code on a secondary thread but both end up using a thread pool thread so there's not even that difference. Stop looking at the irrelevant differences and focus on the relevant similarities.
Is it not possible to simply do this via a UI Binding to a INotififyPropertyChanged Property without using a background worker or a task?
The problem is that updating a property on a secondary thread is going to raise the corresponding event on that same secondary thread, so if you've bound a control then you're still trying to modify that control on that same secondary thread. At some point, you need to marshal a method call to the UI thread. If you're going to be using multi-threading then you could do that in your ViewModel and use the SynchronizationContext class to ensure that the event is raised on the UI thread. There's an example of using the SynchronizationContext class in that same thread I linked to earlier.
 

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
A Task is executed on a thread pool thread. If you create a new Task then you're still executing it on a secondary thread. As I said, any modification of a control must be done specifically on the UI thread. It doesn;t matter which secondary thread you use, a secondary thread is not the UI thread.

Of course. The use of the BackgroundWorker was simply convenient for the example but is irrelevant to the principle. The principle is that if you are executing code on a secondary thread and you want to update the UI then you need to marshal a method call to the UI thread to do that. A Task and a BackgroundWorker are simply two different mechanisms to execute code on a secondary thread but both end up using a thread pool thread so there's not even that difference. Stop looking at the irrelevant differences and focus on the relevant similarities.

The problem is that updating a property on a secondary thread is going to raise the corresponding event on that same secondary thread, so if you've bound a control then you're still trying to modify that control on that same secondary thread. At some point, you need to marshal a method call to the UI thread. If you're going to be using multi-threading then you could do that in your ViewModel and use the SynchronizationContext class to ensure that the event is raised on the UI thread. There's an example of using the SynchronizationContext class in that same thread I linked to earlier.

OK gotcha...using a background worker per your example...

One last issue I am having is that I need to get the current value of "x" in the player generation sub to be able to know the value that should be updated, so I would need to call the worker from inside the secondary thread...however inside the BackgroundWorker1_DoWork event, I have no parameters to supply the Me.UpdateProgressBar since I don't know what they are at that point...


There is also no "InvokeRequired" property on a progress bar going by your code that returns a value, so I am unsure what to put there...
VB.NET:
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()

       ---->'Call Worker UpdateProgressBar(x, NumPlayers)

            
        Next i

    End Sub
 
Last edited:

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
In WPF, the correct way to do this is to create a view model with a property that has change notification enabled through INotifyPropertyChanged. Your task would update the view model property (let's say PercentDone), and the UI progressbar would be bound to that property. You are not supposed to EVER have to directly interact with the UI in WPF.

XML:
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1" x:Class="MainWindow"
    Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MyViewModel/>
    </Window.DataContext>

    <Grid>
        <ProgressBar  Minimum="0" Maximum="100" Value="{Binding PercentDone}" />
    </Grid>
</Window>

VB.NET:
Public Class MyViewModel
    Implements INotifyPropertyChanged

    Private _percentDone As Decimal = 0
    Public Property PercentDone As Decimal
        Get
            Return _percentDone
        End Get
        Set(value As Decimal)
            If value > 100 Then value = 0
            _percentDone = value
            NotifyPropertyChanged()
        End Set
    End Property

    Public Sub New()
        Task.Factory.StartNew(Sub() LoopPercent())
    End Sub

    Private Sub LoopPercent()
        While True
            PercentDone += 1
            Thread.Sleep(500)
        End While
    End Sub

#Region "INotifyPropertyChanged"
    Public Sub NotifyPropertyChanged(<CallerMemberName> Optional ByVal propName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
    End Sub

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
#End Region

End Class
 
Last edited:

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
In WPF, the correct way to do this is to create a view model with a property that has change notification enabled through INotifyPropertyChanged. Your task would update the view model property (let's say PercentDone), and the UI progressbar would be bound to that property. You are not supposed to EVER have to directly interact with the UI in WPF.


Yes, I realize that, I actually created a separate window for the Progress Bar, but the issue occurs because I'm still getting the result from inside the secondary thread which blocks it from being able to use it...

It's the first foray into multithreading so I know I am asking a lot of dumb questions, but please bear with me. I typically go through this stage when I'm learning a new concept and after struggling with it for a while I all of a sudden have a light bulb go off in my head and then I quickly figure it out and learn various other ways to do it...

I know I'm frustrating some of the more experienced users here with some of this, and I apologize for it, but it's the way I learn...I ask lot's of questions and then I keep working at it until I get it working properly. Then I go back and review things I could do to make it more efficient or better, and then I end up having a good understanding of it...
 

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
Look at the example I added... The view model is the ONLY part of code that connects to your XAML. You can have as many threads as you like, as long as the property you defined in your view model is visible from your XAML, it will work.

I will add that you should probably start multithreading without WPF, to really understand how to do it manually. WPF and its awesome databinding hides much of it away. If you were in WinForm, then your issue would matter, and you would need to test your UI controls through .InvokeRequired and then invoke the form to change the control value. In WPF, you just tell the form control where it should go find its value, relative to the data context, and then you just make sure that value is updated. You don't have to push the value into the control, neither should you be trying to. That's the biggest thing to get your head around in WPF. Once you understand that, you get the basics of MVVM. The view is bound to the view model, and the view model exposes data from the model.
 
Last edited:

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Look at the example I added... The view model is the ONLY part of code that connects to your XAML. You can have as many threads as you like, as long as the property you defined in your view model is visible from your XAML, it will work.

I will add that you should probably start multithreading without WPF, to really understand how to do it manually. WPF and its awesome databinding hides much of it away. If you were in WinForm, then your issue would matter, and you would need to test your UI controls through .InvokeRequired and then invoke the form to change the control value. In WPF, you just tell the form control where it should go find its value, relative to the data context, and then you just make sure that value is updated. You don't have to push the value into the control, neither should you be trying to. That's the biggest thing to get your head around in WPF. Once you understand that, you get the basics of MVVM. The view is bound to the view model, and the view model exposes data from the model.

Awesome! Thanks so much...

Now my question is going to be how do I get the value out of the secondary thread, because I need the value from there to know what percent the progress bar should display... Here is the Secondary thread where I need to take the value of x and NumPlayers to calculate a percent...ie x is the number created so far out of NumPlayers total... I have to pass this to the ViewModel somehow for it to know the value of the ProgressBar so it can display it properly...Can I just call the ViewModel property directly from there? Or do I need to use Dispatcher.Invoke?




VB.NET:
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()

       ---->'Call Worker UpdateProgressBar(x, NumPlayers)

            
        Next i

    End Sub

Also....Since this is in a loop, should the sub I'm creating be the entire loop? Otherwise am I not wasting significant processing creating a new Task each time it goes through the loop?
Something Like This?

VB.NET:
Private Sub GenNewPlayersASync()
     Dim x As Integer = 0
      'Generate the Players on an Async Thread
     Task.Factory.StartNew(Sub()
                           For i As Integer = 1 To NumPlayers
                           x = i
                           CollegePlayers.GenDraftPlayers(x, MyDraft, DraftDT, DraftClass, PosCount)).Wait()

                     ---->'Call Worker UpdateProgressBar(x, NumPlayers)

            
                           Next i
                           End Sub)

End Sub

And it doesn't help that in comparison to C#, VB.net is a nightmare for this type of stuff, lol
 
Last edited:

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
Well... In what class is GenNewPlayersASync? Is it in your viewmodel (it shouldn't be...)?

You should NOT have to call updateprogressbar. Just generate the players and stick them into a property. That is it. Your actual data should have no connection AT ALL to your UI. If you need to SHOW some data, then you show it through properties of your view model. Assuming the percentage done is relative to the number of players processed, that means you know in advance how many players there will be. So that also needs to be accessible from the ViewModel. So the view model can take the expected total number of players and do Players.Count / TotalPlayerCount, and then bind the form to that property.

I don't really see what happens in the rest of your code, so it's kind of hard to answer. I think you need to start with a simpler example and work from there. Starting from existing code will just give you the impression what is already there is good code, which it might not be. To work MVVM properly, you MUST have a good understanding of what is a good object oriented architecture. If you start with goofy procedural code and try to migrate to MVVM you will have issues.
 

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Well... In what class is GenNewPlayersASync? Is it in your viewmodel (it shouldn't be...)?

You should NOT have to call updateprogressbar. Just generate the players and stick them into a property. That is it. Your actual data should have no connection AT ALL to your UI. If you need to SHOW some data, then you show it through properties of your view model. Assuming the percentage done is relative to the number of players processed, that means you know in advance how many players there will be. So that also needs to be accessible from the ViewModel. So the view model can take the expected total number of players and do Players.Count / TotalPlayerCount, and then bind the form to that property.

I don't really see what happens in the rest of your code, so it's kind of hard to answer. I think you need to start with a simpler example and work from there. Starting from existing code will just give you the impression what is already there is good code, which it might not be. To work MVVM properly, you MUST have a good understanding of what is a good object oriented architecture. If you start with goofy procedural code and try to migrate to MVVM you will have issues.

This was designed to be more of a quick and dirty type project to give the testers something to work with so they could test out the results from the player generation process and give me feedback, so I suppose I am trying to reverse engineer it to work in a way that I shouldn't be...I use MVVM in the main project and have it working pretty well. Figure I would test it out in this first because if I screw something up its not that big of a deal since its a lot less code, lol...
The GeneratePlayersAsync is in the GeneratePlayers Class, not the viewmodel...

OK, so basically I create a PlayerCount property in the viewmodel and assign it the value and then it kind of bypasses the issues with calling it inside of a UI thread? The number of players is entered in a textbox by the user.
 
Last edited:

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Yup! That's really all there is to it.

Well, its still not working. Both UI and the new ProgressBar Window are unresponsive.

The ProgressBar task is running, the PlayerCount and PercentDone is updating properly, its just not displaying the updated values on the ProgressBar.

Here is the code in the GeneratePlayers Class---moving the task to encompass the entire block of code caused it to go much quicker, 45 seconds when it was creating a new task every iteration to 16-18 seconds now, but it still isn't working properly.

VB.NET:
 Private Sub GenNewPlayersASync()
        Dim x As Integer = 0
        'Generate the Players on an Async Thread
        Me.IsEnabled = False
        MyVM.Show()
        Dim mytask As Task = Task.Factory.StartNew(Sub()
                                                       For i As Integer = 1 To MyVM.TotalPlayers
                                                           x = i
                                                           CollegePlayers.GenDraftPlayers(x, MyDraft, DraftDT, DraftClass, PosCount)
                                                           MyVM.PlayerCount = x
                                                       Next i
                                                   End Sub)
        mytask.Wait()
        MyVM.Close()
        Me.IsEnabled = True
    End Sub

Here is the VM code:

VB.NET:
Public Sub New()


        ' This call is required by the designer.
        InitializeComponent()
        Dim mytask As Task = Task.Factory.StartNew(Sub() LoopPercent())
        ' Add any initialization after the InitializeComponent() call.
        'mytask.Wait()


End Sub


Public Property ProgressValue As Integer
        Get
            Return _ProgressValue
        End Get
        Set(value As Integer)
            _ProgressValue = value
            NotifyPropertyChanged("ProgressValue")
        End Set
    End Property


    Public Property PlayerCount As Integer
        Get
            Return _PlayerCount
        End Get
        Set(value As Integer)
            _PlayerCount = value
            NotifyPropertyChanged("PlayerCount")
        End Set
    End Property


    Public Property TotalPlayers As Integer
        Get
            Return _TotalPlayers
        End Get
        Set(value As Integer)
            _TotalPlayers = value
            NotifyPropertyChanged("TotalPlayers")
        End Set
    End Property


    Public Property PercentDone As Decimal
        Get
            Return _PercentDone
        End Get
        Set(value As Decimal)
            _PercentDone = value
            NotifyPropertyChanged("PercentDone")
        End Set
    End Property

Private Sub LoopPercent()
        'Dim mytask As Task(Of Decimal)
        While PercentDone < 100
            If PlayerCount > 0 Then
                PercentDone = CInt(CDbl(PlayerCount / TotalPlayers) * 100)
                ProgressText = PercentDone.ToString()
                Thread.Sleep(500)
            End If
        End While
    End Sub

And the XAML Code:

VB.NET:
<Window x:Class="ProgressBarDialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WPFGeneration"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Generating Players..."
        Width="300"
        Height="115"
        WindowStartupLocation="CenterOwner"
        WindowStyle="ToolWindow"
        mc:Ignorable="d">
    <Grid>
        <StackPanel Background="SlateGray">
            <Label x:Name="lblProgress"
                   HorizontalAlignment="Center"
                   Content="{Binding ProgressText}"
                   FontSize="16" />
            <ProgressBar x:Name="progress"
                         Height="25"
                         Background="DodgerBlue"
                         IsIndeterminate="False"
                         Value="{Binding PerecentDone}" />
            <StackPanel Margin="0,5,3,0"
                        HorizontalAlignment="Right"
                        Orientation="Horizontal">
                <Button x:Name="btnCancel" Click="btnCancel_Click">Cancel</Button>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

Do I need to "Wait" the task in the VM?
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,717
Location
Sydney, Australia
Programming Experience
10+
Having glanced at some of Herman's posts, I'm wondering whether I may have misled you. I have used WPF some but not a lot and not in a multi-threaded environment. If modifying a bound property on a secondary thread does not create any issues then I'm sorry for the misinformation. Regardless, I'll leave you in Herman's hands as he seems to know more about WPF than I do.
 

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Having glanced at some of Herman's posts, I'm wondering whether I may have misled you. I have used WPF some but not a lot and not in a multi-threaded environment. If modifying a bound property on a secondary thread does not create any issues then I'm sorry for the misinformation. Regardless, I'll leave you in Herman's hands as he seems to know more about WPF than I do.

Its no problem, I am always wanting to learn everything I can, so I will keep this for use in a Winforms or non-WPF environment...

I'm still confused as to why the UI thread is unresponsive even as I have a separate Task running for it...
 

Herman

Well-known member
Joined
Oct 18, 2011
Messages
882
Location
Montreal, QC, CA
Programming Experience
10+
Ok, a couple of issues here...

1- Why is your viewmodel calling InitializeComponents? The ViewModel is NOT the form designer. The viewmodel is a separate class. Create a new class and call it MyViewModel.vb or something. This is what should be in it, and NOTHING ELSE:

Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public Class MyViewModel
    Implements INotifyPropertyChanged

    Private _PlayerCount As Integer = 0
    Private _TotalPlayers As Integer = 0

    ' This property should calculate itself, no point in writing to it, so make it read only.
    Public ReadOnly Property PercentDone As Integer
        Get
            ' Return the calculated percent every time.
            Return CInt((PlayerCount / TotalPlayers) * 100)
        End Get
    End Property

    ' This property sets the current count as set in the GenNewPlayersASync
    ' Note that having your external class dig around properties in your view model
    ' is not ideal, but it should work.
    Public Property PlayerCount As Integer
        Get
            Return _PlayerCount
        End Get
        Set(value As Integer)
            _PlayerCount = value
            NotifyPropertyChanged("PlayerCount")
            ' Every time you change the count, the percentage changes too, so notify the form.
            NotifyPropertyChanged("PercentDone")
        End Set
    End Property

    Public Property TotalPlayers As Integer
        Get
            Return _TotalPlayers
        End Get
        Set(value As Integer)
            _TotalPlayers = value
            NotifyPropertyChanged("TotalPlayers")
        End Set
    End Property

#Region "INotifyPropertyChanged"
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public Sub NotifyPropertyChanged(<CallerMemberName> Optional ByVal propname As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname))
    End Sub
#End Region

End Class


2- Same goes for the MyVM.Show() call in your GeneratePlayers class. If MyVM is the form, then call it MyForm (MyForm.xaml and MyForm.xaml.vb). In the form .VB file, all you should have is glue logic for the UI, event handlers, etc.. So that call needs to go, or change. In your loop you use its reference to set your properties. That reference should be to an instance of your viewmodel. So declare your view model somewhere appropriate. Private myVM = New MyViewModel().

3- I don't see where in your XAML you connect the form to the view model. Either add the datacontext in the XAML like I did above, or in the code behind set the form's DataContext property to your instance of your view model. Form1.DataContext = myVM.

4- Normally, the code in your GeneratePlayers class should not even be aware that a viewmodel exists, and vice versa. The form (XAML) connects to the view model, and the view model connects to the model (model is a generic term for "the program"). The model should NOT depend on the view model. So there are still some architectural issues that should be fixed.

One way to separate your GeneratePlayers class and view model is through events. Instead of writing directly to your view model from the GeneratePlayers class, have your async method raise an event every time a player is generated.
 
Last edited:

CodeLiftsleep

Member
Joined
Mar 11, 2016
Messages
20
Programming Experience
3-5
Ok, a couple of issues here...

1- Why is your viewmodel calling InitializeComponents? The ViewModel is NOT the form designer. The viewmodel is a separate class. Create a new class and call it MyViewModel.vb or something. This is what should be in it, and NOTHING ELSE:

Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public Class MyViewModel
    Implements INotifyPropertyChanged

    Private _PlayerCount As Integer = 0
    Private _TotalPlayers As Integer = 0

    ' This property should calculate itself, no point in writing to it, so make it read only.
    Public ReadOnly Property PercentDone As Integer
        Get
            ' Return the calculated percent every time.
            Return CInt((PlayerCount / TotalPlayers) * 100)
        End Get
    End Property

    ' This property sets the current count as set in the GenNewPlayersASync
    ' Note that having your external class dig around properties in your view model
    ' is not ideal, but it should work.
    Public Property PlayerCount As Integer
        Get
            Return _PlayerCount
        End Get
        Set(value As Integer)
            _PlayerCount = value
            NotifyPropertyChanged("PlayerCount")
            ' Every time you change the count, the percentage changes too, so notify the form.
            NotifyPropertyChanged("PercentDone")
        End Set
    End Property

    Public Property TotalPlayers As Integer
        Get
            Return _TotalPlayers
        End Get
        Set(value As Integer)
            _TotalPlayers = value
            NotifyPropertyChanged("TotalPlayers")
        End Set
    End Property

#Region "INotifyPropertyChanged"
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public Sub NotifyPropertyChanged(<CallerMemberName> Optional ByVal propname As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propname))
    End Sub
#End Region

End Class


2- Same goes for the MyVM.Show() call in your GeneratePlayers class. If MyVM is the form, then call it MyForm (MyForm.xaml and MyForm.xaml.vb). In the form .VB file, all you should have is glue logic for the UI, event handlers, etc.. So that call needs to go, or change. In your loop you use its reference to set your properties. That reference should be to an instance of your viewmodel. So declare your view model somewhere appropriate. Private myVM = New MyViewModel().

3- I don't see where in your XAML you connect the form to the view model. Either add the datacontext in the XAML like I did above, or in the code behind set the form's DataContext property to your instance of your view model. Form1.DataContext = myVM.

4- Normally, the code in your GeneratePlayers class should not even be aware that a viewmodel exists, and vice versa. The form (XAML) connects to the view model, and the view model connects to the model (model is a generic term for "the program"). The model should NOT depend on the view model. So there are still some architectural issues that should be fixed.

One way to separate your GeneratePlayers class and view model is through events. Instead of writing directly to your view model from the GeneratePlayers class, have your async method raise an event every time a player is generated.

Created a new ViewModel and have it exactly as you do, bound using <Window.DataContext>. Previously had it set in the code behind which I didn't show in my example, sorry for the confusion. Still not working. I did get the Progress Bar Window to be responsive by Calling the Button Click Event Async and then changing

VB.NET:
TimeIt(GenSubAsync()) to Await Task.Run(Sub() TimeIt(GenSubAsync())
.

However, even though I can now drag the Progress Bar Window around on the screen after it pops up when I enable it as it runs, the values still won't update even though they are bound to the Properties.
Property values are updating properly, just not showing in their respective places.

I'm at a loss right now...this is supposed to something really simple and I've literally been working at this a day and a half and its still not working...beyond frustrating.
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,603
Location
Norway
Programming Experience
10+
I'm not really into WPF either, but I noticed in your post 13 there was spelling mistake in your binding: {Binding PerecentDone}
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,717
Location
Sydney, Australia
Programming Experience
10+
It's correct in the actual code, its just a typo in here...wish that was the problem tho...lol

Why aren't you copying and pasting your actual code?
 
Top Bottom