using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading; using Terminal.Gui; namespace UICatalog { [ScenarioMetadata (Name: "BackgroundWorker", Description: "A persisting multi Toplevel BackgroundWorker threading")] [ScenarioCategory ("Threading")] [ScenarioCategory ("TopLevel")] [ScenarioCategory ("Dialogs")] [ScenarioCategory ("Controls")] class BackgroundWorkerSample : Scenario { public override void Run () { Top.Dispose (); Application.Run (); Top.Dispose (); } } public class MainApp : Toplevel { private List log = new List (); private ListView listLog; private Dictionary stagingWorkers; public MainApp () { var menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_Options", new MenuItem [] { new MenuItem ("_Run Worker", "", () => RunWorker(), null, null, Key.CtrlMask | Key.R), new MenuItem ("_Cancel Worker", "", () => CancelWorker(), null, null, Key.CtrlMask | Key.C), null, new MenuItem ("_Quit", "", () => Application.RequestStop (), null, null, Key.CtrlMask | Key.Q) }) }); Add (menu); var statusBar = new StatusBar (new [] { new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Application.RequestStop()), new StatusItem(Key.CtrlMask | Key.P, "~^R~ Run Worker", () => RunWorker()), new StatusItem(Key.CtrlMask | Key.P, "~^C~ Cancel Worker", () => CancelWorker()) }); Add (statusBar); var top = new Toplevel (); top.Add (new Label ("Worker Log") { X = Pos.Center (), Y = 0 }); listLog = new ListView (log) { X = 0, Y = 2, Width = Dim.Fill (), Height = Dim.Fill () }; top.Add (listLog); Add (top); } private void RunWorker () { var stagingUI = new StagingUIController (); var worker = new BackgroundWorker () { WorkerSupportsCancellation = true }; worker.DoWork += (s, e) => { var stageResult = new List (); for (int i = 0; i < 500; i++) { stageResult.Add ( $"Worker {i} started at {DateTime.UtcNow}"); e.Result = stageResult; Thread.Sleep (1); if (worker.CancellationPending) { e.Cancel = true; return; } } }; worker.RunWorkerCompleted += (s, e) => { if (e.Error != null) { // Failed log.Add ($"Exception occurred {e.Error.Message} on Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} at {DateTime.UtcNow}"); listLog.SetNeedsDisplay (); } else if (e.Cancelled) { // Canceled log.Add ($"Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} was canceled at {DateTime.UtcNow}!"); listLog.SetNeedsDisplay (); } else { // Passed log.Add ($"Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} was completed at {DateTime.UtcNow}."); listLog.SetNeedsDisplay (); Application.Refresh (); stagingUI.Load (e.Result as List); } stagingWorkers.Remove (stagingUI); }; Application.Run (stagingUI); if (stagingUI.StartStaging != null) { log.Add ($"Worker is started at {stagingUI.StartStaging}.{stagingUI.StartStaging:fff}"); listLog.SetNeedsDisplay (); if (stagingWorkers == null) { stagingWorkers = new Dictionary (); } stagingWorkers.Add (stagingUI, worker); worker.RunWorkerAsync (); } } private void CancelWorker () { if (stagingWorkers.Count == 0) { log.Add ($"Worker is not running at {DateTime.UtcNow}!"); listLog.SetNeedsDisplay (); return; } var eStaging = stagingWorkers.GetEnumerator (); eStaging.MoveNext (); var fStaging = eStaging.Current; var stagingUI = fStaging.Key; var worker = fStaging.Value; worker.CancelAsync (); log.Add ($"Worker {stagingUI.StartStaging}.{stagingUI.StartStaging:fff} is canceling at {DateTime.UtcNow}!"); listLog.SetNeedsDisplay (); } } public class StagingUIController : Window { private Label label; private ListView listView; private Button start; private Button close; public DateTime? StartStaging { get; private set; } public StagingUIController () { X = Pos.Center (); Y = Pos.Center (); Width = Dim.Percent (85); Height = Dim.Percent (85); ColorScheme = Colors.Dialog; Modal = true; Title = "Run Worker"; label = new Label ("Press start to do the work or close to exit.") { X = Pos.Center (), Y = 1, ColorScheme = Colors.Dialog }; Add (label); listView = new ListView () { X = 0, Y = 2, Width = Dim.Fill (), Height = Dim.Fill (2) }; Add (listView); start = new Button ("Start") { IsDefault = true }; start.Clicked += () => { StartStaging = DateTime.UtcNow; Application.RequestStop (); }; Add (start); close = new Button ("Close"); close.Clicked += () => Application.RequestStop (); Add (close); LayoutStarted += (_) => { var btnsWidth = start.Bounds.Width + close.Bounds.Width + 2 - 1; var shiftLeft = Math.Max ((Bounds.Width - btnsWidth) / 2 - 2, 0); shiftLeft += close.Bounds.Width + 1; close.X = Pos.AnchorEnd (shiftLeft); close.Y = Pos.AnchorEnd (1); shiftLeft += start.Bounds.Width + 1; start.X = Pos.AnchorEnd (shiftLeft); start.Y = Pos.AnchorEnd (1); }; } public void Load (List list) { var stagingUI = new StagingUIController (); stagingUI.Title = $"Worker started at {StartStaging}.{StartStaging:fff}"; stagingUI.label.Text = "Work list:"; stagingUI.listView.SetSource (list); stagingUI.start.Visible = false; Application.Run (stagingUI); } } }