BackgroundWorkerSample.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Threading;
  5. using Terminal.Gui;
  6. namespace UICatalog {
  7. [ScenarioMetadata (Name: "BackgroundWorker", Description: "A persisting multi Toplevel BackgroundWorker threading")]
  8. [ScenarioCategory ("Threading")]
  9. [ScenarioCategory ("TopLevel")]
  10. [ScenarioCategory ("Dialogs")]
  11. [ScenarioCategory ("Controls")]
  12. class BackgroundWorkerSample : Scenario {
  13. public override void Run ()
  14. {
  15. Top.Dispose ();
  16. Application.Run<MainApp> ();
  17. Top.Dispose ();
  18. }
  19. }
  20. public class MainApp : Toplevel {
  21. private List<string> log = new List<string> ();
  22. private ListView listLog;
  23. private Dictionary<StagingUIController, BackgroundWorker> stagingWorkers;
  24. public MainApp ()
  25. {
  26. var menu = new MenuBar (new MenuBarItem [] {
  27. new MenuBarItem ("_Options", new MenuItem [] {
  28. new MenuItem ("_Run Worker", "", () => RunWorker(), null, null, Key.CtrlMask | Key.R),
  29. new MenuItem ("_Cancel Worker", "", () => CancelWorker(), null, null, Key.CtrlMask | Key.C),
  30. null,
  31. new MenuItem ("_Quit", "", () => Application.RequestStop (), null, null, Key.CtrlMask | Key.Q)
  32. })
  33. });
  34. Add (menu);
  35. var statusBar = new StatusBar (new [] {
  36. new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Application.RequestStop()),
  37. new StatusItem(Key.CtrlMask | Key.P, "~^R~ Run Worker", () => RunWorker()),
  38. new StatusItem(Key.CtrlMask | Key.P, "~^C~ Cancel Worker", () => CancelWorker())
  39. });
  40. Add (statusBar);
  41. var top = new Toplevel ();
  42. top.Add (new Label ("Worker Log") {
  43. X = Pos.Center (),
  44. Y = 0
  45. });
  46. listLog = new ListView (log) {
  47. X = 0,
  48. Y = 2,
  49. Width = Dim.Fill (),
  50. Height = Dim.Fill ()
  51. };
  52. top.Add (listLog);
  53. Add (top);
  54. }
  55. private void RunWorker ()
  56. {
  57. var stagingUI = new StagingUIController ();
  58. var worker = new BackgroundWorker () { WorkerSupportsCancellation = true };
  59. worker.DoWork += (s, e) => {
  60. var stageResult = new List<string> ();
  61. for (int i = 0; i < 500; i++) {
  62. stageResult.Add (
  63. $"Worker {i} started at {DateTime.UtcNow}");
  64. e.Result = stageResult;
  65. Thread.Sleep (1);
  66. if (worker.CancellationPending) {
  67. e.Cancel = true;
  68. return;
  69. }
  70. }
  71. };
  72. worker.RunWorkerCompleted += (s, e) => {
  73. if (e.Error != null) {
  74. // Failed
  75. log.Add ($"Exception occurred {e.Error.Message} on Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} at {DateTime.UtcNow}");
  76. listLog.SetNeedsDisplay ();
  77. } else if (e.Cancelled) {
  78. // Canceled
  79. log.Add ($"Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} was canceled at {DateTime.UtcNow}!");
  80. listLog.SetNeedsDisplay ();
  81. } else {
  82. // Passed
  83. log.Add ($"Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} was completed at {DateTime.UtcNow}.");
  84. listLog.SetNeedsDisplay ();
  85. Application.Refresh ();
  86. stagingUI.Load (e.Result as List<string>);
  87. }
  88. stagingWorkers.Remove (stagingUI);
  89. };
  90. Application.Run (stagingUI);
  91. if (stagingUI.StartStaging != null) {
  92. log.Add ($"Worker is started at {stagingUI.StartStaging}.{stagingUI.StartStaging:fff}");
  93. listLog.SetNeedsDisplay ();
  94. if (stagingWorkers == null) {
  95. stagingWorkers = new Dictionary<StagingUIController, BackgroundWorker> ();
  96. }
  97. stagingWorkers.Add (stagingUI, worker);
  98. worker.RunWorkerAsync ();
  99. }
  100. }
  101. private void CancelWorker ()
  102. {
  103. if (stagingWorkers.Count == 0) {
  104. log.Add ($"Worker is not running at {DateTime.UtcNow}!");
  105. listLog.SetNeedsDisplay ();
  106. return;
  107. }
  108. var eStaging = stagingWorkers.GetEnumerator ();
  109. eStaging.MoveNext ();
  110. var fStaging = eStaging.Current;
  111. var stagingUI = fStaging.Key;
  112. var worker = fStaging.Value;
  113. worker.CancelAsync ();
  114. log.Add ($"Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} is canceling at {DateTime.UtcNow}!");
  115. listLog.SetNeedsDisplay ();
  116. }
  117. }
  118. public class StagingUIController : Window {
  119. private Label label;
  120. private ListView listView;
  121. private Button start;
  122. private Button close;
  123. public DateTime? StartStaging { get; private set; }
  124. public StagingUIController ()
  125. {
  126. X = Pos.Center ();
  127. Y = Pos.Center ();
  128. Width = Dim.Percent (85);
  129. Height = Dim.Percent (85);
  130. ColorScheme = Colors.Dialog;
  131. Modal = true;
  132. Title = "Run Worker";
  133. label = new Label ("Press start to do the work or close to exit.") {
  134. X = Pos.Center (),
  135. Y = 1,
  136. ColorScheme = Colors.Dialog
  137. };
  138. Add (label);
  139. listView = new ListView () {
  140. X = 0,
  141. Y = 2,
  142. Width = Dim.Fill (),
  143. Height = Dim.Fill (2)
  144. };
  145. Add (listView);
  146. start = new Button ("Start") { IsDefault = true };
  147. start.Clicked += () => {
  148. StartStaging = DateTime.UtcNow;
  149. Application.RequestStop ();
  150. };
  151. Add (start);
  152. close = new Button ("Close");
  153. close.Clicked += () => Application.RequestStop ();
  154. Add (close);
  155. LayoutStarted += (_) => {
  156. var btnsWidth = start.Bounds.Width + close.Bounds.Width + 2 - 1;
  157. var shiftLeft = Math.Max ((Bounds.Width - btnsWidth) / 2 - 2, 0);
  158. shiftLeft += close.Bounds.Width + 1;
  159. close.X = Pos.AnchorEnd (shiftLeft);
  160. close.Y = Pos.AnchorEnd (1);
  161. shiftLeft += start.Bounds.Width + 1;
  162. start.X = Pos.AnchorEnd (shiftLeft);
  163. start.Y = Pos.AnchorEnd (1);
  164. };
  165. }
  166. public void Load (List<string> list)
  167. {
  168. var stagingUI = new StagingUIController ();
  169. stagingUI.Title = $"Worker started at {StartStaging}.{StartStaging:fff}";
  170. stagingUI.label.Text = "Work list:";
  171. stagingUI.listView.SetSource (list);
  172. stagingUI.start.Visible = false;
  173. Application.Run (stagingUI);
  174. }
  175. }
  176. }