SingleBackgroundWorker.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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 Runnable")]
  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> ();
  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. View? top = Application.TopRunnableView;
  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 Runnable? _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. };
  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 (App,
  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. // MenuBar
  203. MenuBar menu = new ();
  204. menu.Add (
  205. new MenuBarItem (
  206. "_Stage",
  207. [
  208. new MenuItem
  209. {
  210. Title = "_Close",
  211. Key = Key.C.WithCtrl,
  212. Action = () =>
  213. {
  214. if (Close ())
  215. {
  216. App?.RequestStop ();
  217. }
  218. }
  219. }
  220. ]
  221. )
  222. );
  223. _top.Add (menu);
  224. // StatusBar
  225. StatusBar statusBar = new (
  226. [
  227. new (
  228. Key.C.WithCtrl,
  229. "Close",
  230. () =>
  231. {
  232. if (Close ())
  233. {
  234. App?.RequestStop ();
  235. }
  236. }
  237. )
  238. ]
  239. );
  240. _top.Add (statusBar);
  241. Y = Pos.Bottom (menu);
  242. Height = Dim.Fill (1);
  243. Title = $"Worker started at {start}.{start:fff}";
  244. SchemeName = "Base";
  245. if (list is { })
  246. {
  247. Add (
  248. new ListView
  249. {
  250. X = 0,
  251. Y = 0,
  252. Width = Dim.Fill (),
  253. Height = Dim.Fill (),
  254. Source = new ListWrapper<string> (list)
  255. }
  256. );
  257. }
  258. _top.Add (this);
  259. }
  260. public void Load ()
  261. {
  262. if (_top is { })
  263. {
  264. App?.Run (_top);
  265. _top.Dispose ();
  266. _top = null;
  267. }
  268. }
  269. }
  270. }