SingleBackgroundWorker.cs 12 KB

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