Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Below is the sample code, this uses devexpress bareditItem(progressbar) to show progress when data is loaded. I would like to know if there is a way I could show the same progress bar(show progress when data is loading) using async await and task.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ProgressBar {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
DataTable workTable;
public Form1() {
InitializeComponent();
workTable = new DataTable("Records");
workTable.Columns.Add("Id", typeof(int));
workTable.Columns.Add("Data", typeof(String));
}
//this data varies from 0 to 50,000 rows
private void LoadData(DoWorkEventArgs e) {
for(int i = 0; i < 1001; i++) {
System.Threading.Thread.Sleep(5);
workTable.Rows.Add(i, String.Format("Record {0}", i));
this.backgroundWorker1.ReportProgress(i, i);
}
}
private void button1_Click(object sender, EventArgs e) {
this.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
LoadData(e);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
DataTable up = workTable.Clone();
this.barEditItem1.EditValue = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
gridControl1.DataSource = workTable;
}
}
}
Your code can be converted and simplified using the async/await pattern and the Progress<T> class to ensure that the progress report update occurs on the correct thread:
namespace ProgressBar {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
DataTable workTable;
public Form1() {
InitializeComponent();
workTable = new DataTable("Records");
workTable.Columns.Add("Id", typeof(int));
workTable.Columns.Add("Data", typeof(String));
}
//this data varies from 0 to 50,000 rows
private void LoadData(IProgress<int> progress) {
for(int i = 0; i < 1001; i++) {
System.Threading.Thread.Sleep(5);
workTable.Rows.Add(i, String.Format("Record {0}", i));
progress.Report(i);
}
}
private async void button1_Click(object sender, EventArgs e) {
// 1. This replaces: backgroundWorker1_ProgressChanged
var progress = new Progress<int>(
i =>
{
// This code will execute on the UI thread, as it should
DataTable up = workTable.Clone();
this.barEditItem1.EditValue = i;
});
// 2. This replaces: backgroundWorker1_DoWork
await Task.Run(() => this.LoadData(progress));
// 3. This replaces: backgroundWorker1_RunWorkerCompleted
gridControl1.DataSource = workTable;
}
}
}
I left in a few lines of code that you have, that I don't think you actually need, like the Thread.Sleep and DataTable up = workTable.Clone();, but I'm sure you can figure that out.
BackgroundWorker is an obsolete feature for me (Since the release of the async-await feature). Try not to use it from now on. This is the equivalent using the fancy async-await:
private async void button1_Click(object sender, EventArgs e)
{
await LoadData();
}
private async Task LoadData()
{
for (int i = 0; i < 1001; i++)
{
await Task.Delay(5);
workTable.Rows.Add(i, String.Format("Record {0}", i));
DataTable up = workTable.Clone(); // useless but copied from your code
this.barEditItem1.EditValue = i;
}
gridControl1.DataSource = workTable;
}
Sure there is. Don't use a background worker at all as it's obsolete.
Here's some (untested) code that should get you on your way. The SynchronizationContext prevents Cross-threaded exceptions. There might be a better way to do that, but that's what I use.
namespace ProgressBar
{
using System.ComponentModel;
using System.Data;
using System.Threading;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
public partial class Form1 : DevExpress.XtraEditors.XtraForm
{
private DataTable workTable;
private SynchronizationContext _context;
public Form1()
{
InitializeComponent();
this._context = SynchronizationContext.Current;
workTable = new DataTable("Records");
workTable.Columns.Add("Id", typeof(int));
workTable.Columns.Add("Data", typeof(String));
}
private async Task LoadData()
{
const int iterations = 1001; //whatever you want...
for (int i = 0; i < iterations; i++)
{
workTable.Rows.Add(i, String.Format("Record {0}", i));
this._context.Post(() =>
{
this.UpdateWorkProgress((int) i /iterations)
});
}
}
private void UpdateWorkProgress(int percent)
{
this.barEditItem1.EditValue = percent;
}
private void button1_Click(object sender, EventArgs e)
{
await this.LoadData();
gridControl1.DataSource = workTable;
}
}
}
Related
first of all i want you to know that i know that there a lot of results for this question, but i have searched far and wide still haven't come up with a solution for my problem.
i have tried to do the following:
1.constructor
2.objects
3.properties
4.delegates
but none of my implementation of them really did worked as wanted (in this "solution" i have used properties
when i press "back" on the "pop up" screen i dont in the main screen the value i choose from in the "pop up" screen
basically, it's something like, i have main screen and a "pop up"
the main screen
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BakaritCV
{
public partial class FrmProdChoose : MetroFramework.Forms.MetroForm
{
CVFeedUtilities utilities = new CVFeedUtilities();
Mixtures mixture;
public string selectedDefault = " 102";
string t;
public FrmProdChoose(string t)
{
InitializeComponent();
this.t = t;
}
public FrmProdChoose()
{
InitializeComponent();
}
private void btnHome_Click(object sender, EventArgs e)
{
FrmMain frmload = new FrmMain();
utilities.moveBetweenScreens(this, frmload);
}
private void mixtureBtn_Click(object sender, EventArgs e)
{
utilities.loadPopUp(this, mixture);
}
private void FrmProdChoose_Load(object sender, EventArgs e)
{
mixture = new Mixtures(this);
mixtureBtn.Text = selectedDefault;
}
public string Selected
{
get { return selectedDefault; }
set { selectedDefault = value; }
}
}
}
the "pop up"
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BakaritCV
{
public partial class Mixtures : MetroFramework.Forms.MetroForm
{
string[] mixture = new string[] { "102", "103", "104", "105" };
MetroFramework.Controls.MetroTile[] tiles;
FrmProdChoose form;
string selectedDefault;
CVFeedUtilities utilities = new CVFeedUtilities();
public Mixtures(FrmProdChoose form)
{
InitializeComponent();
this.form = form;
}
private void btnHome_Click(object sender, EventArgs e)
{
form.Selected = selectedDefault;
utilities.closePopUp(this, form);
}
private void Mixtures_Load(object sender, EventArgs e)
{
tiles = new MetroFramework.Controls.MetroTile[] { tileOne, tileTwo, tileThree, tileFour};
for (int i = 0; i < mixture.Length; i++)
tiles[i].Text = mixture[i];
}
private void tileOne_Click(object sender, EventArgs e)
{
tileOne.BackColor = Color.ForestGreen;
removeBackColor(1);
}
private void tileTwo_Click(object sender, EventArgs e)
{
tileTwo.BackColor = Color.ForestGreen;
removeBackColor(2);
}
private void tileThree_Click(object sender, EventArgs e)
{
tileThree.BackColor = Color.ForestGreen;
removeBackColor(3);
}
private void tileFour_Click(object sender, EventArgs e)
{
tileFour.BackColor = Color.ForestGreen;
removeBackColor(4);
}
private void tileFive_Click(object sender, EventArgs e)
{
tileFive.BackColor = Color.ForestGreen;
removeBackColor(5);
}
public void removeBackColor(int index)
{
for (int i = 0; i < tiles.Length; i++)
{
if (i == index - 1)
{
selectedDefault = tiles[i].Text;
continue;
}
else tiles[i].BackColor = Color.DeepSkyBlue;
}
}
}
}
and the functions loadPopUp and closePopUp
public void loadPopUp(Form from, Form to)
{
to.Tag = from;
to.Show(from);
}
public void closePopUp(Form from, Form to)
{
to.Tag = from;
if (!to.Visible)
to.Show(from);
from.Hide();
}
This question already has answers here:
Thread.Sleep() in C#
(4 answers)
Closed 6 years ago.
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace _8BB_2._0
{
public partial class Form1 : Form
{
public static class globalVars
{
public static bool spacerunning = false;
}
public Form1()
{
InitializeComponent();
globalVars.spacerunning = false;
}
private void button1_Click(object sender, EventArgs e)
{
if (!globalVars.spacerunning)
{
globalVars.spacerunning = true;
while (globalVars.spacerunning)
{
Thread.Sleep(1000);
SendKeys.Send(" ");
}
}
else if (globalVars.spacerunning)
{
globalVars.spacerunning = false;
}
}
}
}
When I click button1 it's starts hitting space every second like it should but when I try to click it again to shut it off the application freezes and it keeps pressing space. I've tried multiple other ways but can't seem to figure out how I can do two things at once since I get locked inside of the while loop.
Calling Thread.Sleep() will block the UI thread. Try to use async/await instead.
private async void button1_Click(object sender, EventArgs e)
{
globalVars.spacerunning = !globalVars.spacerunning;
while (globalVars.spacerunning)
{
await Task.Delay(1000);
SendKeys.Send(" ");
}
}
UPDATE:
You may use a Timer instead.
public class MainForm : Form
{
private Timer timer = new Timer() { Interval = 1000 };
public MainForm()
{
/* other initializations */
timer.Enabled = false;
timer.Tick += timer_Tick;
}
private void timer_Tick(object sender, EventArgs e)
{
SendKeys.Send(" ");
}
private void button1_Click(object sender, EventArgs e)
{
globalVars.spacerunning = !globalVars.spacerunning;
timer.Enabled = globalVars.spacerunning;
}
}
I am learning backgroundworker class in WPF. The code below is in file MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace FrontEnd
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker backGroundWorker;
public MainWindow()
{
InitializeComponent();
backGroundWorker = ((BackgroundWorker)this.FindResource("backgroundWorker"));
}
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
Flow pro = new Flow(20,10);
backGroundWorker.RunWorkerAsync(pro);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Flow pro = (Flow)e.Argument;
e.Result = pro.NaturalNumbers();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value= e.ProgressPercentage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((int)e.Result == 1) MessageBox.Show("DONE");
progressBar1.Value = 0;
}
}
}
The code below is in file Flow.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace FrontEnd
{
class Flow
{
long i;
//private int x,y;
public int X
{
get; set;
}
public int Y
{
get; set;
}
public Flow(int x, int y)
{
X = x;
Y = y;
}
public int NaturalNumbers()
{
for (i = 0; i < 9999; i++)
{
Console.WriteLine(i);
long iteration = i * 100 / 9999;
if ((i % iteration == 0) &&
(backgroundWorker != null) && backgroundWorker.WorkerReportsProgress)
{
backgroundWorker.ReportProgress(iteration);
}
}
return 1;
}
}
}
Error : The name 'backgroundWorker' does not exist in the current
context
How can I make progress bar working?
Here's a simple example that works:
public partial class BackgroundWorkerPage : Page
{
private readonly BackgroundWorker _worker = new BackgroundWorker();
public BackgroundWorkerPage()
{
InitializeComponent();
_worker.DoWork += WorkerOnDoWork;
_worker.WorkerReportsProgress = true;
_worker.ProgressChanged += WorkerOnProgressChanged;
}
private void WorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
progressBar.Value = progressChangedEventArgs.ProgressPercentage;
}
private void WorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(50);
_worker.ReportProgress(i);
}
}
private void Button_Click_1(object sender, System.Windows.RoutedEventArgs e)
{
_worker.RunWorkerAsync();
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ProgressBar x:Name="progressBar" Height="23" Minimum="0" Maximum="100"/>
<Button Grid.Row="1" Height="23" Content="Start" Click="Button_Click_1"/>
</Grid>
And you need to change your code a bit
private void WorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
var flow = new Flow(_worker);
flow.NaturalNumbers();
}
internal class Flow
{
private readonly BackgroundWorker _worker;
public Flow(int x, int y)
{
X = x;
Y = y;
}
public Flow(BackgroundWorker worker)
{
_worker = worker;
}
public int X { get; set; }
public int Y { get; set; }
public int NaturalNumbers()
{
for (int i = 0; i <= 9999; i++)
{
int iteration = i*100/9999;
// your if(...) fails with divide by zero exception
_worker.ReportProgress(iteration);
}
return 1;
}
}
Introduction
Whenever we try to do some long running operation on the UI without freezing it, we need to run it in a separate thread.In this article we will look into the BackgroundWorker class , as one of the various solutions to this problem with a simple example. BackgroundWorker executes an operation on a separate thread and provide a notification to UI Thread whenever necessary.
Straight To Experiment
Let us create a UI as under
The objective is that,when we will click on the "Populate" button, at the same time we should be able to write something in the "Textbox".
Now let us look into the code which is without BackgroundWorker
public partial class WithOutBackgroundThread : Form
{
List<Employee> lstEmp;
public WithOutBackgroundThread()
{
InitializeComponent();
lstEmp = new List<Employee>();
}
private void btnPopulate_Click(object sender, EventArgs e)
{
GetEmployeeRecords();
dataGridView1.DataSource = lstEmp;
lblStatus.Text = "Work Completed";
}
//Prepare the data
private void GetEmployeeRecords()
{
for (int i = 1; i <= 10; i++)
{
// Simulate a pause
Thread.Sleep(1000);
lstEmp.Add(new Employee { EmpId = i, EmpName = "Name" + i });
}
}
}
The code is pretty straightforward.In the "GetEmployeeRecords()" method, we are preparing the data.We have introduce the "Thread.Sleep(1000)" to make a delay. And in the "Populate" button click event, we are populating the Gird.
But if we execute this code, the UI will become unresponsive and henceforth, we cannot perform any task on the "Textbox" which is our objective.
Henceforth let us change our code to the below
public partial class WithBackgroundThread : Form
{
BackgroundWorker workerThread;
List<Employee> lstEmp;
public WithBackgroundThread()
{
InitializeComponent();
lstEmp = new List<Employee>();
workerThread = new BackgroundWorker();
workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
workerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);
}
private void btnPopulate_Click(object sender, EventArgs e)
{
workerThread.RunWorkerAsync();
}
private void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
GetEmployeeRecords();
}
private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblStatus.Text = "Work Completed";
dataGridView1.DataSource = lstEmp;
}
//Prepare the data
private void GetEmployeeRecords()
{
for (int i = 1; i <= 10; i++)
{
// Simulate a pause
Thread.Sleep(1000);
lstEmp.Add(new Employee { EmpId = i, EmpName = "Name" + i });
}
}
}
A lot of new things.We will explore one by one.
First, we need to declare BackgroundWorker Thread
BackgroundWorker workerThread = new BackgroundWorker();
Next,we need to subscribe to the events
workerThread.DoWork += new DoWorkEventHandler(workerThread_DoWork);
workerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerThread_RunWorkerCompleted);
As a third step, we need to implement the two methods
private void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
GetEmployeeRecords();
}
private void workerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//update ui once worker complete its work
lblStatus.Text = "Work Completed";
dataGridView1.DataSource = lstEmp;
}
The "DoWork" event is raised when we call the "RunWorkerAsync" method. This is where we start the operation that performs the potentially time-consuming work.The "RunWorkerCompleted" event is fired when the background operation has completed, has been canceled, or has raised an exception
As a last step, invoke the "RunWorkerAsync" from the "Populate" button click event.
private void btnPopulate_Click(object sender, EventArgs e)
{
workerThread.RunWorkerAsync();
}
The "RunWorkerAsync" starts execution of a background operation.
Now if we run our application we will be able to populate the grid as well as write something on the "Textbox".
Thanks
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to update GUI from another thread in C#?
I have form with textBox1 and textBox2. I need very simple example of an extra thread to fill textBox2, just to understand principle. So far i am using MessageBox to display value, but i want to use textBox2 instead. Here is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace Threads_example
{
public partial class Form1 : Form
{
Thread t = new Thread(t1);
internal static int x1 = 1;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
static void t1()
{
do
{
x1++;
MessageBox.Show("x1 - " + x1.ToString());
}
while(x1<10);
}
private void button1_Click(object sender, EventArgs e)
{
t.Start();
for (int x = 1; x < 100000; x++)
{
textBox1.Text = x.ToString();
textBox1.Refresh();
}
}
}
}
You need to invoke UI actions in the GUI thread.
It can be done with something like this:
private void RunFromAnotherThreadOrNot()
{
if (InvokeRequired)
{
Invoke(new ThreadStart(RunFromAnotherThread));
return;
}
textBox2.Text = "What Ever";
// Other UI actions
}
I have a problem with C# multi-threading.
Form contents are two buttons and two lables.
If I press on the first button, going looping from 1..to 60000, to update label1. ( It works)
If I press on the second button, going looping from 1..to 6000 to update label2,(and my form is lagged). (is not responding)
Please help!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(countNumbers));
thread.IsBackground = true;
thread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread2 = new Thread(new ThreadStart(countNumbers2));
thread2.Start();
}
public void countNumbers()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
label2.Text = "" + i.ToString();
}
);
}
}
catch (Exception e)
{
}
}
public void countNumbers2()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
label4.Text = "" + i.ToString();
}
);
}
}
catch (Exception e)
{
}
}
private void label3_Click(object sender, EventArgs e)
{
}
}
}
Try using a Forms.Timer in the form and poll a value at regular intervals to update the label in a controlled way. Updating the UI the way you do puts way to much load on the system.
A System.Windows.Forms.Timer runs on the GUI thread.
Just make sure to guard the shared resource in some way, this example uses a volatile member to handle thread synchronization.
You do not need the extra Thread.Sleep(10), it is just there to simulate some load.
private volatile int _counter;
private readonly Timer _timer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
_timer.Tick += TimerTick;
_timer.Interval = 20; // ~50 Hz/fps
_timer.Start();
}
void TimerTick(object sender, EventArgs e)
{
_label.Text = _counter.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(CountNumbers) {IsBackground = true};
thread.Start();
}
public void CountNumbers()
{
for (int i = 0; i < 60000; i++)
{
_counter++;
Thread.Sleep(10); // <-- Simulated work load
}
}
Of course, you can easily expand this example to fit your example with two different counters, calculated on separate threads but still using only one Timer to update the entire UI.
You end up with lagging because Invoke (switching to another thread) is very expensive operation and you are calling it too frequently
Try giving this.Refresh() or Application.DoEvents() in your loop
Try to use lock statement
lock (this)
{
label2.Text = "" + i.ToString();
}
you shoud change your code to
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication23
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(countNumbers));
thread.IsBackground = true;
thread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread2 = new Thread(new ThreadStart(countNumbers2));
thread2.Start();
}
public void countNumbers()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
lock (this)
{
label2.Text = "" + i.ToString();
}
}
);
}
}
catch (Exception e)
{
}
}
public void countNumbers2()
{
try
{
for (int i = 0; i < 60000; i++)
{
this.Invoke((MethodInvoker)delegate()
{
lock (this)
{
label4.Text = "" + i.ToString();
}
}
);
}
}
catch (Exception e)
{
}
}
private void label3_Click(object sender, EventArgs e)
{
}
}
}
Put some synchronization mechanism there
You can use
1.lock(this);
2.monitor.enter(obj); and monitor.exit(obj);
lock (this){
label2.Text = "" + i.ToString();
}
monitor.enter(obj);
label2.Text = "" + i.ToString();
monitor.exit(obj);