In an Office Add-In I need to call a WPF which executes a function which may timeout but I want the UI to be responsive to allow the user to click the cancel/close button.
So far my code is the following:
// From the Ribbon
var f = new Forms.CheckConnectivityPopup();
f.doneEvent.WaitOne();
// Get the status from the popup or null if the operation was cancelled
var status = f.status;
if(status != null)
// Continue the execution
--------------------------------
public partial class CheckConnectivityPopup : MetroWindow
{
public readonly BackgroundWorker worker = new BackgroundWorker();
public AutoResetEvent doneEvent = new AutoResetEvent(false);
public Status status = null;
public CheckConnectivityPopup()
{
InitializeComponent();
this.Show();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// displayAndCheck();
status = CheckStatus();
Thread.Sleep(10000); // to simulate the time
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
doneEvent.Set();
this.Close();
}
}
So far the popup is frozen until sleep period is completed.
WaitOne() blocks the current thread. You could replace the AutoResetEvent with a SemaphoreSlim:
public partial class CheckConnectivityPopup : MetroWindow
{
public readonly BackgroundWorker worker = new BackgroundWorker();
public SemaphoreSlim doneEvent = new SemaphoreSlim(0, 1);
public Status status = null;
public CheckConnectivityPopup()
{
InitializeComponent();
this.Show();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//displayAndCheck();
status = CheckStatus();
Thread.Sleep(10000); // to simulate the time
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
doneEvent.Release();
this.Close();
}
}
...that you can await asynchronously:
var f = new Forms.CheckConnectivityPopup();
await f.doneEvent.WaitAsync();
For you to be able to await the WaitAsync() method, you must mark the method where you create the instance of the CheckConnectivityPopup as async:
void async YourMethod() { ... }
If this is not an option for some reason, you could use the ContinueWith method instead:
var f = new Forms.CheckConnectivityPopup();
f.doneEvent.WaitAsync().ContinueWith(_ =>
{
var status = f.status;
if (status != null)
{
//...
}
});
I need to update the program i did when i was still 3 months in the office. And i found out that the code is not clean and not easy to modify.
What i did is every method is called in background worker. And i created multiply Background worker for each task that possibly be executed at once.
So i am planning in making the code cleaner and easier to maintain.
I created a class and inherit the BackgroundWorker as shown below in my code
public class Bgw : BackgroundWorker
{
BackgroundWorker worker;
public int ProcessId { get; set; }
public object[] ProcessData { get; set; }
public Bgw(int i, object[] obj)
{
ProcessId = i;
ProcessData = obj;
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.ProgressChanged += Worker_ProgressChanged;
worker.RunWorkerAsync();
}
public void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
public void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
public void Worker_DoWork(object sender, DoWorkEventArgs e)
{
if (ProcessId == 1)
{
//executed my code here
}
}
}
But with the code above. How can i notify the changes in my controls if i call the class like this
var obj = new object[3];
obj[0] = 1;
obj[1] = PdfPath;
obj[2] = EpubPath;
var work = new Bgw(1, obj);
Or is there a better way on doing this? I know that i need to use the ProgressChanged. But how can i Get that changes from where i called that class?
Thank you
Edit: If im going to use async and await like this
class Program
{
public async static void TestAsyncAwaitMethods()
{
await Task.Run(() =>
{
for (var i = 0; i <= 5000000; i++)
{
UpdateUI(i);
}
});
}
private static void UpdateUI(int i)
{
Console.WriteLine("Update the UI: " + i);
}
}
ANd call the method TestAsyncAwaitMethods()
How can i update my GUI. When the method reside from a different class and the property i want to update is in another class(I'm using mvvm pattern).
I have a Windows Form application and managed DLL in one solution. DLL contains some time consuming functions during which I wish to update the Form contents (callback from the DLL to the Form with progess updates). I have the following code:
Form code, where I initialize the DLL and give it a callback function in the Initialize method. I also start a separate Thread to periodicly check the message_queue for new messages from the DLL. The DLL function is also called in a separate Thread (non blocking for the UI).
private LibraryDLL library_dll;
private ConcurrentQueue<string> message_queue;
public MainForm()
{
InitializeComponent();
library_dll = new LibraryDLL();
message_queue = new ConcurrentQueue<string>();
library_dll.Initialize(ProcessMessage);
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
string message;
if (message_queue.TryDequeue(out message))
{
PrintMessage(message);
}
}).Start();
}
private void ProcessMessage(string message)
{
message_queue.Enqueue(message);
}
private void PrintMessage(string message)
{
this.Invoke((MethodInvoker)delegate
{
listBox_rows.Items.Add(message);
});
}
private void button_send_Click(object sender, EventArgs e)
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
library_dll.DoWork();
}).Start();
}
In DLL code, I use the callback method to report progress:
private CallBack callback;
public delegate void CallBack(string message);
public LibraryDLL() { }
public void Initialize(CallBack callback)
{
this.callback = callback;
}
public void DoWork()
{
callback("working...")
Thread.Sleep(500);
callback("working...")
Thread.Sleep(500);
callback("working...")
Thread.Sleep(500);
}
My problem is, that instead of string "working" appearing every 500ms, it appears 3 times after 1500ms (only after the Thread in which the DoWork method is running ends). I also tried the Invalidate()-Update()-Refresh() sequence in the Form's PrintMessage function, but without any effect.
Thanks for the advice!
EDIT1:
I modified the code to use the BackgroundWorker, however, the problem remains (nothing for 1500ms, than all 3 strings at once).
BackgroundWorker bck_worker;
public MainForm()
{
InitializeComponent();
library_dll = new LibraryDLL();
library_dll.Initialize(bck_worker);
bck_worker = new BackgroundWorker();
bck_worker.ProgressChanged += new ProgressChangedEventHandler(bckWorker_ProgressChanged);
bck_worker.WorkerReportsProgress = true;
bck_worker.WorkerSupportsCancellation = true;
}
private void bckWorker_DoWork(object sender, DoWorkEventArgs e)
{
library_dll.DoWork();
}
private void bckWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
PrintMessage((string)e.UserState);
}
private void button_send_Click(object sender, EventArgs e)
{
bck_worker.DoWork += new DoWorkEventHandler(bckWorker_DoWork);
bck_worker.RunWorkerAsync();
}
private void PrintMessage(string message)
{
listBox_rows.Items.Add(message);
}
And the DLL:
private BackgroundWorker bck_worker;
public LibraryDLL() { }
public void Initialize(BackgroundWorker bck_worker)
{
this.bck_worker = bck_worker;
}
public void DoWork()
{
bck_worker.ReportProgress(25, "working...");
Thread.Sleep(500);
bck_worker.ReportProgress(50, "working...");
Thread.Sleep(500);
bck_worker.ReportProgress(75, "working...");
Thread.Sleep(500);
}
EDIT2:
OK, I now tried to add the Invalidate-Update-Refresh sequence at the end of the PrintMessage function and it finaly works (with the BackgroundWorker approach)!
Use background worker and workers's report progress to update your UI: background worker doc
In my program I have two methods that takes a while to complete, about few minutes each. While these methods are being executed, I display a Progress Bar in a separate window which shows the progress of each method. My two methods are in a static Utility class. They look like the following:
public static class Utility
{
public static bool TimeConsumingMethodOne(object sender)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
(sender as BackgroundWorker).ReportProgress(i);
}
return true;
}
public static bool TimeConsumingMethodTwo(object sender)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50);
(sender as BackgroundWorker).ReportProgress(i);
}
return true;
}
}
Reading through similar questions in SO I learned that I should use BackgroundWorker and used the RunWorkerCompleted() to see when the worker completes its work. So in my Main() I used BackgroundWorer() and subscribed to the RunWorkerCompleted() method. My goal here is to run the TimeConsumingMethodOne() first (and display progress while running), then once finished, run TimeConsumingMethodTwo() and show progress again, and when that's completed output the message box (which simulates some other work in my program). My Main() looks like the following:
public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}
private BackgroundWorker worker = null;
private AutoResetEvent _resetEventOne = new AutoResetEvent(false);
private AutoResetEvent _resetEventTwo = new AutoResetEvent(false);
private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;
public MainWindow()
{
InitializeComponent();
}
private void btnRun_Click(object sender, RoutedEventArgs e)
{
RunMethodCallers(sender, MethodType.One);
_resetEventOne.WaitOne();
RunMethodCallers(sender, MethodType.Two);
_resetEventTwo.WaitOne();
MessageBox.Show("COMPLETED!");
}
private void RunMethodCallers(object sender, MethodType type)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
switch (type)
{
case MethodType.One:
worker.DoWork += MethodOneCaller;
worker.ProgressChanged += worker_ProgressChangedOne;
worker.RunWorkerCompleted += worker_RunWorkerCompletedOne;
break;
case MethodType.Two:
worker.DoWork += MethodTwoCaller;
worker.ProgressChanged += worker_ProgressChangedTwo;
worker.RunWorkerCompleted += worker_RunWorkerCompletedTwo;
break;
}
worker.RunWorkerAsync();
}
private void MethodOneCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});
Utility.TimeConsumingMethodOne(sender);
}
private void MethodTwoCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});
Utility.TimeConsumingMethodTwo(sender);
}
private void worker_RunWorkerCompletedOne(object sender, RunWorkerCompletedEventArgs e)
{
_resetEventOne.Set();
}
private void worker_RunWorkerCompletedTwo(object sender, RunWorkerCompletedEventArgs e)
{
_resetEventTwo.Set();
}
private void worker_ProgressChangedOne(object sender, ProgressChangedEventArgs e)
{
pbWindowOne.SetProgressUpdate(e.ProgressPercentage);
}
private void worker_ProgressChangedTwo(object sender, ProgressChangedEventArgs e)
{
pbWindowTwo.SetProgressUpdate(e.ProgressPercentage);
}
}
Now the problem I have is, when I use _resetEventOne.WaitOne(); the UI hangs. If I removed those two waits, both methods run asynchronously and the execution moves on and outputs the MessageBox even before those two methods complete.
What am I doing wrong? How do I get the program to finish my first BackgroundWorker and then move onto the next, and then when that's done, output the MessageBox?
Now the problem I have is, when I use _resetEventOne.WaitOne(); the UI hangs. If I removed those two waits, both methods run asynchronously and the execution moves on and outputs the MessageBox even before those two methods complete.
What am I doing wrong?
When you call WaitOne(), you are blocking the UI thread, causing the UI to hang. If you remove that call, then of course you start both workers at once.
There are several different ways to approach your question. One is to stick as closely to your current implementation, and just fix the barest minimum to get it to work. Doing that, what you'll need to do is perform the actual next statement in the RunWorkerCompleted handler, instead of using an event to wait for the handler to execute.
That looks like this:
public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}
private BackgroundWorker worker = null;
private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;
public MainWindow()
{
InitializeComponent();
}
private void btnRun_Click(object sender, RoutedEventArgs e)
{
RunMethodCallers(sender, MethodType.One);
}
private void RunMethodCallers(object sender, MethodType type)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
switch (type)
{
case MethodType.One:
worker.DoWork += MethodOneCaller;
worker.ProgressChanged += worker_ProgressChangedOne;
worker.RunWorkerCompleted += worker_RunWorkerCompletedOne;
break;
case MethodType.Two:
worker.DoWork += MethodTwoCaller;
worker.ProgressChanged += worker_ProgressChangedTwo;
worker.RunWorkerCompleted += worker_RunWorkerCompletedTwo;
break;
}
worker.RunWorkerAsync();
}
private void MethodOneCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});
Utility.TimeConsumingMethodOne(sender);
}
private void MethodTwoCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});
Utility.TimeConsumingMethodTwo(sender);
}
private void worker_RunWorkerCompletedOne(object sender, RunWorkerCompletedEventArgs e)
{
RunMethodCallers(sender, MethodType.Two);
}
private void worker_RunWorkerCompletedTwo(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("COMPLETED!");
}
private void worker_ProgressChangedOne(object sender, ProgressChangedEventArgs e)
{
pbWindowOne.SetProgressUpdate(e.ProgressPercentage);
}
private void worker_ProgressChangedTwo(object sender, ProgressChangedEventArgs e)
{
pbWindowTwo.SetProgressUpdate(e.ProgressPercentage);
}
}
That said, BackgroundWorker has been made obsolete by the newer task-based API with async and await. With some small changes to your code, it can be adapted to use that newer idiom:
public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}
private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;
public MainWindow()
{
InitializeComponent();
}
private async void btnRun_Click(object sender, RoutedEventArgs e)
{
await RunMethodCallers(sender, MethodType.One);
await RunMethodCallers(sender, MethodType.Two);
MessageBox.Show("COMPLETED!");
}
private async Task RunMethodCallers(object sender, MethodType type)
{
IProgress<int> progress;
switch (type)
{
case MethodType.One:
progress = new Progress<int>(i => pbWindowOne.SetProgressUpdate(i));
await Task.Run(() => MethodOneCaller(progress));
break;
case MethodType.Two:
progress = new Progress<int>(i => pbWindowTwo.SetProgressUpdate(i));
await Task.Run(() => MethodTwoCaller(progress));
break;
}
}
private void MethodOneCaller(IProgress<int> progress)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});
Utility.TimeConsumingMethodOne(progress);
}
private void MethodTwoCaller(IProgress<int> progress)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});
Utility.TimeConsumingMethodTwo(progress);
}
}
To do the above does require a small adjustment to the Utility class as well:
static class Utility
{
public static bool TimeConsumingMethodOne(IProgress<int> progress)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
progress.Report(i);
}
return true;
}
public static bool TimeConsumingMethodTwo(IProgress<int> progress)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50);
progress.Report(i);
}
return true;
}
}
That is, the Progress<T> class takes the place of the BackgroundWorker.ProgressChanged event and ReportProgress() method.
Note that with the above, the code has gotten significantly shorter, simpler, and is written in a more direct way (i.e. related statements are with each other in the same method now).
The example you gave is necessarily simplified. That's perfectly fine, but it does mean that it's not known here what the Thread.Sleep() method represents. In fact, in many cases, this sort of thing can be refactored further such that only the long-running work is done asynchronously. This can sometimes simplify the progress-reporting even further, because it can be done after await-ing each individual asynchronously-executed work component.
For example, let's suppose the work in the loop is either inherently asynchronous or is costly enough that it's reasonable to use Task.Run() to execute each loop iteration. For the purpose of the same, that can be represented using Task.Delay():
static class Utility
{
public static async Task<bool> TimeConsumingMethodOne(Action<int> progress)
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(100);
progress(i);
}
return true;
}
public static async Task<bool> TimeConsumingMethodTwo(Action<int> progress)
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(50);
progress(i);
}
return true;
}
}
In the above, I also don't use Progress<T>. Just a simple Action<int> delegate for the caller to use however they want.
And with that change, your window code gets even simpler:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void btnRun_Click(object sender, RoutedEventArgs e)
{
await MethodOneCaller();
await MethodTwoCaller();
MessageBox.Show("COMPLETED!");
}
private async Task MethodOneCaller()
{
ProgressBarWindow pbWindowOne =
new ProgressBarWindow("Running Method One") { Owner = this };
pbWindowOne.Show();
await Utility.TimeConsumingMethodOne(i => pbWindowOne.SetProgressUpdate(i));
}
private async Task MethodTwoCaller()
{
ProgressBarWindow pbWindowTwo =
new ProgressBarWindow("Running Method Two") { Owner = this };
pbWindowTwo.Show();
await Utility.TimeConsumingMethodTwo(i => pbWindowTwo.SetProgressUpdate(i));
}
}
Granted, I took the opportunity to remove the MethodType enum and just call the methods directly, which shortened the code even more. But even if all you did was avoid the use of Dispatcher.Invoke(), that still simplifies the code a lot.
In addition to all that, if you were using data binding to represent the progress state instead of setting the value directly, WPF would handle the cross-thread invocation implicitly for you, so that the Progress<T> class isn't even required even if you can't refactor the Utility class code for it itself to be async.
But, those are minor refinements compared to moving away from BackgroundWorker. I recommend doing that, but whether you invest time in those further refinements is less important.
An option i prefer is to have those 2 methods in a different thread and use a while loop to check if thread is still running and if it is use Task.Delay()
EG.
private async void BlahBahBlahAsync()
{
Thread testThread = new Thread(delegate () { });
newThread = new Thread(delegate ()
{
Timeconsuming();
});
newThread.Start();
while (testThread.IsAlive)
{
await Task.Delay(50);
}
}
private void Timeconsuming()
{
// stuff that takes a while
}
I am trying to run a function in a different class than the dispatcher through a backgroundworker and have it update the progress on every iteration. I am getting no errors and the backgroundworker is functioning properly, but my textbox never updates...
public partial class MainWindow : Window
{
public BackgroundWorker worker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(workerDoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(workerProgressChanged);
}
private void myButtonClick(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
void workerDoWork(object sender, DoWorkEventArgs e)
{
yv_usfm.convert(worker);
}
void workerProgressChanged(object sender, ProgressChangedEventArgs e)
{
myTextBox.Text = "some text";
}
}
public class yv_usfm
{
public static void convert(BackgroundWorker worker)
{
int i = 1;
while (i < 100)
{
worker.ReportProgress(i);
i++;
}
}
}
What makes you say the BackgroundWorker is functioning properly? I see no call to worker.RunWorkerAsync(), and without that it will never start.
You're not starting the worker!
worker.RunWorkerAsync();
Try This:
void DoWork(...)
{
YourMethod();
}
void YourMethod()
{
if(yourControl.InvokeRequired)
yourControl.Invoke((Action)(() => YourMethod()));
else
{
//Access controls
}
}
Hope This help.