Stop storyboard when BackgroundWorker finished - c#

When Method ExportStarts() is called, I want to start an animation. Then I call another method within the logic unit (Manager.StartExport()), where I do stuff using a Background Worker. When it finished, I want to go back to the View and stop the animation. How can I do that?
View.xaml.cs
if (...)
{
storyboard.Begin();
List<TaskResult> Results = manager.StartExport();
storyboard.Stop();
}
manager.StartExport()
public static List<TaskResult> StartExport()
{
bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(worker_Do);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunCompleted);
bw.RunWorkerAsync();
return Results;
}
Thanks in advance

You want stop storyboard after worker_RunCompleted yes?
Check TaskCompletionSource :)
If I understood correctly - this will be helpful.
_storyboard.Stop(); raises after setting the result of _storyboardTaskCompletionSource in RunWorkerCompleted.
private TaskCompletionSource<bool> _storyboardTaskCompletionSource;
Storyboard _storyboard = new Storyboard();
private async Task InitAsync()
{
_storyboardTaskCompletionSource = new TaskCompletionSource<bool>();
_storyboard.Begin();
StartProgress();
await _storyboardTaskCompletionSource.Task;
_storyboard.Stop();
}
public void StartProgress()
{
var bw = new BackgroundWorker();
bw.DoWork += DoWork;
bw.RunWorkerCompleted += RunWorkerCompleted;
bw.RunWorkerAsync();
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_storyboardTaskCompletionSource.SetResult(true);
}
private void DoWork(object sender, DoWorkEventArgs e)
{
//logic
}

Related

Having trouble with background worker processes in WPF application

I found a few other articles regarding using background worker which I've linked just below. I used the code examples and attempted to do this to run 3 different SQL Query's. In the code posted below when I break inside of RunBackGroundWorkerProcesses1 it does stop there and is called but method for worker_DoWork1 is never called even though it is in the code. I'm assuming that I've misunderstood this, can someone add some clarity.
Link I used for reference:
WPF Multithreading
Code:
public CallInformationMainScreen()
{
InitializeComponent();
//This is where i call the background processes
RunBackGroundWorkerProcesses1();
RunBackGroundWorkerProcesses2();
RunBackGroundWorkerProcesses3();
}
#endregion
#region Methods used to generate data for the UI
public string DisplayTotalDailyCalls()
{
DailyCallsQuery db = new DailyCallsQuery();
return db.GetNumber(SkillNumber);
}
public string DisplayTotalLastSevenCalls()
{
PrevSevenCallQuery db = new PrevSevenCallQuery();
return db.GetNumber(SkillNumber);
}
public string DisplayDailyAbandonCalls()
{
DailyAbandonQuery db = new DailyAbandonQuery();
return db.GetNumber(SkillNumber);
}
#endregion
#region Background worker processes
private void RunBackGroundWorkerProcesses1()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork1);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
System.Timers.Timer t = new System.Timers.Timer(10000); // 10 second intervals
t.Elapsed += (sender, e) =>
{
// Don't try to start the work if it's still busy with the previous run...
if (!worker.IsBusy)
worker.RunWorkerAsync();
};
}
private void RunBackGroundWorkerProcesses2()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork2);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
System.Timers.Timer t = new System.Timers.Timer(10000); // 10 second intervals
t.Elapsed += (sender, e) =>
{
// Don't try to start the work if it's still busy with the previous run...
if (!worker.IsBusy)
worker.RunWorkerAsync();
};
}
private void RunBackGroundWorkerProcesses3()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork3);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
System.Timers.Timer t = new System.Timers.Timer(10000); // 10 second intervals
t.Elapsed += (sender, e) =>
{
// Don't try to start the work if it's still busy with the previous run...
if (!worker.IsBusy)
worker.RunWorkerAsync();
};
}
private void worker_DoWork1(object sender, DoWorkEventArgs e)
{
// Whatever comes back from the lengthy process, we can put into e.Result
TotalDailyCalls = DisplayTotalDailyCalls();
e.Result = TotalDailyCalls;
}
private void worker_DoWork2(object sender, DoWorkEventArgs e)
{
// Whatever comes back from the lengthy process, we can put into e.Result
TotalDailyLast7Days = DisplayTotalLastSevenCalls();
e.Result = TotalDailyCalls;
}
private void worker_DoWork3(object sender, DoWorkEventArgs e)
{
// Whatever comes back from the lengthy process, we can put into e.Result
TotalDailyAbandon = DisplayDailyAbandonCalls();
e.Result = TotalDailyAbandon;
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
// handle the System.Exception
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// now handle the case where the operation was cancelled...
ErrorHolder = "The operation was cancelled";
}
else
{
// Finally, handle the case where the operation succeeded
ErrorHolder = e.Result.ToString();
}
}
#endregion
You don't start your timers. See Timer.Start Method ().
private void RunBackGroundWorkerProcesses1()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork1);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
System.Timers.Timer t = new System.Timers.Timer(10000); // 10 second intervals
t.Elapsed += (sender, e) =>
{
// Don't try to start the work if it's still busy with the previous run...
if (!worker.IsBusy)
worker.RunWorkerAsync();
};
t.Start(); // Start the timer
}
I'm posting this to demonstrate an easier way to do this. It's not meant to be a direct answer to the question.
If you NuGet "System.Reactive" and the associated WPF libraries you can do this:
IDisposable subscription =
new []
{
Observable.Interval(TimeSpan.FromSeconds(10.0)).Select(x => DisplayTotalDailyCalls()),
Observable.Interval(TimeSpan.FromSeconds(10.0)).Select(x => DisplayTotalLastSevenCalls()),
Observable.Interval(TimeSpan.FromSeconds(10.0)).Select(x => DisplayDailyAbandonCalls()),
}
.Merge()
.ObserveOnDispatcher()
.Subscribe(x => ErrorHolder = x, e => MessageBox.Show(e.Error.Message));
That's it. Job done. All of your code in techically one line of code.
BackgroundWorker.RunWorkerAsync() is only called when the Timer.Elapsed event is fired. Since the timer is set to 10 second intervals, the BackgroundWorker won't start for 10 seconds. You probably should call BackgroundWorker.RunWorkerAsync() after creating and initializing it so that it will start right away.

Run function after Thread has finished running

I'm trying to run a function after a thread has completed running. My thread starts when an UI button is pressed and the thread takes a while to complete.
Once it's done running I want to call a function. Here is the code I tried so far. When I try to run my code the thread never executes and the application freezes. Any suggestion on how to fix this would be helpful.
public bool StartProbe()
{
if (File.Exists(Path.Combine(ObsProbeFolder, "probePJM.exe")))
{
ThreadStart ProbeThreadStart = new ThreadStart(() =>
// right side of lambda
{
// does stuff
});
ProbeThread = new Thread(ProbeThreadStart);
ProbeThread.Priority = ThreadPriority.BelowNormal;
ProbeThread.SetApartmentState(ApartmentState.STA);
ProbeThread.Start();
}
else
{
return false;
}
// waiting for thread to finish
ProbeThread.Join();
// run a function
loadData();
return true;
}
I would use a BackgroundWorker:
Worker = new BackgroundWorker();
Worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
Worker.DoWork += Worker_DoWork;
Worker.RunWorkerAsync(new BackgroundArguments()
{
// arguments
});
Work on alternate thread:
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
// do stuff
}
Return to UI thread:
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// load data or whatever on UI thread
}

Show a form while BackgroundWorker is running

I want to display a "loading form" (a form with some text message plus a progressBar with style set to marquee) while the BackgroundWorker's job isn't done. When the BackgroundWorker is done, the loading form must be closed automatically. Although I do use a BackgroundWorker, the main thread should wait until it's done. I was able to do that using a AutoResetEvent but I noticied that as it does block the main thread, the form loading's progressBar is freezed too.
My question is: How can I show that form without freeze it while runing a process in background and wait for it finish? I hope it's clear.
Here's my current code:
BackgroundWorker bw = new BackgroundWorker();
AutoResetEvent resetEvent = new AutoResetEvent(false);
//a windows form with a progressBar and a label
loadingOperation loadingForm = new loadingOperation(statusMsg);
//that form has a progressBar that's freezed. I want to make
// it not freezed.
loadingForm.Show();
bw.DoWork += (sender, e) =>
{
try
{
if (!e.Cancel)
{
//do something
}
}
finally
{
resetEvent.Set();
}
};
bw.RunWorkerAsync();
resetEvent.WaitOne();
loadingForm.Close();
MessageBox.Show("we are done!");
Connect your BackgroundWorker's RunWorkerCompleted to a callback that will close the form like so:
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
loadingForm.Close();
MessageBox.Show("we are done!");
}
You can delete the resetEvent.WaitOne();
You'll need to make loadingForm a field of course.
Tell me more
Occurs when the background operation has completed, has been canceled, or has raised an exception
BackgroundWorker.RunWorkerCompleted
Make the login form object an instance variable.
//use RunWorkerCompleted event to get notified about work completion where you close the form.
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
event handler code:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.loadingForm.close();
}
Reference: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.runworkercompleted(v=vs.110).aspx
Cek this script, Loading is Form with PictureBox - image gif
private delegate void showProgressCallBack(int value);
private void btnStart5_Click(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
Loading f = new Loading();
f.Show();
bw.DoWork += (s, ea) =>
{
try
{
test1();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
};
bw.RunWorkerCompleted += (s, ea) =>
{
f.Close();
};
bw.RunWorkerAsync();
}
private void showProgress(int value)
{
if (progressBar1.InvokeRequired)
{
showProgressCallBack showProgressDelegate = new showProgressCallBack(showProgress);
this.Invoke(showProgressDelegate, new object[] {value});
}
else
{
progressBar1.Value = value;
}
}
private void test()
{
showProgress(20);
Thread.Sleep(3000);
showProgress(80);
Thread.Sleep(2000);
showProgress(100);
}

Creating a SplashScreen/LoadingScreen with timer

I created a working SplashScreen/LoadingScreen.
I used the following code to show and close the LoadinScreen:
LoadingScreen LS = new LoadingScreen();
LS.Show();
databaseThread = new Thread(CheckDataBase);
databaseThread.Start();
databaseThread.Join();
LS.Close();
This code is doing a great job for me, showing and closing the LoadingScreen.
The problem is: I got some text on the LoadingScreen, that says: Loading Application...
I want to create a Timer to let the dots at the end of the text(Label) do the following:
Loading Application.
1 second later:
Loading Application..
1 second Later:
Loading Application...
I suppose that I need to add a timer to the Load_event of the LoadingScreen form.
How can I achieve this?
Maybe something like this?
class LoadingScreen
{
Timer timer0;
TextBox mytextbox = new TextBox();
public LoadingScreen()
{
timer0 = new System.Timers.Timer(1000);
timer0.Enabled = true;
timer0.Elapsed += new Action<object, System.Timers.ElapsedEventArgs>((object sender, System.Timers.ElapsedEventArgs e) =>
{
switch (mytextbox.Text)
{
case "Loading":
mytextbox.Text = "Loading.";
break;
case "Loading.":
mytextbox.Text = "Loading..";
break;
case "Loading..":
mytextbox.Text = "Loading...";
break;
case "Loading...":
mytextbox.Text = "Loading";
break;
}
});
}
}
Edit:
A good way to prevent UI thread to block waiting for database operation is to move the database operation to a BackgroundWorker ex:
public partial class App : Application
{
LoadingScreen LS;
public void Main()
{
System.ComponentModel.BackgroundWorker BW;
BW.DoWork += BW_DoWork;
BW.RunWorkerCompleted += BW_RunWorkerCompleted;
LS = new LoadingScreen();
LS.Show();
}
private void BW_DoWork(System.Object sender, System.ComponentModel.DoWorkEventArgs e)
{
//Do here anything you have to do with the database
}
void BW_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
LS.Close();
}
}
It should be as simple as:
Timer timer = new Timer();
timer.Interval = 300;
timer.Tick += new EventHandler(methodToUpdateText);
timer.Start();

C# Backgroundworker race - any alternative?

I have some problem with backgroundWorker class. I wish I could within one function cancel Backroundworker and just after it start Backgroundworker dowork from scratch. It cause race and I have no idea how to change it. Is there any alternative to Backgroundworker in .net 3.5
Edited:
My code:
BackgroundWorker worker = new BackgroundWorker();
public void Func()
{
if(worker.IsBusy)
{
worker.CancelAsync();
worker = new BackgroundWorker();
}
View.Clear();
worker.WorkerSupportsCancellation = true;
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
BackgroundWorker sender = s as BackgroundWorker;
if(sender.CancellationPending)
{
args.Cancel = true;
return;
}
View.Generate(Filter);
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if(args.Error != null)
{
MessageBox.Show(args.Error.Message);
return;
}
BackgroundWorker sender = s as BackgroundWorker;
if(args.Cancelled )
{
View.Clear();
}
};
worker.RunWorkerAsync();
}
The problem is: cancelAsync doesnt close worker at all and my view is generated in incorrect way
You can use CancelAsync() and set WorkerSupportsCancellation to true and implements a loop to get CancellationPending
while(x.CancellationPending) { /*TODO*/ }

Categories

Resources