SingleBackgroundWorker.cs 12 KB

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