Hello I am trying to populate a progress bar but the ReportProgress call its not been executed for some reason.
Here is my code
//create status_Worker
status_Worker = new BackgroundWorker();
status_Worker.DoWork += new DoWorkEventHandler(Status_DoWork);
status_Worker.ProgressChanged += new ProgressChangedEventHandler(Worker_ProgressChanged);
status_Worker.WorkerReportsProgress = true;
status_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
private void Status_DoWork(object sender, DoWorkEventArgs e)
{
//make call to Logger class getStatus method
_logger.getStatus(sender);
}
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressbar1.Value = e.ProgressPercentage;
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
return;
}
else
{
Start_button.IsEnabled = true;
}
}
private void Start_button_Click(object sender, RoutedEventArgs e)
{
//initiate status_Worker when button is clicked
status_Worker.RunWorkerAsync();
Start_button.IsEnabled = false;
}
Now inside the Logger class I have the getStatus() method. i make a call to a local server to get status of the files been processed and all that works and I see the values been updated automatically on my MainWindow.Status.
public async Task getStatus(object sender)
{
BackgroundWorker statusWorker = (BackgroundWorker)sender;
//Making a call to ReportProgress here works and it shows the progress bar
//statusWorker.ReportProgress(99);
//REQUEST STATUS from a server
//Status format
//CurrentParser, NumberOfFilesToParse,CountOfCompletedFiles,Status, NumberOfProcessRunning
int CountOfCompletedFiles;
int NumberOfFilesToParse;
int percent;
string status = "Running";
string[] stats;
char[] delimiterChars = { ' ', ',', '.', ':', '\t' };
while(status!="Complete")
{
var getstatus = await request.GetStringAsync("http://localhost:8085/status");
logs.Add(getstatus);
stats = getstatus.Split(delimiterChars);
NumberOfFilesToParse = Int32.Parse(stats[1]);
CountOfCompletedFiles = Int32.Parse(stats[2]);
status = stats[3];
Thread.Sleep(1000);
MainWindow.main.Status = "Files to process: " + NumberOfFilesToParse + " Files completed: " + CountOfCompletedFiles + " Status: " + status;
if(NumberOfFilesToParse!=0 && status!="Complete")
{
percent = (CountOfCompletedFiles * 100) / NumberOfFilesToParse;
//a call to ReportProgress here stalls the program at this point
//statusWorker.ReportProgress(percent);
}
}
MainWindow.main.Status = "Completed!";
}
A call to ReportProgress at the start of the getStatus method works but a call to ReportProgress during or after my while loop results in process stalling at that point. Even when using static numbers ReportProgress(99) it only executes at the beginning
Your Status_DoWork method is doing fire-and-forget. It's calling an async Task method and then ignoring the Task it returns.
One of the problems you've run into is that BackgroundWorker simply doesn't work with async. What's actually happening is that as soon as the first await is reached in getStatus, it returns an incomplete Task to Status_DoWork, which then exits. This causes the BackgroundWorker to finish, so raising progress events no longer makes sense for that BackgroundWorker.
The modern replacement for BackgroundWorker is Task.Run, which includes support for progress reporting. Ideally, you would only use Task.Run for CPU-bound methods, not the I/O-bound methods:
private void Start_button_Click(object sender, RoutedEventArgs e)
{
Start_button.IsEnabled = false;
var progress = new Progress<int>(update => progressbar1.Value = update);
try
{
await _logger.getStatus(progress);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Start_button.IsEnabled = true;
}
}
public async Task getStatus(IProgress<int> progress)
{
int CountOfCompletedFiles;
int NumberOfFilesToParse;
int percent;
string status = "Running";
string[] stats;
char[] delimiterChars = { ' ', ',', '.', ':', '\t' };
while(status!="Complete")
{
var getstatus = await request.GetStringAsync("http://localhost:8085/status");
logs.Add(getstatus);
stats = getstatus.Split(delimiterChars);
NumberOfFilesToParse = Int32.Parse(stats[1]);
CountOfCompletedFiles = Int32.Parse(stats[2]);
status = stats[3];
await Task.Run(() => Thread.Sleep(1000)); // process file in Task.Run
MainWindow.main.Status = "Files to process: " + NumberOfFilesToParse + " Files completed: " + CountOfCompletedFiles + " Status: " + status;
if(NumberOfFilesToParse!=0 && status!="Complete")
{
percent = (CountOfCompletedFiles * 100) / NumberOfFilesToParse;
progress.Report(percent);
}
}
MainWindow.main.Status = "Completed!";
}
Related
I have a simple work to do, On button click it will get all Network IP addresses from a range, loop through them and put the active in a list. While performing the process a panel will be shown on which ip address will be displayed that is being checked. Code runs fine but form hangs up, application go to not responding and ip address not displayed even panel is not shown. how to do that?
my code is:
private void btnAutoSearch_Click(object sender, EventArgs e)
{
panel_Search.Visible = true; // Not Working
Cursor.Current = Cursors.WaitCursor;
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
{
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = p.Send(ip, 100);
if (pingresult.Status == IPStatus.Success)
{
lstIPs.Add(ip);
lblConnecting.Text = ip; // Not Working
}
}
GridConn.Rows.Clear();
foreach (string s in lstIPs)
{
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (obj.ConnectToHostServer())
{
obj.SendConnectionMessage();
obj.ReceiveConnectionMessage();
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
}
}
Cursor.Current = Cursors.Default;
panel_Search.Visible = false;
}
The issue is that the UI must be updated from the 'Main' thread (always called UI thread), but if you do other processing on this thread then the UI itself will lock up. So you should put long running process onto another thread.
This will cause the problem however that this thread cannot update the UI, as it is not the Main/UI thread. So then you have to invoke the UI thread yourself to update it.
Luckily there is simple way of doing this using the BackgroundWorker class in C# which can help you a lot. But you need to separate out your UI and background tasks.
//Define worker
BackgroundWorker myBGWorker;
//Initalise worker and start it from your button
private void btnAutoSearch_Click(object sender, EventArgs e)
{
panel_Search.Visible = true; // Not Working
Cursor.Current = Cursors.WaitCursor;
myBGWorker = new BackgroundWorker()
//This method will execute your processing on a background thread
myBGWorker.DoWork += new DoWorkEventHandler(bgw_DoWork);
//This method will execute when your background worker finishes
//It runs on the Main/UI thread
myBGWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
//This method will execute when the background thread wants to update the progress
//It runs on the Main/UI Thread
myBGWorker.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
//Tell it we will be reporting progress
myBGWorker.WorkerReportsProgress = true;
//Start!
myBGWorker.RunWorkerAsync()
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
{
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = p.Send(ip, 100);
if (pingresult.Status == IPStatus.Success)
{
lstIPs.Add(ip);
//Below reports the progress. The number shouuld represent the percentage of process, the object can be anything you want
double percentDone = (100.0 / 255.0) * i;
e.ReportProgress(Convert.ToInt32(percentDone), ip);
}
}
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lblConnecting.Text = e.UserState as string;
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
MessageBox.Show("Operation was canceled");
else if (e.Error != null)
MessageBox.Show(e.Error.Message);
else
{
GridConn.Rows.Clear();
foreach (string s in lstIPs)
{
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (obj.ConnectToHostServer())
{
obj.SendConnectionMessage();
obj.ReceiveConnectionMessage();
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
}
}
Cursor.Current = Cursors.Default;
panel_Search.Visible = false;
}
}
Desktop applications use one single thread to handle UI, and event handlers run synchronously on the UI thread, that is why form hangs, because it cannot handle other UI interactions while the event handler is running.
Best practice is that event handlers take UI thread for very short time.
When you need to perform long running tasks because of user interaction, you should perform the task in another thread, you should not use the UI thread for long running tasks.
You can use BackgroundWorker to move the task to another thread, but it is better to use asynchronous event handlers.
For example:
private async void myButton_Click(object sender, EventArgs e)
{
await PerformLongRunningTaskAsync();
//TODO: update UI after completing task
await Task.Run(() => PerformLongRunningTaskSynchronously());
//TODO: update UI after completing task;
}
private async Task PerformLongRunnigTaskAsync() {
//TODO: implement this async method
}
private void PerformLongRunningTaskSynchronously() {
//TODO: implement this synchronus method
}
Your code should be something like this:
private async void btnAutoSearch_Click(object sender, EventArgs e)
{
panel_Search.Visible = true; // Not Working
Cursor.Current = Cursors.WaitCursor;
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
{
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = await p.SendPingAsync(ip, 100);
if (pingresult.Status == IPStatus.Success)
{
lstIPs.Add(ip);
lblConnecting.Text = ip; // Not Working
}
}
GridConn.Rows.Clear();
foreach (string s in lstIPs)
{
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (await Task.Run(() => obj.ConnectToHostServer())
{
await Task.Run(() => obj.SendConnectionMessage());
await Task.Run(() => obj.ReceiveConnectionMessage());
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
}
}
Cursor.Current = Cursors.Default;
panel_Search.Visible = false;
}
Here is how i did the work thanks to Brian Rogers,
private void btnAutoSearch_Click(object sender, EventArgs e)
{
backgroundWorker1.WorkerSupportsCancellation = true;
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int j = 1; j <= 10; j++)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
Cursor.Current = Cursors.WaitCursor;
panel_Search.Location = new Point(380, 72);
// Perform a time consuming operation and report progress.
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
{
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = p.Send(ip, 100);
if (pingresult.Status == IPStatus.Success)
{
lstIPs.Add(ip);
lblConnecting.Text = ip;
//listBox1.Items.Add(ip);
}
}
GridConn.Rows.Clear();
foreach (string s in lstIPs)
{
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (obj.ConnectToHostServer())
{
obj.SendConnectionMessage();
obj.ReceiveConnectionMessage();
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
}
}
Cursor.Current = Cursors.Default;
panel_Search.Location = new Point(333, 252);
}
}
}
I try to call a function after thread finished but I can't .
I only can use while(threadName.isAlive) method before my function caller code , but it's not good because the program stops when i use this code . have you any idea ?
public partial class Form1 : Form
{
Thread myThread;
string myString = string.Empty;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
myThread = new Thread(write);
myThread.Start();
while (myThread.IsAlive) ;
textBox1.Text = myString;
}
public void write()
{
for (int i = 0; i < 10; i++) {
myString += "aaa " + i + "\r\n";
Thread.Sleep(1000);
}
}
}
If you must attach to a Thread rather than a Task then you can just start a task to wait for the thread to exit and then run some additional code, like this:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
static class Program
{
static void Main()
{
Thread thread = new Thread(work);
thread.Start();
Task.Run(() =>
{
thread.Join();
Console.WriteLine("Run after thread finished");
});
Console.ReadLine();
}
static void work()
{
Console.WriteLine("Starting work");
Thread.Sleep(1000);
Console.WriteLine("Finished work");
}
}
}
However, the modern way to approach this is to use Task, await and async.
For example:
async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "Awaiting task";
await writeAsync();
textBox1.Text = "Task finished";
}
Task writeAsync()
{
return Task.Run(() => write());
}
void write()
{
Thread.Sleep(10000);
}
If you try this second approach, you'll see that the UI remains responsive while the textbox says "Awaiting task".
Also note that normally you'd want to stop the user from being able to press the button again while the task is being awaited, to avoid multiple tasks being run. The easiest way to do that is to disable the button while the task is active like so:
async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
textBox1.Text = "Awaiting task";
await writeAsync();
textBox1.Text = "Task finished";
button1.Enabled = true;
}
Switch to Task from Thread and let .Net do the (low level) work for you:
public async Task<string> write() {
string myString = string.Empty;
for (int i = 0; i < 10; i++) {
myString += "aaa " + i + "\r\n";
await Task.Delay(1000);
}
return myString;
}
private async void button1_Click(object sender, EventArgs e) {
string result = await write();
// continue with (please, notice await) with assigning
textBox1.Text = result;
}
I am executing some .py scripts async. One Script takes about 30 seconds to be executed. It could happen that two or even more Scripts are being selected in a timespan of two or three seconds. The Goal is to have a Scheduler which collects all the tasks and executes them one after the other. A FIFO functionality should be included.
I 've tried the following Code just to try the functionality of the queuedTaskScheduler, but even that doesn't work.
QueuedTaskScheduler queueScheduler;
private TaskScheduler ts_priority1;
int pos = 0;
public Form1()
{
InitializeComponent();
queueScheduler = new QueuedTaskScheduler(targetScheduler: TaskScheduler.Default, maxConcurrencyLevel: 1);
ts_priority1 = queueScheduler.ActivateNewQueue(1);
}
private void button3_Click(object sender, EventArgs e)
{
QueueValue(pos, ts_priority1);
pos++;
}
private void button4_Click(object sender, EventArgs e)
{
changeString(pos);
pos++;
}
private void changeString (int position)
{
var bea = "This is Thread " + position + " starting";
MethodInvoker Labelupdate = delegate
{
label2.Text = bea;
};
Invoke(Labelupdate);
Thread.Sleep(3000);
bea = "Thread " + position + " is ending";
MethodInvoker Labelupdate1 = delegate
{
label2.Text = bea;
};
Invoke(Labelupdate1);
Thread.Sleep(1000);
}
private void updateLabel (string Lab)
{
MethodInvoker Labelupdate = delegate
{
label2.Text = Lab;
};
Invoke(Labelupdate);
}
private Task QueueTask(Func<Task> f, TaskScheduler ts)
{
return Task.Factory.StartNew(f, CancellationToken.None, TaskCreationOptions.HideScheduler | TaskCreationOptions.DenyChildAttach, ts);
}
private Task QueueValue(int position, TaskScheduler ts)
{
return QueueTask(async () =>
{
label2.Text = "This is Thread " + position + " starting";
Thread.Sleep(3000);
label2.Text = "Thread " + position + " is ending";
Thread.Sleep(1000);
}, ts);
}
I solved it. There is only need of a Semaphore. It is the same way as in this Thread
Here is the Code:
private static SemaphoreSlim semaphore = new SemaphoreSlim(1);
private Task QueueValue(int position, TaskScheduler ts)
{
return QueueTask(async () =>
{
await semaphore.WaitAsync();
try
{
var at = "This is Thread " + position + " starting";
updateLabel(at);
await Task.Delay(3000);
at = "Thread " + position + " is ending";
updateLabel(at);
await Task.Delay(1000);
}
finally
{
semaphore.Release();
}
}, ts);
}
Many thanks!
private bool ImportData()
{
bool result = false;
try
{
intdevid = int.Parse(cmbDeviceName.SelectedValue.ToString());
FetchDevicedata(intdevid);
//FTPTCompletedBatchTransfer();
FetchMaxReportId();
GetFTPFile(strDeviceIP, strDeviceUsername, strDevicePwd, strDevicePath + "//RunningBatch//RunningBatch.db", "RunningBatch.db"); // Copy RunningBatch.db to Debug Folder from Remote
LoadRunningData(); // Get Running Data in dataset from running.db
if (DecodeBatchData_R() == false)
{
MessageBox.Show("Running Batch Data Not Found");
}// save in batch master and row data table
GetFTPFile(strDeviceIP, strDeviceUsername, strDevicePwd, strDevicePath + "//CompletedBatch//CompletedBatch.db", "CompletedBatch.db");
LoadCompletedData();
if (DecodeBatchData() == false)
{
MessageBox.Show("Completed Batch Data not found");
}
result = true;
}
catch (Exception ex)\\here error:Cross-thread operation not valid: Control 'cmbDeviceName' accessed from a thread other than the thread it was created on.
{
clsLogs.LogError("Error: " + ex.Message + this.Name + " || ImportData");
result = false;
}
return result;
}
private void btnimport_Click(object sender, EventArgs e)
{
//////////////////copy checkweigher .db to database folder
dsCheckRptId = new DataSet();
///////////////////////////////////////////////////////////
if (cmbDeviceName.Text.ToString().Trim() == "--Select--")
{
MessageBox.Show("Please Select Proper Device");
cmbDeviceName.Focus();
return;
}
var deviceId = (int)cmbDeviceName.SelectedValue;
bgw.RunWorkerAsync(deviceId);
progressBar1.Visible = true;
label2.Visible = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
var deviceId = (int)e.Argument;
e.Result = ImportData();
System.Threading.Thread.Sleep(100);
bgw.ReportProgress(i);
}
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label2.Text = String.Format("Progress: {0} %", e.ProgressPercentage);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var result = (bool)e.Result;
if (cmbDeviceName.SelectedValue != null && cmbDeviceName.SelectedValue.ToString().Trim() != "0" && cmbDeviceName.SelectedValue.ToString().Trim() != "System.Data.DataRowView" && cmbDeviceName.SelectedValue.ToString().Trim() != "")
if (result)
{
MessageBox.Show("Data Import Completed Successfully for " + strDevicename);
clsLogs.LogEvent(3, "Data Import Completed Successfully for " + strDevicename);
}
else
{
MessageBox.Show("Data Import Fail For " + strDevicename);
clsLogs.LogEvent(3, "Data Import Fail for " + strDevicename);
}
progressBar1.Visible = false;
label2.Visible = false;
}
;When I run this background worker coding, there's an error stating "Cross-thread operation not valid: Control 'cmbDeviceName' accessed from a thread other than the thread it was created on. ."
How do I solve this problem guys?
WinForms controls are not thread safe, thus cross-thread operations on controls are not valid. You can access controls only from thread which created those controls. In your code you are accessing cmbDeviceName combobox from background thread. Best option to solve this is passing intdevid as RunWorkerAsync argument:
// executed on main thread
var deviceId = (int)cmbDeviceName.SelectedValue;
backgroundWorker.RunWorkerAsync(deviceId);
And get this argument in your DoWork handler:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// executed on background thread
var deviceId = (int)e.Argument;
// ...
}
Suggested reading: Safe, Simple Multithreading in Windows Forms
I divided my programme in 3 layers; GUI, BL, IO and tried to grap files from my server to my pc. I made it multi threaded and zorks fine, but when i tried to add a delegate to it for sending messages from my IO to my GUI, it troubels me. It said something like:
It is not allowed to perform an operation through various threads: it
was from another thread had access to the control label download
progress than the thread on which the element is created.
What i have is this:
GUI
private void buttonDownload_Click(object sender, EventArgs e)
{
download = new BL_DataTransfer(Wat.FILM, titel, this.downloadDel);
t = new Thread(new ThreadStart(download.DownLoadFiles));
t.Start();
}
private void UpdateDownloadLabel(string File)
{
labelDownloadProgress.Text = "Downloading: " + File;
}
BL
public void DownLoadFiles()
{
//bestanden zoeken op server
string map = BASEDIR + this.wat.ToString() + #"\" + this.titel + #"\";
string[] files = IO_DataTransfer.GrapFiles(map);
//pad omvormen
string[] downloadFiles = this.VeranderNaarDownLoadPad(files,this.titel);
IO_DataTransfer.DownloadFiles(#".\" + this.titel + #"\", files, downloadFiles, this.obserdelegate);
}
IO
public static void DownloadFiles(string map, string[] bestanden, string[] uploadPlaats, ObserverDelegate observerDelegete)
{
try
{
Directory.CreateDirectory(map);
for (int i = 0; i < bestanden.Count(); i++)
{
observerDelegete(bestanden[i]);
File.Copy(bestanden[i], uploadPlaats[i]);
}
}
catch (UnauthorizedAccessException uoe) { }
catch (FileNotFoundException fnfe) { }
catch (Exception e) { }
}
Delgate
public delegate void ObserverDelegate(string fileName);
Assuming that it's the update of the label that's failing you need to marshal the event onto the UI thread. To do this change your update code to be:
private void UpdateDownloadLabel(string File)
{
if (labelDownloadProgress.InvokeRequired)
{
labelDownloadProgress.Invoke(new Action(() =>
{
labelDownloadProgress.Text = "Downloading: " + File;
});
}
else
{
labelDownloadProgress.Text = "Downloading: " + File;
}
}
I've ended up creating an extension method for this that I can call - thus reducing the amount of repeated code in my applications:
public static void InvokeIfRequired(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
Which is then called like this:
private void UpdateDownloadLabel(string File)
{
this.labelDownloadProgress.InvokeIfRequired(() =>
labelDownloadProgress.Text = "Downloading: " + File);
}
if UpdateDownloadLabel function is in some control code-file, use pattern like this:
private void UpdateDownloadLabel(string File)
{
this.Invoke(new Action(()=> {
labelDownloadProgress.Text = "Downloading: " + File;
})));
}
You need to invoke assignment on UI thread in order to be able to change something on label.