Documentation
Best place to hear about BTM implementation details
Best place to hear about BTM implementation details
All example's sources are placed in VS 2010 Project folder.
Compiled version of examples - in Compiled/BasicThreadManagerSampleApp.exe.
Above window is automatically generated from WinAPI called TaskDialog. Check Program.cs file for details
Above image presents very similar window to window from Getting started section, but with additional features like:
WindowTitle
propertyThreadWorkerWindowClosed
eventIf you choose Cancel button (sorry for polish translation) or like an image Anuluj this task will be cancelled. Before that, task is paused and waiting for your move.
If you click minimize button, BTM will be hidden into tray. Check View.Trayable
property
You can execute below code to achieve this effect.
All comments and reviews are placed in code after //
(double slash) or multiline comments /* ... */
/* * Source from Program.cs file * Subproject: BasicThreadManagerSampleApp * Start line: 70 */ var sgt = new BasicThreadWorker(); sgt.Maximum = 100000; // Set maximum for counting sgt.View.Trayable = true; // Is BTM hides to tray after minimize window ? sgt.UseTaskbarProgressBar = true; // Is BTM show progress also in taskbar ? sgt.ThreadMethod = delegate { for (int i = 0; i < sgt.Maximum; i++) { // Our processor is too fast to see what is going on the screen so // we have to delay current thread about 1ms. System.Threading.Thread.Sleep(1); // If BTM is paused, loop start working... while (sgt.isPaused) { System.Threading.Thread.Sleep(100); } // Reporting progress to BTM is not mandatory, but if we have opportunity // to show progress in BTM window - it is. sgt.ReportProgress(i); // That's we updating BTM window title... sgt.WindowTitle = string.Format("Loading status {0}", i); } }; // This is one of many BTM's events - When you try to close BTM... sgt.ThreadWorkerWindowClosed += delegate(object o, ThreadWorkerWindowEventArgs args) { // Check if BTM is working while we clicking Closing button, if so... if (sgt.IsBusy) { // If paused already then unpause if (sgt.isPaused) { sgt.Resume(); } else { // Send pause signal to BTM - see line 21 sgt.Pause(); switch ( MessageBox.Show("You can continue this process by clicking Retry button or press Abort to cancel.", "Process has been stopped", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question)) { case DialogResult.Retry: sgt.Resume(); // Resume BTM working break; case DialogResult.Cancel: sgt.Cancel(); // Cancel BTM working break; } } } }; // At the end most important thing - Start BTM :) sgt.Start();
You can also show task completition in taskbar icon (WinAPI). Check UseTaskbarProgressBar
property
You can execute below code to achieve this effect.
All comments and reviews are placed in code after //
(double slash) or multiline comments /* ... */
/* * Source from Program.cs file * Subproject: BasicThreadManagerSampleApp * Start line: 123 */ var sgt = new BasicThreadWorker(); sgt.Maximum = 500; // Set maximum for counting sgt.UseTaskbarProgressBar = true; // Is BTM show progress also in taskbar ? // Before use it, add reference to Microsoft.WindowsAPICodePack.Shell.dll in project! sgt.TaskbarProgressBarState = TaskbarProgressBarState.Error; sgt.ThreadMethod = delegate { for (int i = 0; i < sgt.Maximum; i++) { System.Threading.Thread.Sleep(5); // Reporting progress to BTM is not mandatory, but if we have opportunity // to show progress in taskbar - it is. sgt.ReportProgress(i); } }; // At the end most important thing - Start BTM :) sgt.StartSimple();
For more Taskbar colors and effects check TaskbarProgressBarState
property
BTM might be used also for processing time-undefined task such as downloading remote file (http/ftp/etc.). Click one of this three links or enter your own remote file location.
After entering remote file location and click Down them all! button we have Preparing to download title...
...after 2 or 3 seconds later download begins...
You can execute below code to achieve this effect.
All comments and reviews are placed in code after //
(double slash) or multiline comments /* ... */
/* * Source from FileDownloader.cs file * Subproject: BasicThreadManagerSampleApp/Views * Start line: 22 */ // If remote file location address is empty cancel download... if (url.Text == string.Empty) return; var sgt = new BasicThreadWorker(); sgt.Maximum = 100; // Set maximum for counting sgt.PreloaderSpeed = 5; // Set default marquee speed sgt.WindowTitle = "Preparing to download..."; // Set initial window title sgt.View.Trayable = true; // Is BTM hides to tray after minimize window ? sgt.UseTaskbarProgressBar = true; // Is BTM show progress also in taskbar ? var sfd = new SaveFileDialog(); // Display Save Dialog window var ft = Path.GetExtension(url.Text); sfd.Filter = string.Format("File {0} ({1})|{1}|All Files (*.*)|*.*", ft.ToUpper(), ft); sfd.Title = "Save remote file on local disk"; sfd.ShowDialog(); if (sfd.FileName != string.Empty) { //sgt.Maximum = 100; sgt.ThreadMethod = delegate { var remoteFilename = string.Empty; // This is very important in all of multithreading stuff! // If you want to modify form control such as button, textbox etc.. you need do it with .Invoke // Without that this code fails cause of thread crossing problem. Check description under this code... url.Invoke( (MethodInvoker)delegate { remoteFilename = url.Text; }); // This is file downloading sections... // First check size of remote file, secondly download it... var req = WebRequest.Create(remoteFilename); req.Method = "HEAD"; using (WebResponse resp = req.GetResponse()) { int contentLength; if (int.TryParse(resp.Headers.Get("Content-Length"), out contentLength)) { // Set progressbar style sgt.ProgressBarStyle = ProgressBarStyle.Continuous; // Set BTM maximum to filesize in bytes sgt.Maximum = contentLength; int bytesProcessed = 0; Stream remoteStream = null; Stream localStream = null; WebResponse response = null; try { var request = WebRequest.Create(remoteFilename); response = request.GetResponse(); remoteStream = response.GetResponseStream(); localStream = File.Create(sfd.FileName); byte[] buffer = new byte[1024]; int bytesRead = 0; do { if (remoteStream != null) bytesRead = remoteStream.Read(buffer, 0, buffer.Length); localStream.Write(buffer, 0, bytesRead); bytesProcessed += bytesRead; if (remoteStream != null) { // Report progress... How many bytes are downloaded sgt.ReportProgress(bytesProcessed); // Autoupdate BTM window title sgt.WindowTitle = string.Format("{0}% - {1}", Math.Round(double.Parse((bytesProcessed*100/contentLength).ToString(CultureInfo.InvariantCulture)), 2), url.Text); } } while (bytesRead > 0); } catch (Exception er) { Console.WriteLine(er.Message); } finally { // Close all of opened resources... if (response != null) response.Close(); if (remoteStream != null) remoteStream.Close(); if (localStream != null) localStream.Close(); } } } }; #region = ThreadWorkerWindowClosed = sgt.ThreadWorkerWindowClosed += delegate(object o, ThreadWorkerWindowEventArgs args) { // Check if BTM is working while we clicking Closing button, if so... if (sgt.IsBusy) { // If paused already then unpause if (sgt.isPaused) { sgt.Resume(); } else { // Send pause signal to BTM sgt.Pause(); switch ( MessageBox.Show("You can continue this process by clicking Retry button or press Abort to cancel.", "Process has been stopped", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question)) { case DialogResult.Retry: sgt.Resume(); break; case DialogResult.Cancel: sgt.Cancel(); break; } } } }; #endregion // At the end most important thing - Start BTM :) sgt.Start();
If you have trouble with cross-threads, be sure you read section How to update GUI ?
This sample app shows how to parse and search through large files (CSV).
After lunching this app, BTM loads CSV file into memory (RAM) for further use by another methods. This process ends very fast. Probably you won't see anything but trust me - BTM works there.
After that you will see this window...
CSV file (sample) looks as shown below - Name, Surname and E-mail
If you type some text (name, surname or e-mail, regular expressions also) into this field and press Enter app will alive...
Whole searching process is showed in small progressbar on the right of this window, also we updating groupbox Searching status about new matches
If you click Show me all Students button you will se BTM window and do some new students parsing. After this process BTM display new window with ListView control which is filled by parsed students data
You can execute below code to achieve this effect.
All comments and reviews are placed in code after //
(double slash) or multiline comments /* ... */
/* * Source from PersonalFinder.cs file * Subproject: BasicThreadManagerSampleApp/Views * Start line: 107 */ loader_pgbar.Maximum = _personalData.Length; // Set maximum of mini-progressbar loader_pgbar.Style = ProgressBarStyle.Blocks; // Set look of mini-progressbar var sgt = new BasicThreadWorker(); sgt.Maximum = _personalData.Length; // Set maximum of BTM sgt.ThreadMethod = delegate { listView1.Invoke((MethodInvoker)delegate() { listView1.Items.Clear(); // Thread-safely clear previous results }); var i = 0; var matches_cnt = 0; foreach (string student in _personalData) { sgt.ReportProgress(i++); // Rise progress status up loader_pgbar.Invoke((MethodInvoker)delegate() { loader_pgbar.Value = i; }); // Change label above mini-progressbar loader_lbl.Invoke((MethodInvoker)delegate() { loader_lbl.Text = string.Format("Searching... {0}%", Math.Round((double.Parse((i*100/_personalData.Length).ToString(CultureInfo.InvariantCulture))), 2)); }); try { // Check by regular expression Match match = Regex.Match(student, textBox1.Text); if (match.Success) { label3.Invoke((MethodInvoker)delegate() { label3.ForeColor = Color.Green; }); matches_cnt++; // Thread-safely change title of groupbox groupBox1.Invoke((MethodInvoker)delegate() { groupBox1.Text = string.Format("Searching status - {0} matches", matches_cnt); }); try { string[] student_data = student.Split(';'); var lvi = new ListViewItem(); lvi.Text = student_data[0]; lvi.SubItems.Add(student_data[1]); lvi.SubItems.Add(student_data[2]); listView1.Invoke((MethodInvoker)delegate() { listView1.Items.Add(lvi); // Thread-safely add new ListView item }); } catch (Exception err) { Console.WriteLine("Parsing error " + student + Environment.NewLine + "Err: " + err.Message); } } else { label3.Invoke((MethodInvoker)delegate() { label3.ForeColor = Color.Red; }); } } catch (Exception err) { MessageBox.Show(err.Message, "An error has occured", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } label3.Invoke((MethodInvoker)delegate() { label3.Text = student; }); listView1.Invoke((MethodInvoker)delegate() { if(listView1.Items.Count > 2) listView1.Items[listView1.Items.Count - 1].EnsureVisible(); }); } }; // After BTM starts... sgt.ThreadWorkerStart += delegate(object sender, ThreadWorkerEventArgs args) { ShowLoader(); }; // After BTM ends sgt.ThreadWorkerEnd += delegate(object sender, ThreadWorkerEventArgs args) { HideLoader(); }; // Start BTM with non-windowed state sgt.StartSimple();
Many times you need to chain execution of your methods in particular order - trust me
The solution is Queue. Check below code.
/* * Source from Program.cs file * Subproject: BasicThreadManagerSampleApp * Start line: 153 */ var queue = new BasicThreadQueue(); queue.Add(ActionTest1); queue.Add(ActionTest2); queue.Add(ActionTest3); queue.Run();
Methods ActionTest1 to ActionTest3 have to be written in BTM style, eg.
private static void ActionTest1() { var sgt = new BasicThreadWorker(); sgt.Maximum = 1000; sgt.UseTaskbarProgressBar = true; sgt.Position = FormStartPosition.CenterScreen; // Before use it, add reference to Microsoft.WindowsAPICodePack.Shell.dll in project! sgt.TaskbarProgressBarState = TaskbarProgressBarState.Error; sgt.ThreadMethod = delegate { for (int i = 0; i < sgt.Maximum; i++) { System.Threading.Thread.Sleep(5); sgt.ReportProgress(i); sgt.WindowTitle = string.Format("Downloading the Internet to 3.5\" DDR - {0}", i); } }; sgt.Start(); }
This app shows you how to change many BTM propeties such as Progressbar color, style and so from while executing task
Check this code.
/* * Source from Program.cs file * Subproject: BasicThreadManagerSampleApp * Start line: 161 */ var sgt = new BasicThreadWorker(); sgt.Maximum = 5000; sgt.UseTaskbarProgressBar = true; sgt.Position = FormStartPosition.CenterScreen; // Before use it, add reference to Microsoft.WindowsAPICodePack.Shell.dll in project! sgt.TaskbarProgressBarState = TaskbarProgressBarState.Normal; sgt.ThreadMethod = delegate { for (int i = 0; i < sgt.Maximum; i++) { sgt.ReportProgress(i); if (i >= 1000 && i < 2000) { System.Threading.Thread.Sleep(5); sgt.WindowTitle = string.Format("Parsing file {0}.dll", i.ToString(CultureInfo.InvariantCulture).Md5().ToLower().Substring(0, 15)); sgt.ProgressBarStyle = ProgressBarStyle.Marquee; } else if (i >= 2000 && i < 4000) { System.Threading.Thread.Sleep(5); sgt.WindowTitle = string.Format("Installing filesystem... {0}%", sgt.View.prgBar.Value); sgt.ProgressBarStyle = ProgressBarStyle.Continuous; } else if (i >= 4000 && i < 5000) { System.Threading.Thread.Sleep(15); sgt.WindowTitle = string.Format("Compiling & installing file {0}.exe", i.ToString(CultureInfo.InvariantCulture).Md5().ToLower().Substring(0, 15)); sgt.ProgressBarStyle = ProgressBarStyle.Marquee; } else { System.Threading.Thread.Sleep(5); sgt.WindowTitle = string.Format("Downloading {0}%", sgt.View.prgBar.Value); sgt.ProgressBarStyle = ProgressBarStyle.Blocks; } } }; sgt.Start();
This is the most cool and big part of BTM sample app project. In this app you will able to build your own BTM with 100% of your look and feel. On the other hand you will check event names that you want to sing in and check when it's executed
Code of this app is located in BasicThreadManagerSampleApp/Views/Events.cs file.
This app look like this
Have you ever seen this guy ?
If so, you must read this section to get rid of exception
When making method calls to a control, if the caller is on a different thread than the one the control was created on, you need to call using Control.Invoke.
Solution is very simple. Every time when we need to modify eg. button from another thread like
// This section creates new thread... sgt.ThreadMethod = delegate { // THIS IS VERY BAD IDEA CAUSES EXCEPTION! mybutton.Text = "some title"; // Between this parenthasis { and } you have to call by .Invoke() // How to do this ? - scroll down... };
...we need to use .Invoke
// This section creates new thread... sgt.ThreadMethod = delegate { mybutton.Invoke( (MethodInvoker)delegate { mybutton.Text = "some title"; }); };
If you scared... - don't be! If you don't understand all of it - it's ok!
Most important thing is to use it in this way. I wrote code template. You will only have to apply it to your project. Check this out.
// This section creates new thread... sgt.ThreadMethod = delegate { <control_name>.Invoke( (MethodInvoker)delegate { // Now do whatever you want with <control_name> ! }); };
Ofcourse, when you modyfing control created in the same thread eg.
private void button1_Click(object sender, EventArgs e) { // Nice!, you do not have to use .Invoke() mybutton.Text = "some title"; }
There is no need to use .Invoke()
If we have situation:
button1_Click()
like abovechangeButtonText()
button1_Click()
method we lunching changeButtonText()
methodsgt.ThreadMethod = delegate { changeButtonText(); };
we lunching changeButtonText()
method
private void button1_Click(object sender, EventArgs e) { // This is called from the same thread with application... changeButtonText(); } // ...creating BTM... sgt.ThreadMethod = delegate { // This is called from another thread... changeButtonText(); }; sgt.Start(); private void changeButtonText() { mybutton.Invoke( (MethodInvoker)delegate { mybutton.Text = "some title"; }); }
You will see exception... why ? It's simple. If you have one method which is working for two another
methods (one - from the same thread, another - from new thread) you must check if .Invoke()
is required for example it's not if you calling this method from the same thread.
Above code needs to be
private void button1_Click(object sender, EventArgs e) { // This is called from the same thread with application... changeButtonText(); } // ...creating BTM... sgt.ThreadMethod = delegate { // This is called from another thread... changeButtonText(); }; sgt.Start(); private void changeButtonText() { // Check if .Invoke() is required if (mybutton.InvokeRequired) { mybutton.Invoke( (MethodInvoker)delegate { mybutton.Text = "some title"; }); } else { mybutton.Text = "some title"; } }
And that's all!
Name | Description | Default value | |
IsBusy | Get information about whether BTM is working or not | false; |
|
IsPaused | Get information about whether BTM is paused or not | false; |
|
Maximum | Get or Set maximum value for progressbar | 0; |
|
Minimum | Get or Set minimum value for progressbar | 0; |
|
Position | Get or Set value of BTM window position relative to screen. Available values
|
FormStartPosition.CenterScreen; |
|
PreloaderSpeed | Get or Set value of progressbar's marquee animation speed | 5; |
|
ProgressBarStyle | Get or Set style of BTM window progressbar | ProgressBarStyle.Blocks; |
|
TaskbarProgressBarState | Get or Set style of progressbar display in taskbar icon | TaskbarProgressBarState.NoProgress; |
|
ThreadMethod | Get or Set method for futher executing in another thread | NULL | |
ThreadName | Get or Set name of thread created by ThreadMethod | NULL | |
UseTaskbarProgressBar | Get or Set value whether progressbar will be displayed in taskbar icon | false; |
|
View | Get or Set BTM window form with progressbar | new PrimaryWnd(); |
|
WindowTitle | Get or Set BTM window title | string.Empty; |
|
View.Trayable | Get or Set value meaning is BTM window will minimize to tray after clicking minimize button or not | false; |
|
_bw | Contains instance of background worker | NULL | |
_isDisposed | Contains value indicating whether object removed from memory (disposed) | false; |
|
_isPaused | Contains value indication whether BTM is paused or not | false; |
|
_maxValue | Contains maximum value for progressbar | 0; |
|
_minValue | Contains minimum value for progressbar | 0; |
|
_pos | Contains value of position BTM window on the screen | FormStartPosition.CenterScreen; |
|
_preloaderSpeed | Contains preloader's speed value | 5; |
|
_taskbarProgressBarState | Contains value of progressbar style in taskbar icon | TaskbarProgressBarState.NoProgress; |
|
_threadMethod | Contains method for futher executing in another thread | NULL | |
_threadName | Contains name of thread created by _threadMethod | NULL | |
_useTaskbarProgressBar | Contains value whether progressbar will be displayed in taskbar icon | false; |
|
_view | Contains BTM window form with progressbar | new PrimaryWnd(); |
|
_windowsTaskbar | Contains instance on WinAPI TaskDialog object | NULL | |
_wndProgBarStyle | Contains style for BTM window progressbar | ProgressBarStyle.Blocks; |
|
_wndTitle | Contains actual BTM window title | string.Empty; |
Name | Description | Return value | |
BasicThreadWorker(string threadName) | Constructor with thread name parameter. If given, newly created threads will be named like threadName value | void | |
Cancel() | Cancel current task | void | |
Dispose | Clearing memory and destroy BTM object | void | |
OnThreadWorkerCancel(ThreadWorkerEventArgs e) | Virtual method connected with ThreadWorkerCancel event. Executed when BTM is cancelled. | void | |
OnThreadWorkerEnd(ThreadWorkerEventArgs e) | Virtual method connected with ThreadWorkerEnd event. Executed when BTM ends. | void | |
OnThreadWorkerError(ThreadWorkerEventArgs e) | Virtual method connected with ThreadWorkerError event. Executed when BTM has some errors. | void | |
OnThreadWorkerPause(ThreadWorkerEventArgs e) | Virtual method connected with ThreadWorkerPause event. Executed when BTM is paused. | void | |
OnThreadWorkerStart(ThreadWorkerEventArgs e) | Virtual method connected with ThreadWorkerStart event. Executed when BTM starts. | void | |
OnThreadWorkerWindowClosed(ThreadWorkerWindowEventArgs e) | Virtual method connected with ThreadWorkerWindowClosed event. Executed when BTM window closed. | void | |
OnThreadWorkerWindowMinimized(ThreadWorkerWindowEventArgs e) | Virtual method connected with ThreadWorkerWindowMinimized event. Executed when BTM minimized (clicked minimize button). | void | |
OnThreadWorkerWindowRestored(ThreadWorkerWindowEventArgs e) | Virtual method connected with ThreadWorkerWindowRestored event. Executed when BTM window is restored from minimize/tray state. | void | |
OnThreadWorkerWindowTrayed(ThreadWorkerWindowEventArgs e) | Virtual method connected with ThreadWorkerWindowTrayed event. Executed when BTM window has trayed. | void | |
OnThreadWorkerWindowUntrayed(ThreadWorkerWindowEventArgs e) | Virtual method connected with ThreadWorkerWindowUntrayed event. Executed when BTM restored from tray. | void | |
Pause() | Pause current thread execution | void | |
ReportProgress(int percent) | Gives to BTM information about current status | void | |
ReportProgress(int percent, Dictionary<string,string> param) | Gives to BTM information about current status and additional params | void | |
Resume() | When current thread is paused this method unpausing it | void | |
Start() | Starts BTM with windowed mode | void | |
StartSimple() | Starts BTM without windowed mode | void |
Name | Description | |
ThreadWorkerCancel | Occurs when BTM cancelled | |
ThreadWorkerEnd | Occurs when BTM ends | |
ThreadWorkerError | Occurs when BTM has some errors | |
ThreadWorkerPause | Occurs when BTM paused | |
ThreadWorkerStart | Occurs when BTM starts | |
ThreadWorkerWindowClosed | Occurs when BTM window closed | |
ThreadWorkerWindowMinimized | Occurs when BTM window minimized | |
ThreadWorkerWindowRestored | Occurs when BTM window restored from minimize/tray state | |
ThreadWorkerWindowTrayed | Occurs when BTM window trayed | |
ThreadWorkerWindowUntrayed | Occurs when BTM window restored from tray icon |