Reputation: 1616
I'm trying to update a progress bar from two separate tasks at the same time. I've tried it a number of ways but haven't had much success. See below full code example. In WPF, add a progressbar, bind Maximum to TextMax and Value to TextProgress and you will notice that the progress bar only fills to about half way.
NOTE: This is not my actual solution, just a sample of what I am doing that shows the issue that I threw together, please ignore code style/pattern issues.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace DelegateTesting
{
public partial class MainWindow : Window
{
ResultsItemViewModel _ViewModel = new ResultsItemViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _ViewModel;
TextProcessing();
}
private static void Method1(
Action<int> reportProgress)
{
var progress = 0;
for(int i = 0;i<100;i++)
{
//Thread.Sleep(200);
reportProgress?.Invoke(++progress);
}
}
private static void Method2(
Action<int> reportProgress)
{
var progress = 0;
for (int i = 0; i < 100; i++)
{
//Thread.Sleep(200);
reportProgress?.Invoke(++progress);
}
}
private async Task TextProcessing()
{
_ViewModel.TextMax += 100;
_ViewModel.TextMax += 100;
var dispatcher = Application.Current.Dispatcher;
var reportProgress = dispatcher.MakeInvoker<int>(p => _ViewModel.TextProgress = p);
await Task.WhenAll(
Task.Run(() => Method1(reportProgress)),
Task.Run(() => Method2(reportProgress)));
}
}
public static class DispatcherHelper
{
public static Action<T> MakeInvoker<T>(
this Dispatcher dispatcher,
Action<T> action,
DispatcherPriority priority = DispatcherPriority.Normal)
{
return o => dispatcher.BeginInvoke(priority, action, o);
}
}
public class ResultsItemViewModel : INotifyPropertyChanged
{
int _textProgress, _textMax;
public int TextProgress
{
get => _textProgress;
set
{
_textProgress = value;
NotifyPropertyChanged();
}
}
public int TextMax
{
get => _textMax;
set
{
_textMax = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Upvotes: 0
Views: 826
Reputation: 3915
You are not awaiting TextProcessing
. You cannot just call await
in the middle. You can either go asynchronous all the way or not at all.
Also, doing work in a constructor is not a good practice.
In order for this to work you have to allow WPF
to handle async calls for you. I assume you want to start an action when someone does something, e.g.:
protected async void Button_OnClick(Object sender, EventArgs e)
{
await TextProcessing();
}
You could always bind it to an event that starts a window or something like that.
To be honest your code is very unclear to me, so this might help you understand what you actually need:
int _textProgress, _textMax;
public int TextProgress
{
get => _textProgress;
set
{
_textProgress = value;
NotifyPropertyChanged();
}
}
public int TextMax
{
get => _textMax;
set
{
_textMax = value;
NotifyPropertyChanged();
}
}
protected async void Button_OnClick(Object sender, EventArgs e)
{
TextMax = 0;
var t1 = Task.Run(() => {
TextProgress += 50;
});
var t2 = Task.Run(() => {
TextProgress += 50;
});
await Task.WhenAll(t1, t2);
}
And in your view you should have some kind of button with command for Button_OnClick
and progress bar:
<ProgressBar Maximum="100" Height="50" Value="{Binding TextProgress}"></ProgressBar>
One more thing. It looks like you have a ViewModel, but you are doing work in a view. You should move that logic to your ViewModel.
Upvotes: 1