Access any background worker created in runtime - c#

Scenario: I have a DataGrid in my application in which onclikcing each row, the values get populated in the texboxes below. I'll update the values and when clicking save the process is taking more time to complete.
So i have written a backgroundworker to make the process run asynchronously. When each row is clikced an instance of a backgroundworker is created and the process is accomplished. During that update the user will select the second row of the grid and update that values. So this will create another instance and the process will run in background.
Now when both the update process is running if the user selects the first row of the grid there should be a message showing "The process is still running".
//Code:
' OnClick of the event
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += delegate {
SaveDetails();
};
bw.RunWorkerCompleted += delegate {
MessageBox.Show("Completed");
};
bw.RunWorkerAsync();
'Save method
public void SaveDetails()
{
for (int i = 0; i < 10;i++ )
{
System.Threading.Thread.Sleep(5000);
MessageBox.Show("Hi");
}
}
How can i access the previously created backgroundworker instance and check the status of the process and display the messgage?
Note: There may be many process running simultaneously, so i should be able to access any process.
Is this possible?

You can store any BackgrounWorker you create in a List to refer to them at any moment, but alternatively you can create a tasks Queue and store there any pending process, so you have only one BackgroundWorker at any time.
Storing the BackgroundWorkers in a List
Create a List(Of BackgroundWorker) which can be accesed anywhere you need in your code, for example. Every time you create a new BackgroundWorker, add it to the List:
mylist.Add(bw)
You a lot of options to access the correct backgroundworker later on. The easiest one is to create your own class which will have an identificator (the row of the DataGrid, for example) and the backgroundworker. This way, your list will be of this class instead of BackgroundWorkers:
myClass.BackgroundWorkerProperty = bw
myClass.id = myId
myList.Add(myClass)
Using a Queue to run the tasks
Create a Queue with a type which has the information to run the task. For example, the row of the DataGrid, if that's enought, that will be type Integer (the index), then add it everytime the backgroundworker is running:
myQueue.Add(myRow)
Everytime the backgroundworker finish, check the Queue and run the next task stored.

You can use the Tag property of the DataGridViewRow:
var bw = new BackgroundWorker();
row.Tag = bw;
So you can access it.

If you are using .NET 4.5 i would suggest to use Async/Await rather than Background thread to solve your problem, because its programming style is easy and efficient as given below:
public partial class Form1 : Form
{
List<Task<bool>> taskList = new List<Task<bool>>();
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
Task<bool> task = Task.Run(() => SaveDetails());
MessageBox.Show(task.Id + " started.");
taskList.Add(task);
var isSuccess = await task;
}
public bool SaveDetails()
{
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(5000);
//MessageBox.Show("Finishing.");
}
return true;
}
private void button2_Click(object sender, EventArgs e)
{
foreach (var task in taskList)
{
if (task.IsCompleted == true)
MessageBox.Show(task.Id + " Completed.");
}
}
}
i have created a second button click event to access and display all currently completed tasks.
Also, please make a note that you can use task.Id as the row identifier of your grid row.
Hope that helps.

well, that's rather easy. put the BackgroundWorker as a member in your form and then you can access it from everywhere:
BackgroundWorker bg;
public Form1()
{
bg = new BackgroundWorker();
}
private void button1_Click(object sender, EventArgs e)
{
if(bg.IsBusy)
{
// show the message
}
else
{
bw.WorkerReportsProgress = true;
bw.DoWork += delegate {
SaveDetails();
};
bw.RunWorkerCompleted += delegate {
MessageBox.Show("Completed");
};
bw.RunWorkerAsync();
}
}

Related

Progress Bar with Label — Unable to Update Label in ProgressChanged

I have a background worker with a long running task. The task goes through a list of files and I want to update the user with which file we are on. I have a tool strip that has a label named panel1.text. The progress bar is working however the label is not changing in my ProgressChanged method i.e. It should say Processing File1 then change to Processing File2, but it stays on the default of Processing.
private void btnProcess_Click(object sender, EventArgs e)
{
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Maximum = 1000000000;
panel1.Text = "Processing "; // this appears properly
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(processFiles);
worker.ProgressChanged += ProgressChanged;
worker.RunWorkerAsync();
while (worker.IsBusy)
{
// the reason for this is because nothing can happen until the processing is done
toolStripProgressBar1.Increment(1);
}
// more processing
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
panel1.Text = "Processing "+ e.UserState.ToString(); <<<---- This is Not Updating panel1.Text but it evaluates properly
}
private void processFiles(object sender, EventArgs e)
{
int retVal = 0;
foreach (string fileName in listBox1.Items)
{
ProgressChangedEventArgs ea = new ProgressChangedEventArgs(1,fileName);
ProgressChanged(this, ea);
// do more processing
}
}
I would appreciate any help.
You are using the same thread, which is being blocked by another process. You need to use a Task to create a new thread and possibly use Dispatcher.BeginIvoke if the control is on the other thread. Make sure whatever Button Click, etc is happening is marked with the Async keyword as well to make it Asynchronous.
Example:
Await Task mytask = Task.Run(() =>
for(var i = 0; i < 1000; i++)
{
Label.Dispatcher.BeginInvoke( () =>
UpdateMe(int i, LabelClass/Component class/component)});
Then inside the Label Class or wherever the label is:
Public void UpdateMe(int i, LabelClass class)
{
class.label.content = Cint((i/Total)*100);
Thread.Sleep(500);
}
There are other ways to do it as well such as Binding the value to the UI, but this will give you a better understanding of why its not working and how things work with other threads.
If you want to really get a visual understanding call:
`Console.WriteLine($"Current Thread ID: System.Threading.Thread.CurrentThread.ManagedThreadId}");`
Right before you go into the Task---it will give you the main thread ID
Then inside the Task call it again...this will give you the secondary thread ID.
Then Right before the Dispatcher call:
Console.WriteLine($"Do I have access to the label on this thread? {Label.Dispatcher.CheckAccess()}";
If you have access it will display True, if not it will display False...In your case it will display false because its owned by the other thread, but you can use the Dispatcher to be able to do work on that thread while in another thread...
Also, I recommend you not use Background Worker and use Tasks instead...this explains why in depth...basically Tasks do everything Background workers do and more, have less issues and are easier to work with...
http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html
As already commented by Ivan, remove the while loop while (worker.IsBusy) as it's blocking the UI thread to process further. As well, you should enable the WorkerReportsProgress to true
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
while (!worker.IsBusy)
{
worker.RunWorkerAsync();
}
Per your comment, move those later processing to BackgroundWorker.RunWorkerCompleted Event

How to perform operation on a background thread

I am working on Silverlight project based on MVVM architechture.
on click of a button a c# linq query gets executed which takes some time to execute (about one and half minute) due to which my UI hangs for this much time untill the response is received.
I'm having a custom progress bar which needs to be shown in between.
I tried to execute this linq statement on a background thread but no success.
Current Code:
private void _selectRecords()
{
//linq-query
}
I tried below steps,
private void _selectRecords()
{
System.Threading.Thread worker = new System.Threading.Thread(GetData);
worker.Start();
}
private void GetData()
{
//linq-query
}
EDIT :
in above case while execution getting Exception Invalid cross-thread access.
and
private void _selectRecords()
{
System.Threading.Thread worker = new System.Threading.Thread(GetData);
worker.Start();
}
private void GetData()
{
ApplicationConstants.AppConstants.WaitCursor = true; //MY PROGRESS BAR
Deployment.current.Dispatcher.BeginInvoke(()=>{
//linq-query
});
}
how can i run this linq statement on a background thread?
How about using BackgroundWorker
Declare a backgroundworker variable like this in your view Class
BackgroundWorker _worker;
in the constructor of your view initiate a Backgroundworker
like this
_worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
//call your getdata() method here
GetData();
}
In button click you can start this backgroundworker like this
m_oWorker.RunWorkerAsync();
Using Progress Changed Event handler you can show the progress to the user
_worker_ProgressChanged
I just tried to incorporate your code into something like this

How to update the UI in real-time using Threads

I am developing an application which will actively read from a file and analyze and display information in that file to the UI in real time.
Everything I have read tells me I should use some kind of threading to do this. I have explored background workers and also tried creating a separate thread at run time and using that thread to update all of the UI elements.
The problem comes in when I can't (or shouldn't) be making cross thread calls, as the original UI elements were created on a different thread.
Is there a way to create these UI elements on the thread that will be updating them? What is the best way to do this?
edit: There was a reply to this post (which is now gone) explaining how I should do this. After updating my code with the method described
This is the updated code I have used. Everything is fine until I add in the file system watcher. As soon as I add that I get the same error about not making cross thread calls.
Session is a class I created that parses through the log file
private Session s1 = new Session("");
private FileSystemWatcher fsw;
private OpenFileDialog ofd1 = new OpenFileDialog();
private BackgroundWorker bgw;
private bool logActive = false;
public frmMain()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
bgw.ProgressChanged += HandleProgressChanged;
bgw.DoWork += HandleDoWork;
fsw = new FileSystemWatcher(#"H:\Logs", "*.txt");
fsw.SynchronizingObject = this;
fsw.IncludeSubdirectories = false;
fsw.EnableRaisingEvents = true;
fsw.NotifyFilter = NotifyFilters.Size;
fsw.Changed += new FileSystemEventHandler(fsw_OnChanged);
}
private void frmMain_Load(object sender, EventArgs e)
{
ofd1.Filter = "log files (*.txt)|*.txt|All files (*.*)|*.*";
ofd1.FilterIndex = 2;
ofd1.RestoreDirectory = true;
}
private void fsw_OnChanged(object source, System.IO.FileSystemEventArgs e)
{
bgw.RunWorkerAsync();
}
// this runs on the UI thread
// here's where you update the UI based on the information from the event args
private void HandleProgressChanged(object sender, ProgressChangedEventArgs e)
{
for (int i = s1.previousLineNumber; i < s1.GetMessageCount(); i++)
{
ListViewItem lvi = new ListViewItem((s1.GetMessage(i).date).ToString());
lvi.SubItems.Add(s1.GetMessage(i).type.ToString());
lvi.SubItems.Add(s1.GetMessage(i).data);
listView1.Items.Add(lvi);
}
}
// this runs on a background thread; you cannot modify UI controls here
private void HandleDoWork(object sender, DoWorkEventArgs e)
{
s1.ParseLiveFile();
bgw.ReportProgress(100);
}
In order to update the UI you should use Invoke or BeginInvoke.
void LengthyProcessInThread()
{
...
foreach(var item in file)
{
Invoke(delegate() {
.. Update UI here.
});
}
}
Invoke is a method on a control, Eg. the form containing the UI.
I wish you good luck with your quest.

How to update a list box by an asynchronous call?

I have developed a windows forms c# application, i just want update items in a Listbox in the main form by spin offing another thread without blocking the GUI form.
Since threads cannot access form entities like listbox, i thought of using delegates.
Following code in the below shows how i used a delegate to do that task, but it blocks the GUI form. so i just want to convert it to an asynchronous delegate which updates list box without blocking the GUI Form
delegate declaration
delegate void monitoringServiceDel();
calling the delegate
new monitoringServiceDel(monitoringService).BeginInvoke(null, null);
delegate method implementation
private void monitoringService()
{
this.listEvents.Invoke(new MethodInvoker(delegate()
{
int i = 0 ;
while (i<50)
{
listEvents.Items.Add("count :" + count++);
Thread.Sleep(1000);
i ++;
}
}));
}
For Win Forms you'll need to use the Control's Invoke method:
Executes the specified delegate on the thread that owns the control's
underlying window handle
The basic scenario is:
Do the heavy lifting work with a BackgroundWorker to retrieve all of your items on a non UI blocking thread.
On the BackgroundWorker.RunWorkerCompleted Event, use the Control's Invoke method to add the items to the Control (ListBox in your case).
Something along the lines of:
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => MethodToDoWork;
bw.RunWorkerCompleted += (sender, args) => MethodToUpdateControl;
bw.RunWorkerAsync();
This should get you going in the right direction.
Edit: working sample
public List<string> MyList { get; set; }
private void button1_Click( object sender, EventArgs e )
{
MyList = new List<string>();
var bw = new BackgroundWorker();
bw.DoWork += ( o, args ) => MethodToDoWork();
bw.RunWorkerCompleted += ( o, args ) => MethodToUpdateControl();
bw.RunWorkerAsync();
}
private void MethodToDoWork()
{
for( int i = 0; i < 10; i++ )
{
MyList.Add( string.Format( "item {0}", i ) );
System.Threading.Thread.Sleep( 100 );
}
}
private void MethodToUpdateControl()
{
// since the BackgroundWorker is designed to use
// the form's UI thread on the RunWorkerCompleted
// event, you should just be able to add the items
// to the list box:
listBox1.Items.AddRange( MyList.ToArray() );
// the above should not block the UI, if it does
// due to some other code, then use the ListBox's
// Invoke method:
// listBox1.Invoke( new Action( () => listBox1.Items.AddRange( MyList.ToArray() ) ) );
}
If you are modifying a UI element, then you are going to HAVE to block the UI thread. If the items come in bursts or require processing between adding each one, then you might want to think about running the processing behind the scenes (via a backgroundworker or a Task). But, if you are just taking data and populating the list, then you are required to use the UI thread.
The easiest solution would be to use the BackgroundWorker control, combined with two Panels. The idea is to have one panel on the foreground Visible when the form loads, and have an ImageBox inside of it that plays a simple loading gif. The ListBox will be inside the other panel that won't be visible by default and will be right behind the first panel.
Once the form is loaded, start your BackgroundWorker and accomplish whatever Data retrieving or updating that you have to do and once the Task is complete, set the data inside your ListBox and simply bring the ListBox panel and make it visible.
That way you'll have a Semi Asynchronous loading of your ListBox, while it's not updated after every item being added. You can use this technique anytime you want, not simply on form load!
Here is a code example:
namespace AsyncForm
{
public partial class Form1 : Form
{
private List<String> collectionItems = new List<String>();
public Form1()
{
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 20; i++)
{
((List<String>)e.Argument).Add("Something " + i);
System.Threading.Thread.Sleep(200);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
listBox1.Items.AddRange(collectionItems.ToArray());
listBox1.Visible = true;
pictureBox1.Visible = false;
}
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(collectionItems);
}
}
}
You should separate function to update UI and long-time process.
To handle UI logic..
private void UpdateUI(string item)
{
if (Thread.CurrentThread.IsBackground)
{
listEvents.Dispatcher.Invoke(new Action(() => //dispatch to UI Thread
{
listEvents.Items.Add(item);
}));
}
else
{
listEvents.Items.Add(item);
}
}
To do asynchronous process using TaskParallel
private void Dowork()
{
Task task = Task.Factory.StartNew(() =>
{
int i = 0;
while (i < 10)
{
Thread.Sleep(1000);
UpdateUI(i.ToString());
i++;
}
});
}

The calling thread cannot access this object (rewrited but same error)

I have a MainFrame window with imageViewer control on it. Also there is my dll which calculates changes for the image all was working fine before I decided to add ProgressDialog.(( The Idea was - firstly I am loading the image via dll to main frame (this still OK). Then if user clicks button then show ProgressDialog and in worker.DoWork create new image via the same dllwrapper class (I am using "new")
All seems to be ok but when i am trying to set my currentImage property of imageviewer control (this is nothing more then setter for Image)it show me this error!
This is the code of my userButtonClickHandler from where I am launching ProgressDialog:
void OnThumbnailClick(object sender, RoutedEventArgs e)
{
pd = new ProgressDlg();
pd.Cancel += CancelProcess;
int max = 1000;
System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
LibWrap lwrap = new LibWrap();//!NEW instance for dll wrapper!
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//ERROR IS HERE!!!//The calling thread cannot access this object because a different thread owns it.
//what process??
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
};
worker.RunWorkerAsync();
pd.ShowDialog();
}
There is function from the same MainFrame class for canceling (There is OK too)
void CancelProcess(object sender, EventArgs e)
{
worker.CancelAsync();
}
This is class for ProgressDlg (it has nothing more then progress bar and cancel button):
public partial class ProgressDlg : Window
{
public ProgressDlg()
{
InitializeComponent();
}
public string ProgressText
{
set
{
this.lblProgress.Content = value;
}
}
public int ProgressValue
{
set
{
this.progress.Value = value;
}
}
public event EventHandler Cancel = delegate { };
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Cancel(sender, e);
}
}
}
I am working with this problem for (almost) two days and still couldn't find the solution. Help me please if you have an idea.
1 UPDATE
It seems to me that you was right about this threads - when I am trying to load previously loaded(initial) image (from the main thread) -it loads OK but if I am trying libWrap it fails due to processes conflict!
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
imageViewer.Width = 1000;//work!
imageViewer.CurrentImage = MyPrj.App.draggedImage;//Work!
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//Fail =(!
}
2 UPDATE
I have tried this construction OnThumbnailClick
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(() =>
{
imaeViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));
}
This caused same error/ Perhaps it will be correct to pass this value in MainThread (UI)? But I have no idea how.( I couldnot use serializers - becouse it is rapidly calling operation and this images are temporary/
WPF cannot alter items that were created on another thread.
So if you create an ImageViewer on one thread, you cannot alter it's properties on another thread.
Instead, use the Dispatcher, which is WPF's internal message queue for the main UI thread, to update your objects.
Or, use Henk's Answer to do your work on another thread, but return the result to the main thread so it can update your ImageViewer's properties
You need at least these changes:
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
args.Result = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{ ... } // handle error
else if (args.Cancelled)
{ ... } // handle Cancel
else
{
imageViewer.CurrentImage = args.Result;
}
pd.Close();
}
I'm not sure if it's enough but try again.
The imageViewer was created on the main thread of the application (which is appropriate because it is a UI control). UI controls can ONLY be accessed by the thread which created it, and that thread must have its own dispatcher (by which I mean message loop).
Remove the threading code, and it will work.
If you want this to popup the window and then show the image when the conversion completes, you will have to store the returned image in a variable until you return to the main thread, and then make the assignment to the imageViewer.

Categories

Resources