SingleBackgroundWorker.cs 14 KB

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