Keeping responsive UI - c#

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();
}
}

Related

Asynchronously add controls to Form during Form_Load

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);

How to check the process progress using background worker

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.

Progress bar c# - loading like process [duplicate]

This question already has answers here:
How to use a BackgroundWorker?
(2 answers)
Closed 1 year ago.
Hello I have a function which takes time to load. that's why i'm planning to put a progress bar on my winform so that the user would know that my program is still running. however, I do not know how I'm gonna solve it. is there someone here who could help guide me.
Here's what I'm planning to do:
private void btnProcess_Click(object sender, EventArgs e)
{
//function which takes time because it contacts to a server
}
I want to have a progressbar which increments and ends after my process has finished. Should I use a backgroundworker for this?
***I've followed this tutorial http://www.codeproject.com/Tips/83317/BackgroundWorker-and-ProgressBar-demo but it does not wait for a specific function or event to finish like a loading screen.
***My progressbar does not end after my buttonclick event has finished executing all its functions.
I've created:
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 100; i++)
{
myBackgroundWorker.ReportProgress(i);
System.Threading.Thread.Sleep(100);
}
}
private void myBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
private void btnProcess_Click(object sender, EventArgs e)
{
myBackgroundWorker.RunWorkerAsync();
//function which takes time because it contacts to a server
}
How Would I know when will my buttonclick event gonna end? so that my progress bar will end also?
These are the two really good examples
http://www.dreamincode.net/forums/topic/112547-using-the-backgroundworker-in-c%23/
http://www.dreamincode.net/forums/topic/246911-c%23-multi-threading-in-a-gui-environment/
Hope that helps
EDIT:
public partial class Form1 : Form
{
//--------------------------------------------------------------------------
public Form1()
{
InitializeComponent();
//Initialize backgroundworker
Shown += new EventHandler(Form1_Shown);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
//counter
YourObject.Counter = 100;
}
//--------------------------------------------------------------------------
void btnClick_Clicked(object sender, EventArgs e)
{
//Trigger the background process
backgroundWorker1.RunWorkerAsync();
}
//--------------------------------------------------------------------------
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Your freakishly long process,
//which needs to be run in the background
//to make it simple, I'll just do a for loop
for (int i = 0; i < 100; i++)
{
//The counter will keep track of your process
//In your case, it might not be a for loop
//So you will have to decide how to keep track of this counter
//My suggestion is to decrease/increase this counter
//right after importants action of your process
backgroundWorker1.ReportProgress(YourObject.Counter--);
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//The counter is updated and displayed with a progress bar
YourObject.Counter = e.ProgressPercentage;
}
}
Yeah, you should. It's pretty straightforward.
Make a background worker that executes whatever work btnProcess_Click does.
Have it report progress:
worker.WorkerReportsProgress = true;
And now you can have this progress report be triggered by an event you subscribe to.
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
In doing so, you can create a progress bar than can update itself based on this worker_ProgressChanged event, triggered by your computation.
You can find plenty of implementations of this just by Googling. Good luck, hope this helps.

What's the best way to asynchronously load data into a Data Grid View?

This code allows the form to load before the data is loaded but some of the components on the form such as buttons and the datagridview itself are "invisible" until the data is loaded.
How do I fix this problem?
private void Form1_Load(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(delegate()
{
this.Invoke(new MyDelegate(delegate()
{
ReadXml(path);
Bind();
}));
}));
t.Start();
}
private void Bind()
{
dataGridView1.DataSource = table;
}
I also have this other code which works better, but requires that I use 2 new threads. This can't be the best way to do this, can it?
private void Form1_Load(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(delegate()
{
this.Invoke(new InvokeDelegate(delegate()
{
Thread t2 = new Thread(new ThreadStart(delegate()
{
ReadXml(path);
}));
t2.Start();
t2.Join();
Bind();
}));
}));
t.Start();
}
A form timer (not system timer) will let all other messages process before it fires.
Just give it an interval of like 100-250 milliseconds;
Set it to enabled=false in the designer;
Set it to enabled=true in the form_load event.
In the timer_tick event make the first line timer.enabled = false.
After that (still in the tick event) load your grid.
If you use BeginInvoke() instead of an Invoke() the code in the delegate will be executed on the current UI thread but it wont happen until after all the current UI work pending finishes (like the current Form1_Load invocation). Invoke is a synchronous call so that's why you needed the thread.
void Form1_Load(object sender, EventArgs e)
{
this.BeginInvoke(new MyDelegate(delegate()
{
ReadXml(path);
Bind();
}));
}

Progressbar for long running function WP7 C#

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.

Categories

Resources