This chapter of the tutorial deals with progress dialogs. A progress dialog is a window that displays the current progress of a time-consuming operation.
To display a progress dialog with the Gtk# binding of the Two-Layered GUI Toolkit, the TwoLayeredGUI.Gtk.ProgressDialog
class can be used. Its most straightforward method is ShowDialog
, which has a variety of strongly and weakly typed overloads.
Most of these overloads expect a delegate that performs the time-consuming work. This delegate will be executed in a background worker thread. It will be supplied with a controller object that allows thread-safe communication with the progress indicator. Here is an exemplary worker method:
private static void TakeSomeTime(IProgressDialogController controller, int number) { for (int i = 0; i < number; i++) { controller.Progress = Convert.ToInt32((double)i / number * 100); System.Threading.Thread.Sleep(100); } }
For the purpose of this tutorial, the worker method does not really work all that much, instead it takes a nap for a certain amount of milliseconds every now and then, while counting from zero to the number specified in the last parameter.
As you see, the first parameter is the aforementioned controller object of type IProgressDialogController
. The second parameter accepts a custom value that we can use in our lengthy computation, in this case an int
value. This latter value can be passed to ShowDialog
when we display the progress window. We can choose the type ourselves, and if we had used a reference type instead of int
, we could actually use this to return the result of the computation in a thread-safe manner.
The controller object is used only once: For every step, its Progress
property is updated to reflect the current progress, expressed as a percentage.
Now that the worker method has been defined, showing the progress dialog is very easy:
ProgressDialogSettings settings = new ProgressDialogSettings("Current Progress"); ProgressDialog.ShowDialog(settings, TakeSomeTime, 250);
The last argument is the value that will be propagated to the TakeSomeTime
method that is passed to the second parameter. The first parameter specifies the settings object, which in its own constructor argument received the title bar text of the progress dialog. Note that one of the typesafe overloads of ShowDialog
with a generic parameter was used: The generic parameter determines the type of the custom value that is passed to the worker method. (The C# compiler can deduce that it is int
in this case on its own, based on the declaration of TakeSomeTime
and the literal 250
, therefore <int>
is not explicitly stated here.)
Now, some more detailed information than a mere bar chart might be desirable. Therefore, modify the TakeSomeTime
method to also show a status message - pretending that with every iteration, an item of some repository is processed:
private static void TakeSomeTime(IProgressDialogController controller, int number) { for (int i = 0; i < number; i++) { controller.Progress = Convert.ToInt32((double)i / number * 100); controller.StatusMessage = string.Format("Processing item {0} ...", i); System.Threading.Thread.Sleep(100); } }
If you try the code again, you will see that the StatusMessage
property sets some additional text in the progress dialog.
Now, sometimes users change their mind and notice they do not actually want to perform the lengthy operation that has already begun. In this case, it would be user-friendly to provide a means of aborting the operation. This is possible with the very same ProgressDialogSettings
constructor and the very same ShowDialog
overload used above:
ProgressDialogSettings settings = new ProgressDialogSettings("Current Progress", ButtonDef.CancelButton); ProgressDialog.ShowDialog(settings, TakeSomeTime, 250);
This Cancel button will only be effective if we also interrupt our worker method once it is pressed. Therefore, the controller object offers an IsCanceled
property that indicates whether the user has canceled the operation. If so, we can happily leave the loop immediately:
private static void TakeSomeTime(IProgressDialogController controller, int number) { for (int i = 0; i < number; i++) { controller.Progress = Convert.ToInt32((double)i / number * 100); controller.StatusMessage = string.Format("Processing item {0} ...", i); System.Threading.Thread.Sleep(100); if (controller.IsCanceled) { break; } } }
Now what if the user should see even more information about the current operations? That is where the details area comes into play. The details area is a panel that displays lines of text, and the controller object can be used to append additional lines to that area. The following variant of the TakeSomeTime
method appends text to the details area in every iteration:
private static void TakeSomeTime(IProgressDialogController controller, int number) { for (int i = 0; i < number; i++) { controller.Progress = Convert.ToInt32((double)i / number * controller.Maximum); controller.StatusMessage = string.Format("Processing item {0} ...", i); System.Threading.Thread.Sleep(100); controller.AddDetail(string.Format("Item {0} took {1} ms to process.", i, 100)); } }
Make sure to use a ProgressDialogSettings
constructor that can display the details area:
ProgressDialogSettings settings = new ProgressDialogSettings("Current Progress", true); ProgressDialog.ShowDialog(settings, TakeSomeTime, 250);
As you will notice, the progress dialog now contains an additional areas where the detail lines are constantly added.
Wiki: Help-Tutorial-SettingsObjects-InputBoxes
Wiki: Help-Tutorial-SettingsObjects-WizardFramework
Wiki: Help-Tutorial-SettingsObjects