Async pass multiple parameters into ReportProgress method - c#

How can I pass multiple parameters into the ReportProgress method?
I was following this guide: MSDN to create a progressbar. My code looks like this.
MainWindow.xaml
public User User { get; set; }
public MainWindow()
{
InitializeComponent();
this.User = new User();
this.DataContext = User;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
var progressIndicator = new Progress<int>(ReportProgress); //here is the error
await this.User.ReadUsers(progressIndicator, this.User)
}
void ReportProgress(int value, User pUser)
{
this.User.Val = value;
}
User.xaml
public async Task<bool> ReadUsers(IProgress<int> pProgress, User pUser)
{
for (int i = 1; i < 11; i++)
{
await Task.Delay(500);
pProgress.Report(i, pUser);
}
return true;
}
As you may see, I was trying to add simply a new parameter (User pUser) to the ReportProgress method. Now I get an error inside the Button_Click method (line is marked).
Argument 1: cannot convert from method group into System.Action
best overloaded method match for 'System.Progress.Progress(System.Action)'-method has some invalid arguments
no overload for Report-Method takes 2 arguments
I was trying it like this, because in my real application I will have an ObersableCollection<User>. Is there maybe a better way I should go?

You should pass a second parameter manually, because 'Progress' constructor takes only actions with one argument. Try this:
new Progress<int>(i => ReportProgress(i, this.User));
And remove second argument from 'pProgress.Report' method:
pProgress.Report(i);

I would prefer creating a message and pass multiple values to Report Progress
public class RMssg
{
public int ProgressIndicator { get; set; }
public User userInstance { get; set; }
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
var progressIndicator = new Progress<RMssg>(r => ReportProgress(r));
await this.User.ReadUsers(progressIndicator, this.User);
}
void ReportProgress(RMssg rMssg)
{
this.User.Val = rMssg.ProgressIndicator;
var user = rMssg.userInstance;
}
public async Task<bool> ReadUsers(IProgress<RMssg> pProgress, User pUser)
{
for (int i = 1; i < 11; i++)
{
await Task.Delay(500);
var rMssg = new RMssg() { ProgressIndicator = i, userInstance = pUser };
pProgress.Report(rMssg);
}
return true;
}

Related

Updating Winforms Label with Timer and Thread, stock app

Gist of it has probably been asked before, but I'm completely lost so I'm looking for some personal guidance. Been trying to make a stock tracker app for funsies using WinForms and the Yahoo API. Trying to get it so you can input a tracker symbol and it will make a new Label that will keep updating itself every so often. However, it keeps giving me error messages about "Cross-thread operation not valid". I've tried to do some googling, but yeah, completely lost. Here is most of the code, hope you guys can make some sense of it.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using YahooFinanceApi;
namespace stockpoging4
{
public partial class Form1 : Form
{
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
}
}
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol + " $" + price;
}
catch
{
return "404";
}
}
public async void do_Things(string result)
{
string price;
Label label = null;
if (label == null)
{
price = await getStockPrices(result);
label = new Label() { Name = result, Text = result + " $" + price };
flowLayoutPanel2.Controls.Add(label);
}
else
{
Thread testThread = new Thread(async delegate ()
{
uiLockingTask();
price = await getStockPrices(result);
label.Text = result + " $" + price;
label.Update();
});
}
System.Timers.Timer timer = new System.Timers.Timer(10000);
timer.Start();
timer.Elapsed += do_Things(results);
}
private void uiLockingTask() {
Thread.Sleep(5000);
}
}
}
Let me point out several things in your implementation.
You subscribe to timer.Elapsed after timer.Start that might be invalid in case of a short-timer interval
The event handler is called in background that's why you continuously get "Cross-thread operation not valid". UI components should be dispatched correctly from background threads, for example, by calling flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label))); and label.BeginInvoke(new Action(label.Update)). This change already would fix your exception.
Despite the fact that I would implement this functionality in a different way, here I post slightly changed code that just does exactly what you need with some tweaks.
public partial class Form1 : Form
{
Task _runningTask;
CancellationTokenSource _cancellationToken;
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
_cancellationToken = new CancellationTokenSource();
_runningTask = StartTimer(() => do_Things(result), _cancellationToken);
}
}
}
private void onCancelClick()
{
_cancellationToken.Cancel();
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol + " $" + price;
}
catch
{
return "404";
}
}
private async Task StartTimer(Action action, CancellationTokenSource cancellationTokenSource)
{
try
{
while (!cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(1000, cancellationTokenSource.Token);
action();
}
}
catch (OperationCanceledException) { }
}
public async void do_Things(string result)
{
var price = await getStockPrices(result);
var label = new Label() { Name = result, Text = result + " $" + price };
flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label)));
}
}
A much easier way is using async these days.
Here is a class which triggers an Action every interval:
public class UITimer : IDisposable
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// use a private function which returns a task
private async Task Innerloop(TimeSpan interval, Action<UITimer> action)
{
try
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(interval, _cancellationTokenSource.Token);
action(this);
}
}
catch (OperationCanceledException) { }
}
// the constructor calls the private StartTimer, (the first part will run synchroniously, until the away delay)
public UITimer(TimeSpan interval, Action<UITimer> action) =>
_ = Innerloop(interval, action);
// make sure the while loop will stop.
public void Dispose() =>
_cancellationTokenSource?.Cancel();
}
If you work with dotnet 3.0 or higher, you can use the IAsyncDisposable. With this you're able to await the DisposeAsync method, so you can await the _timerTask to be finished.
And I created a new form with this as code behind:
public partial class Form1 : Form
{
private readonly UITimer _uiTimer;
private int _counter;
public Form1()
{
InitializeComponent();
// setup the time and pass the callback action
_uiTimer = new UITimer(TimeSpan.FromSeconds(1), Update);
}
// the orgin timer is passed as parameter.
private void Update(UITimer timer)
{
// do your thing on the UI thread.
_counter++;
label1.Text= _counter.ToString();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// make sure the time (whileloop) is stopped.
_uiTimer.Dispose();
}
}
The advantage is, that the callback runs on the UI thread but doesn't block it. The await Task.Delay(..) is using a Timer in the background, but posts the rest of the method/statemachine on the UI thread (because the UI thread has a SynchronizaionContext)
Easy but does the trick ;-)

Singleton Async Loading List Property

I have a winforms application with a singleton class that contains a property (list) that is the datasource for a search grid. Filling this property takes a significant amount of time (>1 minute) so, I want to start filling this property asynchronously when the user launches the program.
The main form has a button to launch another search form. If, when the user launches the search if the datasource is ready then, no problem. However, if the datasource is still filling, the user sees a wait cursor and the search grid should fill as soon as the datasource has finished populating.
To do this, I created a method that fires after the asynchronous method completes and then the grid is bound to the datasource.
Everything appears to work correctly, the event fires, and then I try to bind the list to the grid and nothing happens... Debugging halts and I never hit the next line of code (see comments in FrmSearch.cs).
Any ideas relating to what is going wrong or general code improvements would be very much appeciated, thanks!
Program.cs
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Task.Run(async() => { Singleton.DogsList = await Dogs.FindAllAsync(); });
Application.Run(new FrmMain());
}
}
Singleton.cs
public static class Singleton
{
public static event DogsListHandler DogsListLoaded;
public delegate void DogsListHandler(object sender, EventArgs e);
public static BindingList<Dogs> DogsList
{
get
{
if (dogsList == null)
{
Task.Run(async () =>
{
dogsList = await Dogs.FindAllAsync();
notifyListLoaded();
});
}
return dogsList;
}
set { dogsList = value; }
}
private static BindingList<Dogs> dogsList;
private static void notifyListLoaded()
{
if (DogsListLoaded != null) { DogsListLoaded(null, EventArgs.Empty); }
}
}
FrmSearch.cs
public partial class FrmSearch : Form //launched using the .Show() method from a button on the main form
{
public FrmSearch()
{
InitializeComponent();
}
private void FrmSearch_Load(object sender, EventArgs e)
{
Singleton.DogsListLoaded += new Singleton.DogsListHandler(Dogs_ListLoaded);
Cursor = Cursors.WaitCursor;
if (Singleton.DogsList != null)
{
grid.DataSource = Singleton.DogsList;
Cursor = Cursors.Default;
}
else { Cursor = Cursors.WaitCursor; }
}
public void Dogs_ListLoaded(object sender, EventArgs e)
{
grid.DataSource = Singleton.DogsList; //freezes here
Cursor = Cursors.Default; //this line never gets hit
}
}
Dogs.cs (will pull from db normally but just doing some iteration for the sample)
public class Dogs
{
public string Name { get; set; }
public string Breed { get; set; }
public int Age { get; set; }
public string Summary { get { return string.Format("Name: {0} / Breed: {1} / Age: {2}", Name, Breed, Age.ToString()); } }
public static async Task<BindingList<Dogs>> FindAllAsync()
{
BindingList<Dogs> dl = new BindingList<Dogs>();
await Task.Run(() =>
{
int i = 0;
while (i <= 999999)
{
dl.Add(new Dogs() { Name = "River" + i.ToString(), Breed = "Border Collie", Age = 3 });
dl.Add(new Dogs() { Name = "Jack" + i.ToString(), Breed = "Labrador", Age = 2 });
dl.Add(new Dogs() { Name = "Emma" + i.ToString(), Breed = "Beagle", Age = 7 });
i++;
}
});
return dl;
}
}
You should retrieve the list as you would retrieve all other lists getting from anywhere:
Call a async method and await that.
Easy to handle
public static class SomeLookups
{
private static object _lock = new object( );
private static Task<IList<string>> _foosAsync;
public static Task<IList<string>> GetFoosAsync()
{
lock ( _lock )
{
return _foosAsync = _foosAsync ?? PrivateGetFoosAsync( );
}
}
private static async Task<IList<string>> PrivateGetFoosAsync()
{
var list = new List<string>( );
for ( int i = 0; i < 20; i++ )
{
await Task.Delay( 200 ).ConfigureAwait( false );
list.Add( "item " + i );
}
return list.AsReadOnly( );
}
}
In the consuming class
private async Task RetrieveAllData()
{
IsBusy = true;
MyFooCollection = await SomeLookups.GetFoosAsync();
IsBusy = false;
}
Once the task is finished you can still await it. But because it is finished you get the result without any delay.

Get Value from async Method?

I want to have the returned value from the send Operation which is a string and use it in the public MainPage() section. I tried this way, bot doesn´t work. Any idea how to get this value out of the send() Method?
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
string stringData = "";
stringData = "aktion=getBenutzer&name=" + Login.getBenutzername();
//send(stringData);
//textBlock1.Text = getContentOfSendOperation();
button.Foreground = Einstellungen.getBrush();
button1.Foreground = Einstellungen.getBrush();
button2.Foreground = Einstellungen.getBrush();
stringData = "aktion=getMitarbeiterListe";
//string mitarbeiterListe = getContentOfSendOperation();
var task = send(stringData);
string mitarbeiterListe = task.Result;
textBlock1.Text = mitarbeiterListe;
//comboBox.Items.Add()
}
public Frame globalFrame { get { return _mainFrame; } }
private void button_Click(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(typeof(Datenbank));
}
public async Task<String> send(string stringData)
{
System.Net.Http.HttpClient oHttpClient = new System.Net.Http.HttpClient();
Uri uri = new Uri("*********");
oHttpClient.DefaultRequestHeaders.UserAgent.ParseAdd("moralsKite/DesktopTestClient");
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Post, uri);
request.Content = new StringContent(stringData, Encoding.UTF8, "application/x-www-form-urlencoded");
var reponse = await oHttpClient.SendAsync(request);
if (reponse.IsSuccessStatusCode)
{
return "??";
//return await reponse.Content.ReadAsStringAsync();
}
return "!!";
}
private void button1_Click_1(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(typeof(Einstellungen));
}
private void button2_Click_1(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(typeof(Ueber));
}
}
}
You cannot run asynchronous operation and await it in constructor. In your example the task can run little longer (varying on signal and so on), the constructor of a class should be fast. Better subscribe to one of the page's events like Loaded and put your work there:
public MainPage()
{
this.InitializeComponent();
// rest of code
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
// Following Peter Torr's comment - Loaded event can be fired multiple times
// for example once you navigate back to the page
this.Loaded -= MainPage_Loaded; // deregister from event if you want to run it once
stringData = "aktion=getMitarbeiterListe";
// in your send method uncomment the line:
// return await reponse.Content.ReadAsStringAsync();
// then it will return asynchronously the content as string and can be used like this:
string mitarbeiterListe = await send(stringData);
}
Events can be async then there shouldn't be problems, you may also implement an information for user that something is loading in the background.
Instead of :
var task = send(stringData);
string mitarbeiterListe = task.Result;
use
string mitarbeiterListe = await send(stringData);
Make the method where you call send data async.
I do not recommend to call gathering of data in constructor.

Best practice on using async / await

Say I have the following class definitions:
public class Calculator
{
public CalculatorResult Calculate()
{
return LongRunningCalculation();
}
private CalculatorResult LongRunningCalculation()
{
return new CalculatorResult(0.00);
}
}
public class ClassThatUsesACalculator
{
private readonly Calculator calculator;
public ClassThatUsesACalculator()
{
this.calculator = new Calculator();
}
public void DoWork()
{
for (int i = 0; i < 10; i++)
{
var result = calculator.Calculate();
DoSomethingWithCalculationResult(result);
DoLightWork();
OnProgressChanged();
}
}
}
public partial class Form : Form
{
public Form()
{
InitializeComponent();
}
private void Method(object sender, EventArgs e)
{
DoWork();
}
private void DoWork()
{
var calculator = new ClassThatUsesACalculator();
calculator.ProgressChanged += (s, e) =>
{
// Update progressbar
};
calculator.DoWork();
}
}
If I would want to do the work done in DoWork(), on the form, asynchronously I could add a method (GetCalculationTask) that returns a task using Task.Run() and add a async eventhandler i.e. For a button (MethodOne).
Please correct me if I'm wrong, but it seems to me that this would be the only option when the ClassThatUsesACalculator and Calculator classes reside in a library I don't own.
private Task GetCalculationTask(IProgress<CalculatorProgress> progress)
{
var calculator = new ClassThatUsesACalculator();
calculator.ProgressChanged += (s, e) =>
{
progress.Report(new CalculatorProgress(0));
};
return Task.Run(() =>
{
calculator.DoWork();
});
}
private async void MethodOne(object sender, EventArgs e)
{
IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress> (UpdateProgressBar);
await GetCalculationTask(progress);
}
In the case I do own the library I think there are two more options, one of which very much like the first one. Probably due to the lack of my own understanding.
Create a method on on ClassThatUsesACalculator that encapsulates the DoWork() method and then call that from an asynchronous method on the form.
or,
Encapsulate the LongRunningCalculation() on the Calculator class with a Task.Run().
public Task<CalculatorResult> CalculateAsync()
{
return Task.Run(() =>
{
return LongRunningCalculation();
});
}
Create an async method on ClassThatUsesACalculator the calls that awaits the newly created method.
public async Task DoWorkAsync()
{
for (int i = 0; i < 10; i++)
{
var result = await calculator.CalculateAsync();
DoSomethingWithCalculationResult(result);
DoLightWork();
OnProgressChanged();
}
}
Create an asynchronous method on the form (MethodThree)
private async void MethodThree(object sender, EventArgs e)
{
IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar);
var calculator = new ClassThatUsesACalculator();
calculator.ProgressChanged += (s, args) =>
{
progress.Report(new CalculatorProgress(0));
};
await calculator.DoWorkAsync();
}
Now, in my opinion the last option would be the best as I would remain more control. But maybe I'm way off and would like someone's opinion or pointers on this as I can only find explanations on how to consume async, but never really how to build methods for others to consume.
As a general rule, push any Task.Run usage as far up the call stack as possible.
What you want to avoid is having a method with an asynchronous signature that is implemented using Task.Run in a reusable component. That's a lying API. I have a blog post on the subject that goes into greater detail.
If you control the classes in question, I recommend using IProgress<T> instead of events for progress updates. IProgress<T> works just fine with synchronous code as well as asynchronous:
public void DoWork(IProgress<CalculatorProgress> progress = null)
{
for (int i = 0; i < 10; i++)
{
var result = calculator.Calculate();
DoSomethingWithCalculationResult(result);
DoLightWork();
if (progress != null)
progress.Report(new CalculatorProgress(...));
}
}
Then using it is quite straightforward:
private async void MethodTwo(object sender, EventArgs e)
{
IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar);
var calculator = new ClassThatUsesACalculator();
await Task.Run(() => calculator.DoWork(progress));
}
That keeps the Task.Run usage in the component that needs it - the UI layer - and out of the business logic.

Notify multiple background workers with a property change C#

I have a global variable called:
string tweet;
I run several background workers, that does nothing but wait on value change of the tweet variable. Then run a function called: ProcessTweet( object sender, MyCustomEventArgs args )
My question is what is the best way to handle the property changed event from all those background workers, and later process the results based on the tweet value and another argument passed to the ProcessTweet function.
I tried to take a look at INotifyPropertyChanged but I am not sure how to handle OnValueChange event from each background worker. Will it run the same ProcessTweet function once or each background worker will run an instance of that function?
EDIT:
private ITweet _LastTweet;
public ITweet LastTweet
{
get { return this._LastTweet; }
set
{
this._LastTweet = value;
}
}
Still not sure how to handle property change event the best way ^
And below is the rest of the code
private void bgworker_DoWork(object sender, DoWorkEventArgs e)
{
MyCustomClass myCustomClass = e.Argument as MyCustomClass;
//here I want to listen on the LastTweet Value Change event and handle it
}
List<BackgroundWorker> listOfBGWorkers = new List<BackgroundWorker>();
private BackgroundWorker CreateBackgroundWorker()
{
BackgroundWorker bgworker = new BackgroundWorker();
//add the DoWork etc..
bgworker.DoWork += new System.ComponentModel.DoWorkEventHandler(bgworker_DoWork);
return bgworker;
}
private void buttonStart_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
//Create the background workers
var bgworker = CreateBackgroundWorker();
listOfBGWorkers.Add(bgworker);
//get the MYCustomClass value;
var myCustomClass = SomeFunction();
bgworker.RunWorkerAsync(myCustomClass);
}
}
Ok - here's a small console app that demonstrates what I think you're trying to do.
It creates a 'source of tweets' in a thread.
You can subscribe to this 'source' and be notified when a new tweet 'arrives'.
You create TweetHandlers which have internal queues of tweets to process
You subscribe these TweetHandlers to the source
When a new tweet arrives, it is added to the queues of all the TweetHandlers by the event subscription
The TweetHandlers are set to run in their own Tasks. Each TweetHandler has its own delegate for performing a customizable action on a Tweet.
The code is as follows:
interface ITweet
{
object someData { get; }
}
class Tweet : ITweet
{
public object someData { get; set; }
}
class TweetSource
{
public event Action<ITweet> NewTweetEvent = delegate { };
private Task tweetSourceTask;
public void Start()
{
tweetSourceTask = new TaskFactory().StartNew(createTweetsForever);
}
private void createTweetsForever()
{
while (true)
{
Thread.Sleep(1000);
var tweet = new Tweet{ someData = Guid.NewGuid().ToString() };
NewTweetEvent(tweet);
}
}
}
class TweetHandler
{
public TweetHandler(Action<ITweet> handleTweet)
{
HandleTweet = handleTweet;
}
public void AddTweetToQueue(ITweet tweet)
{
queueOfTweets.Add(tweet);
}
public void HandleTweets(CancellationToken token)
{
ITweet item;
while (queueOfTweets.TryTake(out item, -1, token))
{
HandleTweet(item);
}
}
private BlockingCollection<ITweet> queueOfTweets = new BlockingCollection<ITweet>();
private Action<ITweet> HandleTweet;
}
class Program
{
static void Main(string[] args)
{
var handler1 = new TweetHandler(TweetHandleMethod1);
var handler2 = new TweetHandler(TweetHandleMethod2);
var source = new TweetSource();
source.NewTweetEvent += handler1.AddTweetToQueue;
source.NewTweetEvent += handler2.AddTweetToQueue;
// start up the task threads (2 of them)!
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var taskFactory = new TaskFactory(token);
var task1 = taskFactory.StartNew(() => handler1.HandleTweets(token));
var task2 = taskFactory.StartNew(() => handler2.HandleTweets(token));
// fire up the source
source.Start();
Thread.Sleep(10000);
tokenSource.Cancel();
}
static void TweetHandleMethod1(ITweet tweet)
{
Console.WriteLine("Did action 1 on tweet {0}", tweet.someData);
}
static void TweetHandleMethod2(ITweet tweet)
{
Console.WriteLine("Did action 2 on tweet {0}", tweet.someData);
}
}
The output looks like this:
Did action 2 on tweet 892dd6c1-392c-4dad-8708-ca8c6e180907
Did action 1 on tweet 892dd6c1-392c-4dad-8708-ca8c6e180907
Did action 2 on tweet 8bf97417-5511-4301-86db-3ff561d53f49
Did action 1 on tweet 8bf97417-5511-4301-86db-3ff561d53f49
Did action 2 on tweet 9c902b1f-cfab-4839-8bb0-cc21dfa301d5

Categories