SingleBackgroundWorker.cs 12 KB

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