I am writing a sudoku solver app. The calculation time of the solver in certain cases can exceed 3 seconds, which would require a progress bar.
so my code:
private void solveButton_Click(object sender, RoutedEventArgs e)
{
progressBar1.Visibility = Visibility.Visible;
progressBar1.IsIndeterminate = true;
mySolver.Solve(initialValue)
progressBar1.Visilibity=Visilibity.collapsed;
progressBar1.IsIndeterminate = false;
}
The code here is a condensed version of my actual code. This code doesn't work, as the progress bar does not appear at all. It seems to me that the UI updates only after event is finised executed. If I didn't hide the progressbar after the solver step, the progressBar appears after the sudoku is solved. Replacing the solver with thread.sleep(1000) also results in the same UI update.
thanks for your help.
You should start the solver on a separate thread. That way the user interface thread can keep working on user interface objects even during the solving process, which allows your progress bar to be drawn on the screen and updated.
Problem is that your UI thread is not getting free in between to display the progress bar
You need to use the background worker to solve the problem and in the main UI thread you should display the progress bar
private void solveButton_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler(DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
bg.RunWorkerAsync();
progressBar1.Visibility = Visibility.Visible;
progressBar1.IsIndeterminate = true;
}
void DoWork(Object sender, DoWorkEventArgs args)
{
mySolver.Solve(initialValue)
}
void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs args)
{
// this method will be called once background worker has completed it's task
progressBar1.Visilibity=Visilibity.collapsed;
progressBar1.IsIndeterminate = false
}
Technically the code works fine, you just wrote the wrong code.
Your solver is running on the UI thread so it never has a chance to draw the progress bar before you hide it again. You need to spawn a thread (or use a background worker) to free up the UI thread so it can draw your progress bar.
I don't know too much about WP7 but with winforms if you have a long running function it needs to be run on a different thread than the UI.
Is BackgroundWorker available to you on WP7? you can update the bar on the ProgressChanged event and change the viability on the RunWorkerCompleted event
private void solveButton_Click(object sender, RoutedEventArgs e)
{
progressBar1.Visibility = Visibility.Visible;
progressBar1.IsIndeterminate = true;
solveButton.Enabled = false; //I reccomend this so the button can't be pressed twice.
BackgroundWoker bw = new BackgroundWorker();
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerAsync()
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
mySolver.Solve(initialValue, e)
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
//Handle any exceptions that happened in the background worker.
}
progressBar1.Visilibity=Visilibity.collapsed;
progressBar1.IsIndeterminate = false;
solveButton.Enabled = true;
((IDisposable)sender).Dispose();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
//inside mySolver
void Solve(somthing initialValue, DoWorkEventArgs e)
{
//Your solver work
e.ReportProgress(progress); //a int from 0-100
//more solver work
}
If you still want to run it on the UI Thread (which i wouldn't recommend!!) you can use the progressbar from the WP7 Toolkit that was released yesterday. It contains a progressbar that will work while your UI Thread is blocked.
Related
I have a BackgroundWorker and Progressbar. On The DoWork event of the backgroundworker I'm executing a method that may take 2-5 seconds to complete but may take even longer sometimes. I want to show progress in the progressbar to indicate that something is still executing in the background. Most of the examples I found in the web are tasks from a loop but as I said my DoWork is executing a long running process and there's no way for me to determine the percentage of the progress. Please help. Here's my code.
private void MyForm_Load(object sender, EventArgs e)
{
Shown += new EventHandler(MyForm_Shown);
// To report progress from the background worker we need to set this property
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
}
private void MyForm_Shown(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
GetData();//this method may take 2-5 seconds to complete. I want to show progess in the progressbar while method execution is not yet finished.
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Perform something on complete
}
I implemented a windows form in c# with a progress bar in marquee style and a backgroudworker to do a job. The progress bar animation is working correctly when the backgroundworker sleeps, but it hangs when the backgroundworker starts to do something. Does anyone know what I am doing wrong?
Thanks in advance for your answers.
Here is my code:
public FormProgressBarMarquee()
{
InitializeComponent();
this.progressBar1.Style = ProgressBarStyle.Marquee;
this.progressBar1.MarqueeAnimationSpeed = 50;
Shown += new EventHandler(FormProgressBar_Shown);
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_Completed);
}
void FormProgressBar_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000);
// the progress bar animation works correctly
longtimerunningprocess.start();
// the progress bar animation stops
}
void backgroundWorker1_Completed(object sender, RunWorkerCompletedEventArgs e)
{
Debug.Print(" :: FormProgressBar :: ...Pack And Go loaded, close form...");
this.DialogResult = DialogResult.OK;
this.Close();
}
I could finally solve the problem myself. Thanks anyway for the comment #digvijay which put me on the right track.
The program I am writing is part of a class library. longtimerunningprocess is part of the main program and is executed in the main UI thread.
Although I always try to keep the splash screen in the main UI thread and put the non UI work into a separate thread using a backgroundworker, this time I had to put the splash screen in a separate thread.
To do so, I used this suggestion:
https://stackoverflow.com/a/48946
I'm working on a utility to copy a directory to multiple USB sticks. When Form1 loads, I would like for a label to display status "Detecting Disk Drives...", and then call a method to read the drives and populate the form with information. I have it working, except when the form loads it calls the method before displaying the label. Therefore it appears to be hung (the label is actually a white box on a gray background). I have tried timers and threads and everything I can think of, each with a different dead end. I have not yet found a way to have the label update before calling the method to read the drives.
The method getAndDisplayData() is wait 'hangs' my program. I would like for it not to be called until after the form has updated the text of lblDisplayStatus.Text
I also do not want the user to have to interact with the form before calling the method.
Here is my C# code:
private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
getAndDisplayData();
}
Any help would be greatly appreciated.
Here is the code that I ended up with:
BackgroundWorker _worker;
private void USB_Utility_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
//Background Worker
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblDisplayStatus.Text = "Done...";
displayData();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
getData();
}
The old-fashioned way would be to use a BackgroundWorker to run the blocking work in getAndDisplayData and then update the label before starting the worker and again when the worker completes.
Now-adays I assume you could also use tasks to get the exact same result, but I haven't actually tried it as WinForms is not often first choice for new projects.
BackgroundWorker _worker;
public void Form_Load(object sender, EventArgs e) {
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
lblDisplayStatus.Text = "Done...";
}
private void DoWork(object sender, DoWorkEventArgs e) {
getAndDisplayData();
}
About background workers
you can try this
private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
Task<List<string>> t = new Task<List<string>>(DetectDrivesMethod());
t.ContinueWith((result)=>DisplayDrives(result.Result),TaskScheduler.FromCurrentSynchronizationContext);
t.Start();
}
You can tweak the code to fit your requirement. In DetectDriveMethod you will have logic to get data in background thread and in continue with, you can have logic to update UI. It is important that you pass syncronization context otherwise you will end up with Cross Thread exceptions.
If you want to use the new(ish) async/await pattern, you need to use the TaskScheduler to update the UI from the original thread. Here's an example:
// clear the form
tabUSB_Prep.Controls.Clear();
// This is just to show crossing a "context" works
string test = "";
// get the UI's current TaskScheduler
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
// This can be used to wrap a method that doesn't
// directly implement async/await
Task.Run(() =>
{
// Your method to GET the data (don't update the UI here)
test = "I can set a variable in this context!";
}).ContinueWith(task =>
{
if (task.Status == TaskStatus.RanToCompletion)
{
// update your UI here
// Again, this is just to show how crossing the context works
MessageBox.Show(test);
}
else
{
// update UI with an error message, or display a MessageBox?
}
}, scheduler);
I have a method that use in downloading files from server . MY method works fine, however i want to return a calculated percentage of the process execute at each instance of the execution. i tried to use backgroundworker and use the backgroundworker_changed method for my progressbar as below.
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
Start worker on button click.
private void btnStart_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
worker_changed method
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProcessPercentage;
}
My problem is , i understand i have to report progress from the DoWork() method. Please how do i achieve that? I thought of doing something like this.
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = Convert.ToInt32( e.UserState );
}
It still does not fire as there was no progress returned from the DoWork. Any help would be appreciated.
You need to call ReportProgress from your DoWork method along with the precentage int value.
For example:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//you can safely pass data out of the thread via the 'obj'
bw.ReportProgress(0, obj);
}
As explained in this official tutorial from msdn. You have to report progress changes by yourself inside your DoWork method. Here's is an example :
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(10/*the percentage you want*/);
}
The BackgroundWorker.ProgressChanged event :
is raised when you call the ReportProgress method
and it is designed to :
add code to indicate the progress, such as updating the user
interface.
The BackgroundWorker.RunWorkerCompleted event :
occurs when the background operation has completed, has been canceled,
or has raised an exception
Both events are intended to be called when your DoWork method failed, ended or called the ReportProgress method. So in another words, we can say that you're the one deciding when they occur. It cannot be done without an action of yours, it's your process and only you know when it is 20%, 30%or 100% (and so on...) completed.
I have a winform which have several combo boxes and a gridview.
Initially I am creating the gridview with Rows and Columns and no data filled in with.
The filling data to grid is a long running task which will loop through all rows and read column header and depending on that it will apply different color and data to each cell.
What I am trying to achieve is to load the grid as above in form load event, and after the form loaded start filling data to grid so user can see what happening. Same things apply to the combo box value change since I will load data according to the combo value.
What I have tried is something like this...
In form load I am calling method
private void LoadForm()
{
DataBind(); // this will load the initial grid without cell data
this.BeginInvoke((MethodInvoker)this.LongRunningProcess1);
this.BeginInvoke((MethodInvoker)this.LongRunningProcess2);
}
But still it taking long time and I don't have the responsive UI.
I also tried something like this but no luck....
ThreadStart ts = LongRunningProcess1;
Thread t1 = new Thread(ts);
t1.Start();
Also using a background worker to complete the long running operation causes "Cross thread operation" issue.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
LongRunningProcess1();
LongRunningProcess2();
}
Any help to make this working is appreciate..
Thanks
UPDATE
I found a really cool solution Updating Your Form from Another Thread without Creating Delegates for Every Type of Update
Thanks for the answers!!!
To avoid the CrossThreadException in the completed-event of the background worker, wrap your callback method like this:
public void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new RunWorkerCompletedEventHandler(WorkerCompleted), new {sender, e});
}
else
{
// Business logic goes here
}
}
Typically you will populate the data loaded into the GridView in the else-block. If you on the other hand want to populate the GridView progressively from the long-running background task, you can achieve this with a similar technique using callbacks from the background worker:
public void Worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var someParameter in parameterList) // Long-running loop
{
var data = LoadData(someParameter); // Load data for row X
this.Invoke(new Action<object>(UpdateRow),new[]{data}); // Update on UI-thread
}
}
public void UpdateRow(object data)
{
// Code to populate DataGrid row X with data from argument
}
Note that you can call BeginInvoke instead of Invoke if you want to do the UI-updating asynchronously. This will usually not make a difference in this case.
The backgroud worker process is the correct way to go, you just need to eliminate all the "Cross thread operation" exceptions by making sure that all the calls that modify the form elements use the Control.Invoke method wrapper.
If you want to use the backgroundworker/multithreading, you can use delegates updating your form(they run on ui thread). See example here: How to update the GUI from another thread in C#?
Simple example using BackgroundWorker and RunWorkerAsync. Hope this helps.
public partial class Form2 : Form
{
BackgroundWorker worker = new BackgroundWorker();
public Form2()
{
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
InitializeComponent();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int totalSteps = 5;
for (int i = 1; i <= totalSteps; i++)
{
Thread.Sleep(1000);
worker.ReportProgress(i);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
resultText.Text = "Worker complete";
btnDoWork.Enabled = true;
progressBar1.Visible = false;
}
private void btnDoWork_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
worker.RunWorkerAsync();
}
}