Reputation: 39
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);
Upvotes: 0
Views: 659
Reputation: 137148
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);
}
Upvotes: 1
Reputation: 62246
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.
Upvotes: 0