SingleBackgroundWorker.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Threading;
  6. using Terminal.Gui;
  7. namespace UICatalog.Scenarios;
  8. [ScenarioMetadata ("Single BackgroundWorker", "A single BackgroundWorker threading opening another Toplevel")]
  9. [ScenarioCategory ("Threading")]
  10. [ScenarioCategory ("Overlapped")]
  11. [ScenarioCategory ("Runnable")]
  12. public class SingleBackgroundWorker : Scenario
  13. {
  14. public override void Main ()
  15. {
  16. Application.Run<MainApp> ().Dispose ();
  17. Application.Shutdown ();
  18. }
  19. public class MainApp : Toplevel
  20. {
  21. private readonly ListView _listLog;
  22. private readonly ObservableCollection<string> _log = [];
  23. private DateTime? _startStaging;
  24. private BackgroundWorker _worker;
  25. public MainApp ()
  26. {
  27. var menu = new MenuBar
  28. {
  29. Menus =
  30. [
  31. new (
  32. "_Options",
  33. new MenuItem []
  34. {
  35. new (
  36. "_Run Worker",
  37. "",
  38. () => RunWorker (),
  39. null,
  40. null,
  41. KeyCode.CtrlMask | KeyCode.R
  42. ),
  43. null,
  44. new (
  45. "_Quit",
  46. "",
  47. () => Application.RequestStop (),
  48. null,
  49. null,
  50. KeyCode.CtrlMask | KeyCode.Q
  51. )
  52. }
  53. )
  54. ]
  55. };
  56. Add (menu);
  57. var statusBar = new StatusBar (
  58. [
  59. new (Application.QuitKey, "Quit", () => Application.RequestStop ()),
  60. new (Key.R.WithCtrl, "Run Worker", RunWorker)
  61. ]);
  62. Add (statusBar);
  63. var workerLogTop = new Toplevel { Title = "Worker Log Top" };
  64. workerLogTop.Add (
  65. new Label { X = Pos.Center (), Y = 0, Text = "Worker Log" }
  66. );
  67. _listLog = new()
  68. {
  69. X = 0,
  70. Y = 2,
  71. Width = Dim.Fill (),
  72. Height = Dim.Fill (),
  73. Source = new ListWrapper<string> (_log)
  74. };
  75. workerLogTop.Add (_listLog);
  76. Add (workerLogTop);
  77. Title = "MainApp";
  78. }
  79. private void RunWorker ()
  80. {
  81. _worker = new() { WorkerSupportsCancellation = true };
  82. var cancel = new Button { Text = "Cancel Worker" };
  83. cancel.Accept += (s, e) =>
  84. {
  85. if (_worker == null)
  86. {
  87. _log.Add ($"Worker is not running at {DateTime.Now}!");
  88. _listLog.SetNeedsDisplay ();
  89. return;
  90. }
  91. _log.Add (
  92. $"Worker {_startStaging}.{_startStaging:fff} is canceling at {DateTime.Now}!"
  93. );
  94. _listLog.SetNeedsDisplay ();
  95. _worker.CancelAsync ();
  96. };
  97. _startStaging = DateTime.Now;
  98. _log.Add ($"Worker is started at {_startStaging}.{_startStaging:fff}");
  99. _listLog.SetNeedsDisplay ();
  100. var md = new Dialog
  101. {
  102. Title = $"Running Worker started at {_startStaging}.{_startStaging:fff}", Buttons = [cancel]
  103. };
  104. md.Add (
  105. new Label { X = Pos.Center (), Y = Pos.Center (), Text = "Wait for worker to finish..." }
  106. );
  107. _worker.DoWork += (s, e) =>
  108. {
  109. List<string> stageResult = new ();
  110. for (var i = 0; i < 500; i++)
  111. {
  112. stageResult.Add ($"Worker {i} started at {DateTime.Now}");
  113. e.Result = stageResult;
  114. Thread.Sleep (1);
  115. if (_worker.CancellationPending)
  116. {
  117. e.Cancel = true;
  118. return;
  119. }
  120. }
  121. };
  122. _worker.RunWorkerCompleted += (s, e) =>
  123. {
  124. if (md.IsCurrentTop)
  125. {
  126. //Close the dialog
  127. Application.RequestStop ();
  128. }
  129. if (e.Error != null)
  130. {
  131. // Failed
  132. _log.Add (
  133. $"Exception occurred {e.Error.Message} on Worker {_startStaging}.{_startStaging:fff} at {DateTime.Now}"
  134. );
  135. _listLog.SetNeedsDisplay ();
  136. }
  137. else if (e.Cancelled)
  138. {
  139. // Canceled
  140. _log.Add (
  141. $"Worker {_startStaging}.{_startStaging:fff} was canceled at {DateTime.Now}!"
  142. );
  143. _listLog.SetNeedsDisplay ();
  144. }
  145. else
  146. {
  147. // Passed
  148. _log.Add (
  149. $"Worker {_startStaging}.{_startStaging:fff} was completed at {DateTime.Now}."
  150. );
  151. _listLog.SetNeedsDisplay ();
  152. Application.Refresh ();
  153. var builderUI =
  154. new StagingUIController (_startStaging, e.Result as ObservableCollection<string>);
  155. Toplevel top = Application.Top;
  156. top.Visible = false;
  157. Application.Current.Visible = false;
  158. builderUI.Load ();
  159. builderUI.Dispose ();
  160. top.Visible = true;
  161. }
  162. _worker = null;
  163. };
  164. _worker.RunWorkerAsync ();
  165. Application.Run (md);
  166. md.Dispose ();
  167. }
  168. }
  169. public class StagingUIController : Window
  170. {
  171. private Toplevel _top;
  172. public StagingUIController (DateTime? start, ObservableCollection<string> list)
  173. {
  174. _top = new()
  175. {
  176. Title = "_top", Width = Dim.Fill (), Height = Dim.Fill ()
  177. };
  178. _top.KeyDown += (s, e) =>
  179. {
  180. // Prevents App.QuitKey from closing this.
  181. // Only Ctrl+C is allowed.
  182. if (e == Application.QuitKey)
  183. {
  184. e.Handled = true;
  185. }
  186. };
  187. bool Close ()
  188. {
  189. int n = MessageBox.Query (
  190. 50,
  191. 7,
  192. "Close Window.",
  193. "Are you sure you want to close this window?",
  194. "Yes",
  195. "No"
  196. );
  197. return n == 0;
  198. }
  199. var menu = new MenuBar
  200. {
  201. Menus =
  202. [
  203. new (
  204. "_Stage",
  205. new MenuItem []
  206. {
  207. new (
  208. "_Close",
  209. "",
  210. () =>
  211. {
  212. if (Close ())
  213. {
  214. Application.RequestStop ();
  215. }
  216. },
  217. null,
  218. null,
  219. KeyCode.CtrlMask | KeyCode.C
  220. )
  221. }
  222. )
  223. ]
  224. };
  225. _top.Add (menu);
  226. var statusBar = new StatusBar (
  227. [
  228. new (
  229. Key.C.WithCtrl,
  230. "Close",
  231. () =>
  232. {
  233. if (Close ())
  234. {
  235. Application.RequestStop ();
  236. }
  237. }
  238. )
  239. ]);
  240. _top.Add (statusBar);
  241. Title = $"Worker started at {start}.{start:fff}";
  242. ColorScheme = Colors.ColorSchemes ["Base"];
  243. Add (
  244. new ListView
  245. {
  246. X = 0,
  247. Y = 0,
  248. Width = Dim.Fill (),
  249. Height = Dim.Fill (),
  250. Source = new ListWrapper<string> (list)
  251. }
  252. );
  253. _top.Add (this);
  254. }
  255. public void Load ()
  256. {
  257. Application.Run (_top);
  258. _top.Dispose ();
  259. _top = null;
  260. }
  261. }
  262. }