I am copying large files from one place to another.
It is taking a long time so I decided to use a progress bar.
I am following this example.
The copyItems() function iterates through the list items and copies the items from another place. It in turn calls a function CopyListItem which copies one item .
I need to tie the backgroundWorker1.ReportProgress(i) to the total no of items i.e. itemcoll.
I do not want to use thread.sleep() .
The progress bar needs to show the actual time required to copy the file from one place to another.
The Progress bar needs to progress when only when one file is copied.
IT needs to complete when all the files are copied
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
// Start the BackgroundWorker.
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= itemscoll.count; i++)
{
// Wait 100 milliseconds.
Thread.Sleep(100);
// Report progress.
backgroundWorker1.ReportProgress(i);
}
}
private void CopyListItem(SPListItem sourceItem, string destinationListName, string destServerURL)
{
// copy items
}
private void copyitems()
{
try
{
int createdYear = 0;
backgroundWorker1.RunWorkerAsync();
foreach (SPListItem sourceItem in itemscoll)
{
if (Helper.year == createdYear)
{
CopyListItem(sourceItem, Helper.destinationListName,Helper.destServerURL);
DeleteItem(CompRefNo);
}
}
}
catch()
{}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString();
}
}
}
You need to do your copy-action in the DoWork-Event of the BackgroundWorker. So when you call the backgroundWorker1.RunWorkerAsync() you'll have to pass an object to it containing all the needed informations. This could be something like:
internal class WorkerItem
{
public WorkerItem(List<SPListItem> spListItems, string destinationListName, string destinationServerURL)
{
SPListItems = new List<SPListItem>(spListItems);
DestinationListName = destinationListName;
DestinationServerURL = destinationServerURL;
}
public List<SPListItem> SPListItems { get; private set; }
public string DestinationListName { get; private set; }
public string DestinationServerURL { get; private set; }
}
The call of RunWorkerAsync can look something like:
backgroundWorker1.RunWorkerAsync(new WorkerItem(...));
In your DoWork-Handler you than have to get this object from the e.Argument and cast it to WorkerItem. Than you can work with it like:
private void BackgroundWorker1OnDoWork(object sender, DoWorkEventArgs e)
{
WorkerItem workerItem = (WorkerItem)e.Argument;
for (int i = 0; i < workerItem.SPListItems.Count(); i++)
{
// CopyListItem is doing the copy for one item.
CopyListItem(workerItem.SPListItems[i], workerItem.DestinationListName, workerItem.DestinationServerURL);
((BackgroundWorker)sender).ReportProgress(i + 1);
}
}
The ProgressChanged-Handler only increments the value of the ProgressBar:
private void BackgroundWorker1OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
If you want to have more informations in the ProgressChanged-Handler you can pass an additional object as UserState. This you can get by object additional = e.UserState;
Right before you call backgroundWorker1.RunWorkerAsync(new WorkerItem(...)) you should set the Maximum of progressBar1 to the amount of SPListItems like:
progressBar1.Maximum = itemscoll.Count;
Your BackgroundWorker only reports it's progress to you if you set.
backgroundWorker1.WorkerReportsProgress = true;
If you want to be informed when the BackgroundWorker is finished you can get the RunWorkerCompleted-Event.
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1OnRunWorkerCompleted;
Related
It seems I need to use a timer in a separate thread, since a time is not working if i use a normal time. So i did the following, however while it stops at the Invoke statement the progress bar does not change value!
public partial class LoadingForm : Form
{
System.Threading.Timer t;
public LoadingForm()
{
InitializeComponent();
}
private void LoadingForm_Load(object sender, EventArgs e)
{
progressBar.Value = 0;
progressBar.Minimum = 0;
progressBar.Maximum = 100;
t = new System.Threading.Timer(TimerCallback, null, 0, 500);
}
private void TimerCallback(object e)
{
//time is here
progressBar.Invoke(new Action(() => progressBar.Value++));
}
private void timerTick()
{
//this is what i need to do !!!!
if (progressBar.Value == progressBar.Maximum) { progressBar.Value = 0; }
progressBar.Value++;
}
}
(if it works ideally I would want to be able to call timerTick() in the Invoke statement)
Currently im trying to update my progress bar if the background worker reports something, heres my code
Form1.cs
namespace YTD
{
public partial class Form1 : Form
{
private Main app;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
int n;
bool isNumeric = int.TryParse(numberBox.Text, out n);
if (!String.IsNullOrWhiteSpace(emailBox.Text) && !String.IsNullOrWhiteSpace(passBox.Text) && !String.IsNullOrWhiteSpace(numberBox.Text) && isNumeric)
{
this.app = new Main(emailBox.Text, passBox.Text, n, logBox, statusBar, backgroundWorker1);
this.app.startMule();
}
else
{
MessageBox.Show("Please fill out all the form fields", "MuleMaker error");
}
}
}
}
And my Main.cs
namespace YTD.classes
{
public class Main
{
private String email;
private String password;
private int number;
private RichTextBox logBox;
private ProgressBar statusBar;
private BackgroundWorker threadWorker;
public Main(String email, String password, int number, RichTextBox logBox, ProgressBar statusBar, BackgroundWorker threadWorker)
{
// Define some variables
this.email = email;
this.password = password;
this.number = number;
this.logBox = logBox;
this.statusBar = statusBar;
this.threadWorker = threadWorker;
}
public void startMule()
{
// Set progressbar 100% value
statusBar.Maximum = this.number;
if (!threadWorker.IsBusy)
{
threadWorker.RunWorkerAsync();
}
}
private void threadWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
// Perform a time consuming operation and report progress.
MessageBox.Show("ye");
System.Threading.Thread.Sleep(500);
threadWorker.ReportProgress(i * 10);
}
}
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Increment(1);
}
}
}
Currently I get no errors but the progress bar value is not beeing changed.
Without the background worker i can update my progress bar fine but not while doing an expensive action.
Your posted Code does not reveal, if you registered your functions to the BackgroundWorker Events.
Creating a new BackgrounWorker isn't enough.
Here is an example:
public Class Main
{
public Main( ... )
{
BackgroundWorker worker = new BackgroundWorker()
worker.WorkerReportsProgress = true;
// Register to BackgroundWorker-Events
worker.DoWork += threadWorker_DoWork;
worker.ProgressChanged += threadWorker_ProgressChanged;
}
}
in addition you should tell your ProgressBar to rerender.
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Increment(1);
statusBar.Invalidate(true);
}
at least you might want to use the value you have set calling ReportProgress(i * 10).
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Value = e.ProgressPercentage;
statusBar.Invalidate(true);
}
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
I am trying to get a ProgressBar with the progress of a dataset being converted to Excel using the BackgroundWorker. The problem is that the work is being done in a different class than the ProgressBar and I am having difficulty calling worker.ReportProgress(...) from within my loop. I am sorry if this is a easy thing but I am new to C# and have been trying this the whole day and just can't seem to get it right. Your help would be HIGHLY appreciated.
namespace CLT
{
public partial class GenBulkReceipts : UserControl
{
private void btnOpen_Click(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
try
{
OpenFile();
}
Cursor.Current = Cursors.Default;
}
private void OpenFile()
{
if (dsEx1.Tables[0].Rows.Count > 0)
{
backgroundWorker1.RunWorkerAsync(dsEx1);
}
}
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
DataSet ImportDataSet = e.Argument as DataSet;
AccountsToBeImported = new BLLService().Get_AccountsToBeReceipted(ImportDataSet);
}
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
// ...
}
}
namespace BLL
{
class GenBulkReceiptsBLL
{
public DataSet Get_AccountsToBeReceipted(DataSet dsImport)
{
DataSet dsReturn = AccountsDAL.QGenReceiptAccounts(0,0,"");//Kry Skoon DataSet wat ge-populate moet word
CLT.GenBulkReceipts pb = new CLT.GenBulkReceipts();
int TotalRows = dsImport.Tables[0].Rows.Count;
//pb.LoadProgressBar(TotalRows);
int calc = 1;
int ProgressPercentage;
foreach (DataRow dr in dsImport.Tables[0].Rows)
{
ProgressPercentage = (calc / TotalRows) * 100;
//This is the problem as I need to let my Progressbar progress here but I am not sure how
//pb.worker.ReportProgress(ProgressPercentage);
}
return dsReturn;
}
// ...
}
}
You'll need to pass your worker to the Get_AccountsToBeReceipted method - it can then call BackgroundWorker.ReportProgress:
// In GenBulkReceipts
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
DataSet importDataSet = e.Argument as DataSet;
AccountsToBeImported =
new BLLService().Get_AccountsToBeReceipted(importDataSet, worker);
}
// In GenBulkReceiptsBLL
public DataSet Get_AccountsToBeReceipted(DataSet dsImport,
BackgroundWorker worker)
{
...
worker.ReportProgress(...);
}
Alternatively, you could make GenBulkReceiptsBLL have its own Progress event, and subscribe to that from GenBulkReceipts - but that would be more complicated.
Your class GenBulkReceiptsBLL needs some reference to the BackgroundWorker instance. You can achieve this in a variety of ways. One such suggestion would be to pass the reference to the class when instantiating it.
For example, since GenBulkReceipts is the class that instantiates GenBulkReceiptsBLL, then in the constructor for GenBulkReceiptsBLL, you could pass the instance of the BackgroundWorker that is currently being used in GenBulkReceipts. This would allow for you to call ReportProcess(...) directly. Alternately, you could pass the reference directly into the Get_AccountsToBeReceipted(...) method.
I have created a GUI (winforms) and added a backgroundworker to run in a separate thread.
The backgroundworker needs to update 2 labels continuously.
The backgroundworker thread should start with button1 click and run forever.
class EcuData
{
public int RPM { get; set; }
public int MAP { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
EcuData data = new EcuData
{
RPM = 0,
MAP = 0
};
BackWorker1.RunWorkerAsync(data);
}
private void BackWorker1_DoWork(object sender, DoWorkEventArgs e)
{
EcuData argumentData = e.Argument as EcuData;
int x = 0;
while (x<=10)
{
//
// Code for reading in data from hardware.
//
argumentData.RPM = x; //x is for testing only!
argumentData.MAP = x * 2; //x is for testing only!
e.Result = argumentData;
Thread.Sleep(100);
x++;
}
private void BackWorker1_RunWorkerCompleted_1(object sender, RunWorkerCompletedEventArgs e)
{
EcuData data = e.Result as EcuData;
label1.Text = data.RPM.ToString();
label2.Text = data.MAP.ToString();
}
}
The above code just updated the GUI when backgroundworker is done with his job, and that's not what I'm looking for.
You need to look at BackgroundWorker.ReportProgess.
You can use it to periodically pass back an object to a method in the main thread, which can update the labels for you.
You can use a System.Threading.Timer and update the UI from the Timer's callback method by calling BeginInvoke on the main Form.
uiUpdateTimer = new System.Threading.Timer(new TimerCallback(
UpdateUI), null, 200, 200);
private void UpdateUI(object state)
{
this.BeginInvoke(new MethodInvoker(UpdateUI));
}
private void UpdateUI()
{
// modify labels here
}
class EcuData
{
public int RPM { get; set; }
public int MAP { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
EcuData data = new EcuData
{
RPM = 0,
MAP = 0
};
BackWorker1.RunWorkerAsync(data);
}
private void BackWorker1_DoWork(object sender, DoWorkEventArgs e)
{
EcuData argumentData = e.Argument as EcuData;
int x = 0;
while (x<=10)
{
e.Result = argumentData;
Thread.Sleep(100);
this.Invoke((MethodInvoker)delegate
{
label1.Text = Convert.ToString(argumentData.RPM = x); //send hardware data later instead, x is for testing only!
label2.Text = Convert.ToString(argumentData.MAP = x * 2); //send hardware data later instead, x is for testing only!
});
x++;
}
}
This works, but it is the correct way of doing it?