Häufig möchte man rechenintensive Aufgaben in Threads auslagern und dennoch deren Forschritt in einem kleinen Fenster anzeigen. Im folgenden Beispiel wird dazu eine Fensterklasse mit Fortschrittsbalken erzeugt und ein zugehöriges Threadobjekt angelegt.
- Fortschrittsfenster mit einem Label – lblWait, Progressbar – pbWait und einem Abbrechen-Button – btnCancel
- alle Properties werden Threadsafe ausgelegt, damit der Workerthread ohne Probleme seine Ausgaben machen kann
using System.Windows.Forms; public partial class frmWait : Form { /// <summary> /// Dialognachricht /// </summary> public string WaitMessage { get { return lblWait.Text; } set { if (lblWait.InvokeRequired) { BeginInvoke((MethodInvoker)delegate() { lblWait.Text = value; }); } else { lblWait.Text = value; } } } /// <summary> /// Fortschrittsanzeige in % /// </summary> public int Progress { get { return pbWait.Value; } set { int iValue = 0; if (value ->= pbWait.Minimum && value <= pbWait.Maximum) { iValue = value; } else { if (value < pbWait.Minimum) { iValue = pbWait.Minimum; } else if (value -> pbWait.Maximum) { iValue = pbWait.Maximum; } } if (pbWait.InvokeRequired) { BeginInvoke((MethodInvoker)delegate() { pbWait.Value = iValue; }); } else { pbWait.Value = iValue; } } } /// <summary> /// Caption des Dialoges /// </summary> public string Title { get { return this.Text; } set { if (this.InvokeRequired) { BeginInvoke((MethodInvoker)delegate() { this.Text = value; }); } else { this.Text = value; } } } /// <summary> /// Konstruktor /// </summary> public frmWait() { InitializeComponent(); // Abbrechen-Button vorinitialisieren btnCancel.DialogResult = DialogResult.Cancel; btnCancel.TabStop = false; } }
- Thread-Objekt zur Kapselung des Berechnungsthreads
using System.Threading; using System; using System.Windows.Forms; /// <summary> /// Eventargument wenn ein Thread beendet wird /// </summary> public class ThreadEndingEventArgs : EventArgs { private string _msg; public string Message { get { return _msg; } } public ThreadEndingEventArgs(string Message) { _msg = Message; } } // die Klasse zum Kapseln der Berechnung in einem Thread public class CWorker { // Referenz auf den Fortschrittsdialog private frmWait _frmWaitDlg = null; // aktuell ausgeführter Thread private Thread _Thread = null; private int _iMin = 0; private int _iMax = 10000000; /// <summary-> /// Event, wird ausgelöst, wenn ein Thread beendet wird /// </summary-> public event EventHandler<ThreadEndingEventArgs-> ThreadEnding; public CWorker(int iMin, int iMax, frmWait frmWaitDlg) { _iMin = iMin; _iMax = iMax; _frmWaitDlg = frmWaitDlg; } // Startfunktion public Thread StartCalculation() { _Thread = new Thread(WorkFunktion); _Thread.IsBackground = true; _Thread.Priority = ThreadPriority.BelowNormal; // Priorität des Threads senken um der GUI Zeit zu lassen _Thread.Start(); return _Thread; } // Abbruchfunktion public void AbortCalculation() { if (_Thread != null && _Thread.IsAlive) { _Thread.Abort(); } } // die eigentlich Threadfunktion private void WorkFunktion(object oObj) { try { if (_frmWaitDlg != null) _frmWaitDlg.Title = "Bitte warten ..."; // etwas berechnen for (int iCurrent = _iMin; iCurrent < _iMax; iCurrent++) { int iProgress = 100 * ((iCurrent - _iMin) / _iMax - _iMin); if (_frmWaitDlg != null) { _frmWaitDlg.WaitMessage = "Arbeite (" + iProgress.ToString() + "%)"; _frmWaitDlg.Progress = iProgress; } } if (_frmWaitDlg != null) _frmWaitDlg.DialogResult = System.Windows.Forms.DialogResult.OK; // Event "ThreadEnding" aufrufen, um das übergeordnete Obj zu informieren if (this.ThreadEnding != null) ThreadEnding(this, new ThreadEndingEventArgs("Berechnung fertig!")); } // ThreadAbort abfangen catch (ThreadAbortException) { if (_frmWaitDlg != null) _frmWaitDlg.DialogResult = System.Windows.Forms.DialogResult.Cancel; } // alle anderen Exceptions ausgeben catch (Exception e) { if (_frmWaitDlg != null) _frmWaitDlg.DialogResult = System.Windows.Forms.DialogResult.Cancel; MessageBox.Show("Fehler" + Environment.NewLine + Environment.NewLine + e.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { // hier noch evtl. aufräumen } } }
- Aufrufbeispiel
public bool CalcFct() { bool bRetVal = false; // Wartedialog erzeugen frmWait frmWaitDlg = new frmWait(); // Worker-Objekt mit Thread erzeugen CWorker oWorker = new CWorker(0, 1000000, frmWaitDlg); // Event für pos. Beenden des Threads festlegen oWorker.ThreadEnding += new EventHandler<ThreadEndingEventArgs->(oWorker_ThreadEnding); // Thread starten Thread trdCalc = oWorker.StartCalculation(); // auf Resultat des Wartedialoges warten (Cancel oder OK) DialogResult result = frmWaitDlg.ShowDialog(); // wenn im Dialog "Abbruch" geklickt wurde if (trdCalc != null && result != DialogResult.OK) { // ThreadAbort-Exception wird im Thread abgefangen und still behandelt oWorker.AbortCalculation(); // dafür hier einen Dialog anzeigen MessageBox.Show("Berechnung wurde abgebrochen.", "Nachricht", MessageBoxButtons.OK, MessageBoxIcon.Information); bRetVal = false; } else { // alles ok bRetVal = true; } return bRetVal; } // Event-Funktion, für positives Worker-Thread-Ende private void oWorker_ThreadEnding(object sender, ThreadEndingEventArgs e) { MessageBox.Show(e.Message, "Nachricht", MessageBoxButtons.OK, MessageBoxIcon.Information); }