I'm writing a very simple bulk download program in c# that reads a .txt file of URLs to download. I've set it up with a global Thread and delegate for updating the GUI, and the pressing of the "Begin" button creates and starts that thread. What I want to do is have a "Pause" button that enables me to pause the download until I hit the "Resume" button. How do I do this?
The relevant code:
private Thread thr;
private delegate void UpdateProgressCallback(int curFile);
private void Begin_btn_Click(object sender, EventArgs e)
{
thr = new Thread(Download);
thr.Start();
}
private void Pause_btn_Click(object sender, EventArgs e)
{
Pause_btn.Visible = false;
Resume_btn.Visible = true;
//{PAUSE THREAD thr}
}
private void Resume_btn_Click(object sender, Eventargs e)
{
Pause_btn.Visible = true;
Resume_btn.Visible = false;
//{RESUME THREAD thr}
}
public void Download()
{
//Download code goes here
}
Obviously, I am NOT using a Worker, and I really don't wish to unless you can show me how to get it to work (I don't really understand workers). Any help would be appreciated.
If you use System.Net.WebClient.DownloadFile() or System.Net.WebClient.DownloadFileAsync() method then you cannot pause the download. The difference between these methods is that the latter method will start an asynchronous download so you will not need to create a separate thread yourself if you use this method. Unfortunately, downloads executed with either method cannot be paused or resumed.
You need to use System.Net.HttpWebRequest. Try something like this:
class Downloader
{
private const int chunkSize = 1024;
private bool doDownload = true;
private string url;
private string filename;
private Thread downloadThread;
public long FileSize
{
get;
private set;
}
public long Progress
{
get;
private set;
}
public Downloader(string Url, string Filename)
{
this.url = Url;
this.filename = Filename;
}
public void StartDownload()
{
Progress = 0;
FileSize = 0;
commenceDownload();
}
public void PauseDownload()
{
doDownload = false;
downloadThread.Join();
}
public void ResumeDownload()
{
doDownload = true;
commenceDownload();
}
private void commenceDownload()
{
downloadThread = new Thread(downloadWorker);
downloadThread.Start();
}
public void downloadWorker()
{
// Creates an HttpWebRequest with the specified URL.
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
FileMode filemode;
// For download resume
if (Progress == 0)
{
filemode = FileMode.CreateNew;
}
else
{
filemode = FileMode.Append;
myHttpWebRequest.AddRange(Progress);
}
// Set up a filestream to write the file
// Sends the HttpWebRequest and waits for the response.
using (FileStream fs = new FileStream(filename, filemode))
using (HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse())
{
// Gets the stream associated with the response.
Stream receiveStream = myHttpWebResponse.GetResponseStream();
FileSize = myHttpWebResponse.ContentLength;
byte[] read = new byte[chunkSize];
int count;
while ((count = receiveStream.Read(read, 0, chunkSize)) > 0 && doDownload)
{
fs.Write(read, 0, count);
count = receiveStream.Read(read, 0, chunkSize);
Progress += count;
}
}
}
}
I used some code from HttpWebRequest.GetResponse page on MSDN.
Instead of stopping the thread on Pause and starting a new one on Resume, you can also change the while loop to wait until download is resumed as following:
while ((count = receiveStream.Read(read, 0, chunkSize)) > 0)
{
fs.Write(read, 0, count);
count = receiveStream.Read(read, 0, chunkSize);
Progress += count;
while(!doDownload)
System.Threading.Thread.Sleep(100);
}
The up-side is that you may be able to re-use the same thread. The down-side is that the connection may timeout and become closed. In the latter case, you will need to detect this and re-connect.
You may also want to add an event for when the donwload is completed.
Related
I have followed https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/data/clipboard?view=net-maui-7.0 in my project, and I understand that:
Access to the clipboard must be done on the main user interface thread. For more information on how to invoke methods on the main user interface thread, see MainThread.
I cannot do Clipboard.Default.SetTextAsync(url) without errors, and I can't understand where I've done wrong.
The URL is generated through the OnCreated() method of FileSystemWatcher.
I understand this is not the UI thread, but I fire an event which I believe should enable UI thread operations
I would like to get this code below fixed so that I can my app in macOS without any issues.
You can also check the code in GitHub by pulling the following two projects:
https://github.com/McoreD/UploaderX/tree/maui
https://github.com/McoreD/HelpersLib.git
My code is as below.
using HelpersLib;
using Microsoft.Extensions.Logging;
using ShareX.HelpersLib;
using ShareX.UploadersLib;
namespace UploaderX;
public partial class MainPage : ContentPage
{
int count = 0;
private FileSystemWatcher _watcher;
private string _watchDir;
private string _destDir;
public delegate void UrlReceivedEventHandler(string url);
public event UrlReceivedEventHandler UrlReceived;
public MainPage()
{
InitializeComponent();
string AppDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "UploaderX");
string AppSettingsDir = Path.Combine(AppDir, "Settings");
App.Settings = ApplicationConfig.Load(Path.Combine(AppSettingsDir, "ApplicationConfig.json"));
App.UploadersConfig = UploadersConfig.Load(Path.Combine(AppSettingsDir, "UploadersConfig.json"));
App.UploadersConfig.SupportDPAPIEncryption = false;
DebugHelper.Init(Path.Combine(AppDir, $"UploaderX-{DateTime.Now.ToString("yyyyMMdd")}-Log.txt"));
_watchDir = Directory.Exists(App.Settings.CustomScreenshotsPath2) ? App.Settings.CustomScreenshotsPath2 : Path.Combine(AppDir, "Watch Folder");
Helpers.CreateDirectoryFromDirectoryPath(_watchDir);
_destDir = _watchDir;
DebugHelper.Logger.WriteLine("Watch Dir: " + _watchDir);
DebugHelper.Logger.WriteLine("Destination Dir: " + _destDir);
_watcher = new FileSystemWatcher();
_watcher.Path = _watchDir;
_watcher.NotifyFilter = NotifyFilters.FileName;
_watcher.Created += OnCreated;
_watcher.EnableRaisingEvents = true;
this.UrlReceived += MainPage_UrlReceived;
}
private async void MainPage_UrlReceived(string url)
{
await Clipboard.Default.SetTextAsync(url);
}
private void OnUrlReceived(string url)
{
UrlReceived?.Invoke(url);
}
private async void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
await Clipboard.Default.SetTextAsync(CounterBtn.Text);
}
async void OnCreated(object sender, FileSystemEventArgs e)
{
try
{
string fileName = new NameParser(NameParserType.FileName).Parse("%y%mo%d_%ra{10}") + Path.GetExtension(e.FullPath);
string destPath = Path.Combine(Path.Combine(Path.Combine(_destDir, DateTime.Now.ToString("yyyy")), DateTime.Now.ToString("yyyy-MM")), fileName);
FileHelpers.CreateDirectoryFromFilePath(destPath);
if (!Path.GetFileName(e.FullPath).StartsWith("."))
{
int successCount = 0;
long previousSize = -1;
await Helpers.WaitWhileAsync(() =>
{
if (!FileHelpers.IsFileLocked(e.FullPath))
{
long currentSize = FileHelpers.GetFileSize(e.FullPath);
if (currentSize > 0 && currentSize == previousSize)
{
successCount++;
}
previousSize = currentSize;
return successCount < 4;
}
previousSize = -1;
return true;
}, 250, 5000, () =>
{
File.Move(e.FullPath, destPath, overwrite: true);
}, 1000);
WorkerTask task = new WorkerTask(destPath);
UploadResult result = task.UploadFile();
DebugHelper.Logger.WriteLine(result.URL);
OnUrlReceived(result.URL);
}
}
catch (Exception ex)
{
DebugHelper.Logger.WriteLine(ex.Message);
}
}
}
Error once URL is generated:
You can wrap your call to SetTextAsync() with MainThread.BeginInvokeOnMainThread() like so to make sure it's invoked correctly:
private void MainPage_UrlReceived(string url)
{
MainThread.BeginInvokeOnMainThread(() =>
{
Clipboard.Default.SetTextAsync(url);
});
}
There is no need to await the call to SetTextAsync(), either, because you're calling it as a singular operation from within an event handler, which isn't awaitable.
I understand this is not the UI thread, but I fire an event which I believe should enable UI thread operations
Generally, there is no guarantee that event handlers of UI classes are called on the Main Thread.
You can find more information on when and how to invoke methods on the Main Thread in the official documentation: https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/main-thread?view=net-maui-7.0
I wrote the simplified version of my program below. Process A launches a child process (Process B). I use an anonymous pipe to write information about the progress of a method running on process B. Meanwhile I have a function in process A that continually reads from a stream to see if there is a new update coming in from the pipe. If there is, the form on process A is updated to reflect the progress. This works as expected, however I am wondering if there is a better way to accomplish this without having to continually check the stream to see if there are any new updates to the progress.
/////////////////
///Process A ////
/////////////////
public void LaunchProcessB()
{
using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.In,
HandleInheritability.Inheritable))
{
var _Process = new Process();
_Process.StartInfo.FileName = exeString;
_Process.StartInfo.Arguments = pipeServer.GetClientHandleAsString()
_Process.StartInfo.RedirectStandardOutput = true;
_Process.StartInfo.RedirectStandardInput = true;
_Process.StartInfo.CreateNoWindow = true;
_Process.StartInfo.UseShellExecute = false;
_Process.Start(); //launches process B
pipeServer.DisposeLocalCopyOfClientHandle();
using (StreamReader sr = new StreamReader(pipeServer))
{
try
{
while (true)
{
string temp = sr.ReadLine();
if (temp == null) break;
int result;
if (Int32.TryParse(temp, out result))
ShowDocumentProgress(result);
else ShowProgress(temp);
}
}
catch (Exception)
{
//error occured when reading from stream.
}
}
if (!_Process.Responding && !_Process.HasExited)
{
_Process.Kill();
return;
}
_Process.WaitForExit(10000);
}
}
private void ShowProgressPercent(int percentage)
{
if (percentage > currentPercentage)
{
progressBar.Value = percentage;
}
}
private void ShowProgress(string progressString)
{
labelMessage.Text = progressString;
}
/////////////////
///Process B ////
/////////////////
private StreamWriter _progressWriter;
private PipeStream _progressPipe;
static int Main(string[] args)
{
using (progressPipe = new AnonymousPipeClientStream(PipeDirection.Out, args[0]))
using (_progressWriter = new StreamWriter(_progressPipe))
{
RunLongProcess()
}
}
private void RunLongProcess()
{
//attaches events to PercentProgress and StageProgress methods.
}
private void PercentProgress(int percentage)
{
_progressWriter.WriteLine(percentage.ToString());
_progressPipe.WaitForPipeDrain();
}
private void StageProgress(string stage)
{
_progressWriter.WriteLine(stage);
_progressPipe.WaitForPipeDrain();
}
The while condition is not necessary. Simply read until temp is null. That's the end signal of the stream.
Make this a while(true) loop.
I think you also need to add exception handling to catch the process terminating and severing the pipe. !_Process.HasExited && pipeServer.IsConnected is not enough because it might be true but immediately switch to false after the test.
I also would add a WaitForExit at the end to make sure the system is quiesced before you continue.
I'm currently working on a program that converts a list of files from .ps (PostScript) to .png.
Originally, this was done in a batch file, one file at a time. I am working on code that uses the Ghostscript.NET dll to process these files asynchronously. By splitting these up into tasks, I have cut down the processing time from 30 minutes to about 6 minutes.
I want to be able to show the user some sort of progress on this, so that it doesn't just look like my program is frozen.
I know just enough about threading to frustrate myself, so any suggestions on the best way to do this is greatly appreciated. The code below has a BackgroundWorker implemented to try to show the progress. I have used BGWorker before to show progress, but not on multiple tasks like this. In fact, this is my first time multi-threading without just using BGWorker.
I feel that BGWorker is probably not what I need to be using, but I wanted to try to take a stab at it myself before I asked.
Here is the code that I have so far:
public partial class ProcessStatusForm : Form
{
public string[] testList;
public string wordPath;
public string StatusText;
public GhostscriptVersionInfo _gs_version_info;
public DirectoryInfo dInfo;
public List<Task> tasks;
public float NumberOfTasks;
public bool PS2PNGRunning;
public int ProgressPct;
public float dPercent;
public decimal decPercent;
public ProcessStatusForm(string wordDoc, List<string> runList)
{
InitializeComponent();
this.wordPath = wordDoc;
this.testList = runList.ToArray();
this.StatusText = string.Empty;
this._gs_version_info = GhostscriptVersionInfo.GetLastInstalledVersion(GhostscriptLicense.GPL |
GhostscriptLicense.AFPL, GhostscriptLicense.GPL);
this.dInfo = new DirectoryInfo(SettingsClass.PSFolder);
this.PS2PNGRunning = false;
this.ProgressPct = 0;
this.NumberOfTasks = runList.Count;
}
private void ProcessStatusForm_Shown(object sender, EventArgs e)
{
//Spawn tasks for each of the .ps files in the PS_FILES folder
tasks = new List<Task>(dInfo.GetFiles("*.ps").Length);
//Start the BackgroundWorker
this.PS2PNGRunning = true;
BackgroundWorker.RunWorkerAsync();
foreach (var file in dInfo.GetFiles("*.ps"))
{
//Get fileName to pass fo the ConvertPS2PNG
string inputFile = file.Name;
//Create the Task
var task = Task.Factory.StartNew(() => ConvertPS2PNG(inputFile));
tasks.Add(task);
}
//Wait until all tasks have completed
Task.WaitAll(tasks.ToArray());
PS2PNGRunning = false;
}
private void ConvertPS2PNG(string input)
{
string output = input.Replace(".ps", "_01.png");
input = SettingsClass.PSFolder + input;
output = SettingsClass.PNGFolder + output;
GhostscriptProcessor processor = new GhostscriptProcessor(_gs_version_info, true);
processor.Process(CreateGSArgs(input, output), new ConsoleStdIO(true, true, true));
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
ProgressPct = 0;
while (PS2PNGRunning)
{
Thread.Sleep(1000);
float TasksCompleted = 0;
foreach (var tsk in tasks)
{
if (tsk.Status == TaskStatus.RanToCompletion)
{
TasksCompleted++;
}
}
StatusText = TasksCompleted + " of " + NumberOfTasks + " converted...";
dPercent = TasksCompleted / NumberOfTasks;
dPercent *= 100;
decPercent = (decimal)dPercent;
decPercent = Math.Round(decPercent);
ProgressPct = (int)decPercent;
BackgroundWorker.ReportProgress(ProgressPct);
}
BackgroundWorker.ReportProgress(100);
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.ProgressLabel.Text = this.StatusText;
this.progressBar.Style = ProgressBarStyle.Continuous;
this.progressBar.Value = e.ProgressPercentage;
}
public string[] CreateGSArgs(string inPath, string outPath)
{
List<string> gsArgs = new List<string>();
gsArgs.Add("-dBATCH");
gsArgs.Add("-dNOPAUSE");
gsArgs.Add("-sDEVICE=png16m");
gsArgs.Add("-dQUIET");
gsArgs.Add("-sPAPERSIZE=letter");
gsArgs.Add("-r800");
gsArgs.Add("-sOutputFile=" + outPath);
gsArgs.Add(inPath);
return gsArgs.ToArray();
}
}
When I put breaks in the code of BackgroundWorker_DoWork, everything seems to be coming out right, but when it gets to the BackgroundWorker.ReportProgress(), it never makes it to the BackgroundWorker_ProgressChanged() method.
At the very least, I could live with just having a progressBar.Style as marquee while this is running so that the user can see that the program is working, but reporting the actual progress would be ideal.
As I said before, I haven't done a ton of work with threading, and all of my knowledge on the subject pretty much comes from Google and StackOverflow. If there is a completely different way to do this, I am open to all criticism.
Was the name BackgroundWorker given to the object when you dragged it from the designer screen? If not change your code to use the appropriate name it was given (default should have been backgroundWorker1).
Or...
Try casting the sender object to a BackgroundWorker object in your DoWork method and call ReportProgress() from there.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
bw.ReportProgress(25);
}
}
This is the code I use to record an audio file:
internal class AudioRecorder
{
public WaveIn waveSource = null;
public WaveFileWriter waveFile = null;
public string RECORDING_PATH;
public AudioRecorder(string fileName)
{
RECORDING_PATH = fileName;
}
public void Start()
{
waveSource = new WaveIn();
waveSource.WaveFormat = new WaveFormat(44100, 1);
waveSource.DeviceNumber = 0;
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);
System.Timers.Timer t = new System.Timers.Timer(30000);
t.Elapsed += new ElapsedEventHandler(Stop);
waveSource.StartRecording();
t.Start();
}
private void Stop(object sender, ElapsedEventArgs args)
{
waveSource.StopRecording();
}
private void waveSource_DataAvailable(object sender, WaveInEventArgs e)
{
if (waveFile != null)
{
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
}
}
private void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
{
if (waveSource != null)
{
waveSource.Dispose();
waveSource = null;
}
if (waveFile != null)
{
waveFile.Dispose();
waveFile = null;
}
}
}
In the main method I do:
AudioRecorder r = new AudioRecorder(dialog.FileName);
r.Start();
FileInfo file = new FileInfo(r.RECORDING_PATH);
// Do somehting with the recorded audio //
The problem is that when I do r.Start() the thread does not block and keeps running. So I get a corrupt file error. When I try things like Thread.Sleep to keep the thread waiting until recording finishes, this time the AudioRecorder code does not work well (i.e. recording never finishes).
Any ideas about what should I do to correctly wait the recording to finish so that I can safely use the recorded file ?
If you want to record for 30 seconds exactly, just call StopRecording in the DataAvailable event handler once you have enough data. There is absolutely no need for a complicated threading strategy. I do exactly this in the open source .NET voice recorder application.
Dispose the WaveFileWriter in the RecordingStopped event.
If you absolutely must have a blocking call, then use WaveInEvent, and wait on an event which is set in the RecordingStopped handler, as suggested by Rene. By using WaveInEvent, you remove the need for windows message pump to be operational.
You use a ManualResetEvent to wait for the Stop event to be called, giving other threads a change to proceed.
I've only added the new bits...
internal class AudioRecorder
{
private ManualResetEvent mre = new ManualResetEvent(false);
public void Start()
{
t.Start();
while (!mre.WaitOne(200))
{
// NAudio requires the windows message pump to be operational
// this works but you better raise an event
Application.DoEvents();
}
}
private void Stop(object sender, ElapsedEventArgs args)
{
// better: raise an event from here!
waveSource.StopRecording();
}
private void waveSource_RecordingStopped(object sender, EventArgs e)
{
/// ... your code here
mre.Set(); // signal thread we're done!
}
It is good idea to avoid any multi-threaded code if it is not required and Mark's answer is explaining this perfectly.
However, if you are writing a windows application and the requirement is to record 30 seconds than it is a must not to block a main thread in waiting (for 30 seconds). The new async C# feature can be very handy here. It will allow you to keep code logic straightforward and implement waiting in a very efficient way.
I have modified your code slightly to show how the async feature can be used in this case.
Here is the Record method:
public async Task RecordFixedTime(TimeSpan span)
{
waveSource = new WaveIn {WaveFormat = new WaveFormat(44100, 1), DeviceNumber = 0};
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);
waveSource.StartRecording();
await Task.Delay(span);
waveSource.StopRecording();
}
Example of using Record from click handler of WPF app:
private async void btnRecord_Click(object sender, RoutedEventArgs e)
{
try
{
btnRecord.IsEnabled = false;
var fileName = Path.GetTempFileName() + ".wav";
var recorder = new AudioRecorder(fileName);
await recorder.RecordFixedTime(TimeSpan.FromSeconds(5));
Process.Start(fileName);
}
finally
{
btnRecord.IsEnabled = true;
}
}
However, you have to watch out for timing here. Task.Delay does not guarantee that it will continue execution after the exact specified time span. You might get records slightly longer than is required.
I am programming an application for downloading articles from an SQL Database on the internet. I have programmed the Website for managing the articles. Now I'm downloading the article List in gzip Format and then I decompress them to a xml-File. When I'm done I want to insert the articles to the mobile phone. This works great. Now I want to add an progress Bar to see the state of the insertion. I tried with Threading but this doesn't work. I'm posting some pieces of code from my application and also the progressUpdate methods.
private void btn_send_Click(object sender, EventArgs e)
{
label1.Text = "Download started";
string ArticlesURL = "URLTOSITE";
InvokeAsync(ArticlesURL);
}
private void InvokeAsync(string URL)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.AllowWriteStreamBuffering = true;
allDone.Reset();
request.BeginGetRequestStream(new AsyncCallback(ReadArticlesCallback), request);
allDone.WaitOne();
request.BeginGetResponse(new AsyncCallback(ResponseArticlesCallback), request);
}
private static void ReadArticlesCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
//End the operation.
Stream postSream = request.EndGetRequestStream(asynchronousResult);
string postData = "articles=test";
//Convert the string into a byte array.
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
//Write to the request stream.
postSream.Write(byteArray, 0, postData.Length);
postSream.Close();
allDone.Set();
}
private static void ResponseArticlesCallback(IAsyncResult asynchronousResult)
{
Form1 f = new Form1();
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse resp = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = resp.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
nbrArticles = Convert.ToInt16(responseString);
// Close the stream object.
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse.
resp.Close();
f.truncate_articles();
f.get_articles();
}
private void get_articles()
{
string url = "URLTOSITE";
int startPoint = 0;
DownloadZipFile((object)startPoint, url);
DecompressFile();
getXmlAndInsertInDB();
}
private void getXmlAndInsertInDB()
{
int total = nbrArticles;
int count = total / 100; //How much articles are 1 percent
int i = 0;
String barcode = "";
String name = "";
bool state = false;
XmlTextReader reader = new XmlTextReader("Program Files\\SmartDeviceProject1\\articles.xml");
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element: //The node is an element
while (reader.MoveToNextAttribute()) //Get the attributes like barcode, lastname, firstname, pincode
switch (reader.Name)
{
case "barcode":
barcode = reader.Value.ToString();
state = false;
break;
case "name":
name = reader.Value.ToString();
state = true;
break;
}
break;
}
if (state == true)
{
cmd.CommandText = "INSERT INTO articles(barcode, name) " +
"VALUES('" + barcode + "','" + name + "');";
cmd.ExecuteNonQuery();
state = false;
i++;
if (i == count)
{
Thread t = new Thread(new ThreadStart(this.incrementProgressBar));
t.Start();
//incrementProgressBar();
i = 0;
}
}
}
reader.Close();
}
private void updateProgressBarMethod(int progress)
{
if (progressBar1.InvokeRequired)
{
//It was called from a non UI thread so we create a delegate
//and have the UI Thread call this method again
UpdateProgressBar = new UpdateProgressBarDelegate(updateProgressBarMethod);
this.Invoke(UpdateProgressBar, progress);
}
else
{
//Called from the UI Thread OK to update
//update your progress bar here
progressBar1.Value += progress;
}
}
private void incrementProgressBar()
{
//Call the method to update progress Bar on UI thread
//we do not need a delegate here that will be taken care of
//in the method
updateProgressBarMethod(1);
Application.DoEvents();
}
I think the problem is that I am using Callbacks. I have read that the Callbacks are also starting Threads. So I think the problem is there but I can't solve it.
I've found another very good site for threading with mobile applications: Updating the User Interface from a Worker Thread
Now with the new code, the debugger stops always at the same piece of code without any notification or exception :( Here is my new code:
if (i == count)
{
this.info_percent = "Synchro " + step.ToString() + "%";
this.Invoke(new EventHandler(WorkerUpdate)); //The Debugger stops here!
i = 0;
step++;
Thread.Sleep(700);
}
public void WorkerUpdate(object sender, EventArgs e)
{
this.lbl_percent.Text = this.info_percent;
this.lbl_percent.Update();
this.progressBar1.Value = step;
this.progressBar1.Update();
}
The Debugger stops at: this.Invoke(new EventHandler(WorkerUpdate));
I would suggest using the Background worker class. I had a similar problem and implemented the Background worker and it fixed my problem. Hopefully it will fix yours also
http://www.dotnetperls.com/backgroundworker
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
I found another thread discussing this and thought it would help:
Is there a BackgroundWorker replacement for .NET Compact Framework 3.5?
Your code will always hang at this point:
if (i == count)
{
this.info_percent = "Synchro " + step.ToString() + "%";
this.Invoke(new EventHandler(WorkerUpdate)); //The Debugger stops here!
i = 0;
step++;
Thread.Sleep(700);
}
Make these changes:
public delegate void MethodInvoker(); // this is not defined in CF
if (i == count)
{
this.info_percent = "Synchro " + step.ToString() + "%";
object sender = null; // make this whatever you want/need
EventArgs e = new EventArgs();
if (this.InvokeRequired) {
MethodInvoker mi = delegate { WorkerUpdate(sender, e); } };
this.BeginInvoke(mi);
} else {
WorkerUpdate(sender, e);
}
i = 0;
step++;
// Thread.Sleep(700); Why is this here?
}
This should prevent those obnoxious freezes.