From bb705ed31fb600aa456477a4232a0aab34b01098 Mon Sep 17 00:00:00 2001 From: Grant Winney <1354059+grantwinney@users.noreply.github.com> Date: Mon, 5 Dec 2022 19:13:51 -0500 Subject: [PATCH] add TaskCompletionSource example --- README.md | 1 + .../Entities/ISSAstronauts.cs | 26 ++++ .../Entities/ISSLocation.cs | 25 ++++ .../LegacySpaceLibrary.csproj | 52 ++++++++ .../LegacySpaceLibrary/OldSpaceLibrary.cs | 93 ++++++++++++++ .../Properties/AssemblyInfo.cs | 36 ++++++ .../LegacySpaceLibrary/packages.config | 4 + Threading/TaskCompletion/TaskCompletion.sln | 31 +++++ .../TaskCompletion/TaskCompletion/App.config | 6 + .../TaskCompletion/TaskCompletion/Program.cs | 22 ++++ .../TaskCompletion/Properties/AssemblyInfo.cs | 36 ++++++ .../Properties/Resources.Designer.cs | 63 ++++++++++ .../TaskCompletion/Properties/Resources.resx | 117 ++++++++++++++++++ .../Properties/Settings.Designer.cs | 26 ++++ .../Properties/Settings.settings | 7 ++ .../TaskCompletion/SpaceTask.cs | 51 ++++++++ .../TaskCompletion/TaskCompletion.csproj | 28 +++++ .../frmCallBackgroundWorker.Designer.cs | 99 +++++++++++++++ .../TaskCompletion/frmCallBackgroundWorker.cs | 63 ++++++++++ .../frmCallBackgroundWorker.resx | 60 +++++++++ .../frmCallBackgroundWorkerAsTask.Designer.cs | 99 +++++++++++++++ .../frmCallBackgroundWorkerAsTask.cs | 62 ++++++++++ .../frmCallBackgroundWorkerAsTask.resx | 60 +++++++++ 23 files changed, 1067 insertions(+) create mode 100644 Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSAstronauts.cs create mode 100644 Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSLocation.cs create mode 100644 Threading/TaskCompletion/LegacySpaceLibrary/LegacySpaceLibrary.csproj create mode 100644 Threading/TaskCompletion/LegacySpaceLibrary/OldSpaceLibrary.cs create mode 100644 Threading/TaskCompletion/LegacySpaceLibrary/Properties/AssemblyInfo.cs create mode 100644 Threading/TaskCompletion/LegacySpaceLibrary/packages.config create mode 100644 Threading/TaskCompletion/TaskCompletion.sln create mode 100644 Threading/TaskCompletion/TaskCompletion/App.config create mode 100644 Threading/TaskCompletion/TaskCompletion/Program.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/Properties/AssemblyInfo.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/Properties/Resources.Designer.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/Properties/Resources.resx create mode 100644 Threading/TaskCompletion/TaskCompletion/Properties/Settings.Designer.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/Properties/Settings.settings create mode 100644 Threading/TaskCompletion/TaskCompletion/SpaceTask.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/TaskCompletion.csproj create mode 100644 Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.Designer.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.resx create mode 100644 Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.Designer.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.cs create mode 100644 Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.resx diff --git a/README.md b/README.md index e923bc8..301ef85 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ So to all my fellow devs that find themselves supporting the old coldness, let's ## Threading * Using Async, Await, and Task to keep the WinForms UI responsive ([blog post](https://grantwinney.com/using-async-await-and-task-to-keep-the-winforms-ui-more-responsive), [source code](https://github.com/grantwinney/SurvivingWinForms/tree/master/Threading/AsyncAwait)) +* Turning a BackgroundWorker into a Task with TaskCompletionSource ([blog post](https://grantwinney.com/turning-a-backgroundworker-into-a-task-with-taskcompletionsource), [source code](https://github.com/grantwinney/SurvivingWinForms/tree/master/Threading/TaskCompletion)) ## Web diff --git a/Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSAstronauts.cs b/Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSAstronauts.cs new file mode 100644 index 0000000..a64fd42 --- /dev/null +++ b/Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSAstronauts.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace LegacySpaceLibrary +{ + public class ISSAstronauts + { + [JsonProperty("people")] + public List People { get; private set; } + + [JsonProperty("number")] + public int Number { get; private set; } + + [JsonProperty("message")] + public string Message { get; private set; } + } + + public class Astronaut + { + [JsonProperty("name")] + public string Name { get; private set; } + + [JsonProperty("craft")] + public string Craft { get; private set; } + } +} diff --git a/Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSLocation.cs b/Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSLocation.cs new file mode 100644 index 0000000..b7c8159 --- /dev/null +++ b/Threading/TaskCompletion/LegacySpaceLibrary/Entities/ISSLocation.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace LegacySpaceLibrary +{ + public class ISSLocation + { + [JsonProperty("message")] + public string Message { get; private set; } + + [JsonProperty("iss_position")] + public ISSPosition Position { get; private set; } + + [JsonProperty("timestamp")] + public int Timestamp { get; private set; } + } + + public class ISSPosition + { + [JsonProperty("latitude")] + public string Latitude { get; private set; } + + [JsonProperty("longitude")] + public string Longitude { get; private set; } + } +} diff --git a/Threading/TaskCompletion/LegacySpaceLibrary/LegacySpaceLibrary.csproj b/Threading/TaskCompletion/LegacySpaceLibrary/LegacySpaceLibrary.csproj new file mode 100644 index 0000000..31ada53 --- /dev/null +++ b/Threading/TaskCompletion/LegacySpaceLibrary/LegacySpaceLibrary.csproj @@ -0,0 +1,52 @@ + + + + + Debug + AnyCPU + {61D76390-15D1-4A0F-9FB7-FEDE264B39C0} + Library + Properties + LegacySpaceLibrary + LegacySpaceLibrary + v2.0 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.13.0.2\lib\net20\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Threading/TaskCompletion/LegacySpaceLibrary/OldSpaceLibrary.cs b/Threading/TaskCompletion/LegacySpaceLibrary/OldSpaceLibrary.cs new file mode 100644 index 0000000..b671498 --- /dev/null +++ b/Threading/TaskCompletion/LegacySpaceLibrary/OldSpaceLibrary.cs @@ -0,0 +1,93 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Net; +using System.Threading; + +namespace LegacySpaceLibrary +{ + public class OldSpaceLibrary + { + public readonly BackgroundWorker Worker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true }; + + public ISSLocation ISSLocation { get; private set; } + public ISSAstronauts ISSAstronauts { get; private set; } + + public OldSpaceLibrary() + { + Worker.DoWork += Worker_DoWork; + Worker.RunWorkerCompleted += Worker_RunWorkerCompleted; + } + + public void GetData() + { + if (Worker.IsBusy) + return; + + ISSLocation = null; + ISSAstronauts = null; + + Worker.RunWorkerAsync(); + } + + private void Worker_DoWork(object sender, DoWorkEventArgs e) + { + Worker.ReportProgress(0, "Requesting data..."); + + Thread.Sleep(2000); + if (Worker.CancellationPending) + { + e.Cancel = true; + return; + } + + var issLocation = JsonConvert.DeserializeObject( + GetDataAsString("http://api.open-notify.org/iss-now.json")); + Worker.ReportProgress(25, "1/2 requests done."); + + Thread.Sleep(2000); + if (Worker.CancellationPending) + { + e.Cancel = true; + return; + } + + var issAstronauts = JsonConvert.DeserializeObject( + GetDataAsString("http://api.open-notify.org/astros.json")); + issAstronauts.People.RemoveAll(x => x.Craft != "ISS"); + Worker.ReportProgress(50, "2/2 requests done."); + Worker.ReportProgress(75, "Processing data..."); + + Thread.Sleep(2000); + if (Worker.CancellationPending) + { + e.Cancel = true; + return; + } + + Worker.ReportProgress(100, "Processing complete!"); + + e.Result = new List { issLocation, issAstronauts }; + } + + private string GetDataAsString(string endpoint) + { + var request = WebRequest.Create(endpoint); + using (var response = (HttpWebResponse)request.GetResponse()) + using (var dataStream = response.GetResponseStream()) + using (var reader = new StreamReader(dataStream)) + return reader.ReadToEnd(); + } + + private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (!e.Cancelled && e.Error == null) + { + var data = (List)e.Result; + ISSLocation = (ISSLocation)data[0]; + ISSAstronauts = (ISSAstronauts)data[1]; + } + } + } +} diff --git a/Threading/TaskCompletion/LegacySpaceLibrary/Properties/AssemblyInfo.cs b/Threading/TaskCompletion/LegacySpaceLibrary/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..06ecc8f --- /dev/null +++ b/Threading/TaskCompletion/LegacySpaceLibrary/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LegacySpaceLibrary")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LegacySpaceLibrary")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("61d76390-15d1-4a0f-9fb7-fede264b39c0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Threading/TaskCompletion/LegacySpaceLibrary/packages.config b/Threading/TaskCompletion/LegacySpaceLibrary/packages.config new file mode 100644 index 0000000..612b13e --- /dev/null +++ b/Threading/TaskCompletion/LegacySpaceLibrary/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Threading/TaskCompletion/TaskCompletion.sln b/Threading/TaskCompletion/TaskCompletion.sln new file mode 100644 index 0000000..a87b922 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33110.190 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TaskCompletion", "TaskCompletion\TaskCompletion.csproj", "{8DEAEDBE-C6D1-4578-BF8C-36DDB695EB44}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LegacySpaceLibrary", "LegacySpaceLibrary\LegacySpaceLibrary.csproj", "{61D76390-15D1-4A0F-9FB7-FEDE264B39C0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8DEAEDBE-C6D1-4578-BF8C-36DDB695EB44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DEAEDBE-C6D1-4578-BF8C-36DDB695EB44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DEAEDBE-C6D1-4578-BF8C-36DDB695EB44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DEAEDBE-C6D1-4578-BF8C-36DDB695EB44}.Release|Any CPU.Build.0 = Release|Any CPU + {61D76390-15D1-4A0F-9FB7-FEDE264B39C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61D76390-15D1-4A0F-9FB7-FEDE264B39C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61D76390-15D1-4A0F-9FB7-FEDE264B39C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61D76390-15D1-4A0F-9FB7-FEDE264B39C0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A0D4E38A-AA91-4090-819F-1353C4420CA0} + EndGlobalSection +EndGlobal diff --git a/Threading/TaskCompletion/TaskCompletion/App.config b/Threading/TaskCompletion/TaskCompletion/App.config new file mode 100644 index 0000000..4bfa005 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Threading/TaskCompletion/TaskCompletion/Program.cs b/Threading/TaskCompletion/TaskCompletion/Program.cs new file mode 100644 index 0000000..3c82eb3 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Windows.Forms; + +namespace TaskCompletion +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.SetCompatibleTextRenderingDefault(false); + + //Application.Run(new frmCallBackgroundWorker()); + Application.Run(new frmCallBackgroundWorkerAsTask()); + } + } +} diff --git a/Threading/TaskCompletion/TaskCompletion/Properties/AssemblyInfo.cs b/Threading/TaskCompletion/TaskCompletion/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..68b5c65 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SurvivingWinForms")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SurvivingWinForms")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8deaedbe-c6d1-4578-bf8c-36ddb695eb44")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Threading/TaskCompletion/TaskCompletion/Properties/Resources.Designer.cs b/Threading/TaskCompletion/TaskCompletion/Properties/Resources.Designer.cs new file mode 100644 index 0000000..65f3882 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TaskCompletion.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AsyncAwait.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Threading/TaskCompletion/TaskCompletion/Properties/Resources.resx b/Threading/TaskCompletion/TaskCompletion/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Threading/TaskCompletion/TaskCompletion/Properties/Settings.Designer.cs b/Threading/TaskCompletion/TaskCompletion/Properties/Settings.Designer.cs new file mode 100644 index 0000000..e8b1128 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TaskCompletion.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Threading/TaskCompletion/TaskCompletion/Properties/Settings.settings b/Threading/TaskCompletion/TaskCompletion/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Threading/TaskCompletion/TaskCompletion/SpaceTask.cs b/Threading/TaskCompletion/TaskCompletion/SpaceTask.cs new file mode 100644 index 0000000..bc558b7 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/SpaceTask.cs @@ -0,0 +1,51 @@ +using LegacySpaceLibrary; +using System; +using System.Threading.Tasks; + +namespace TaskCompletion +{ + /// + /// Wrapping the BackgroundWorker in a Task, to hide the implementation details of the BGW + /// + public class SpaceTask + { + private TaskCompletionSource> tcs = new(); + private readonly OldSpaceLibrary oldSpaceLibrary = new(); + + public bool IsRunning { get; private set; } + + public void CancelTask() + { + oldSpaceLibrary.Worker.CancelAsync(); + } + + public Task> GetData(IProgress progress = null) + { + if (IsRunning) + throw new Exception("Task is already running. Please wait until it's complete."); + + IsRunning = true; + + oldSpaceLibrary.Worker.ProgressChanged += (s, e) => + { + progress?.Report($"[{e.ProgressPercentage,3}%]: {e.UserState}"); + }; + + oldSpaceLibrary.Worker.RunWorkerCompleted += (s, e) => + { + if (e.Error != null) + tcs.SetException(e.Error); + else if (e.Cancelled) + tcs.SetCanceled(); + else + tcs.SetResult(Tuple.Create(oldSpaceLibrary.ISSLocation, oldSpaceLibrary.ISSAstronauts)); + + IsRunning = false; + }; + + oldSpaceLibrary.GetData(); + + return tcs.Task; + } + } +} diff --git a/Threading/TaskCompletion/TaskCompletion/TaskCompletion.csproj b/Threading/TaskCompletion/TaskCompletion/TaskCompletion.csproj new file mode 100644 index 0000000..fa24c44 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/TaskCompletion.csproj @@ -0,0 +1,28 @@ + + + net6.0-windows + WinExe + AsyncAwait + AsyncAwait + false + true + true + + + + + + + all + + + + + + + + + Form + + + \ No newline at end of file diff --git a/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.Designer.cs b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.Designer.cs new file mode 100644 index 0000000..f444dc7 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.Designer.cs @@ -0,0 +1,99 @@ +namespace TaskCompletion +{ + partial class frmCallBackgroundWorker + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnGetData = new System.Windows.Forms.Button(); + this.txtStatus = new System.Windows.Forms.TextBox(); + this.prgStatus = new System.Windows.Forms.ProgressBar(); + this.SuspendLayout(); + // + // btnGetData + // + this.btnGetData.Location = new System.Drawing.Point(11, 11); + this.btnGetData.Margin = new System.Windows.Forms.Padding(2); + this.btnGetData.Name = "btnGetData"; + this.btnGetData.Size = new System.Drawing.Size(116, 22); + this.btnGetData.TabIndex = 1; + this.btnGetData.Text = "Get Latest ISS Data"; + this.btnGetData.UseVisualStyleBackColor = true; + this.btnGetData.Click += new System.EventHandler(this.btnGetData_Click); + // + // txtStatus + // + this.txtStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtStatus.Location = new System.Drawing.Point(11, 37); + this.txtStatus.Margin = new System.Windows.Forms.Padding(2); + this.txtStatus.Multiline = true; + this.txtStatus.Name = "txtStatus"; + this.txtStatus.ReadOnly = true; + this.txtStatus.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtStatus.Size = new System.Drawing.Size(446, 239); + this.txtStatus.TabIndex = 4; + // + // prgStatus + // + this.prgStatus.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.prgStatus.Location = new System.Drawing.Point(131, 11); + this.prgStatus.Margin = new System.Windows.Forms.Padding(2); + this.prgStatus.MarqueeAnimationSpeed = 30; + this.prgStatus.Name = "prgStatus"; + this.prgStatus.Size = new System.Drawing.Size(324, 22); + this.prgStatus.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.prgStatus.TabIndex = 7; + this.prgStatus.Visible = false; + // + // frmCallBackgroundWorker + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(468, 287); + this.Controls.Add(this.prgStatus); + this.Controls.Add(this.txtStatus); + this.Controls.Add(this.btnGetData); + this.DoubleBuffered = true; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(2); + this.MaximizeBox = false; + this.Name = "frmCallBackgroundWorker"; + this.Text = "Call BackgroundWorker"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnGetData; + private System.Windows.Forms.TextBox txtStatus; + private System.Windows.Forms.ProgressBar prgStatus; + } +} \ No newline at end of file diff --git a/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.cs b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.cs new file mode 100644 index 0000000..9a9edc2 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.cs @@ -0,0 +1,63 @@ +using LegacySpaceLibrary; +using System; +using System.Windows.Forms; + +namespace TaskCompletion +{ + /// + /// Using the BackgroundWorker as provided in the "legacy space library" class. + /// + public partial class frmCallBackgroundWorker : Form + { + private readonly OldSpaceLibrary oldSpaceLibrary = new(); + + public frmCallBackgroundWorker() + { + InitializeComponent(); + + oldSpaceLibrary.Worker.ProgressChanged += Worker_ProgressChanged; + oldSpaceLibrary.Worker.RunWorkerCompleted += Worker_RunWorkerCompleted; + } + + private void btnGetData_Click(object sender, EventArgs e) + { + if (oldSpaceLibrary.Worker.IsBusy) + { + oldSpaceLibrary.Worker.CancelAsync(); + btnGetData.Enabled = false; + return; + } + + btnGetData.Text = "Cancel operation"; + prgStatus.Value = 0; + prgStatus.Show(); + txtStatus.Text = "Retrieving ISS data...\r\n\r\n"; + + oldSpaceLibrary.GetData(); + } + + private void Worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) + { + txtStatus.AppendText($"[{e.ProgressPercentage,3}%]: {e.UserState}\r\n"); + prgStatus.Value = e.ProgressPercentage; + } + + private void Worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) + { + if (e.Error != null) + txtStatus.AppendText($"Failed: {e.Error.Message}"); + else if (e.Cancelled) + txtStatus.AppendText("Cancelled!"); + else + { + var location = oldSpaceLibrary.ISSLocation.Position; + var astronauts = oldSpaceLibrary.ISSAstronauts.People; + txtStatus.AppendText($"\r\nThe ISS is positioned over ({location.Latitude}, {location.Longitude}) with {astronauts.Count} astronauts aboard."); + } + + btnGetData.Text = "Get Latest ISS Data"; + btnGetData.Enabled = true; + prgStatus.Hide(); + } + } +} diff --git a/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.resx b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorker.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.Designer.cs b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.Designer.cs new file mode 100644 index 0000000..346c05d --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.Designer.cs @@ -0,0 +1,99 @@ +namespace TaskCompletion +{ + partial class frmCallBackgroundWorkerAsTask + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnGetData = new System.Windows.Forms.Button(); + this.txtStatus = new System.Windows.Forms.TextBox(); + this.prgStatus = new System.Windows.Forms.ProgressBar(); + this.SuspendLayout(); + // + // btnGetData + // + this.btnGetData.Location = new System.Drawing.Point(11, 11); + this.btnGetData.Margin = new System.Windows.Forms.Padding(2); + this.btnGetData.Name = "btnGetData"; + this.btnGetData.Size = new System.Drawing.Size(116, 22); + this.btnGetData.TabIndex = 1; + this.btnGetData.Text = "Get Latest ISS Data"; + this.btnGetData.UseVisualStyleBackColor = true; + this.btnGetData.Click += new System.EventHandler(this.btnGetData_Click); + // + // txtStatus + // + this.txtStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtStatus.Location = new System.Drawing.Point(11, 37); + this.txtStatus.Margin = new System.Windows.Forms.Padding(2); + this.txtStatus.Multiline = true; + this.txtStatus.Name = "txtStatus"; + this.txtStatus.ReadOnly = true; + this.txtStatus.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtStatus.Size = new System.Drawing.Size(446, 239); + this.txtStatus.TabIndex = 4; + // + // prgStatus + // + this.prgStatus.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.prgStatus.Location = new System.Drawing.Point(131, 11); + this.prgStatus.Margin = new System.Windows.Forms.Padding(2); + this.prgStatus.MarqueeAnimationSpeed = 30; + this.prgStatus.Name = "prgStatus"; + this.prgStatus.Size = new System.Drawing.Size(324, 22); + this.prgStatus.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.prgStatus.TabIndex = 7; + this.prgStatus.Visible = false; + // + // frmCallBackgroundWorkerAsTask + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(468, 287); + this.Controls.Add(this.prgStatus); + this.Controls.Add(this.txtStatus); + this.Controls.Add(this.btnGetData); + this.DoubleBuffered = true; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(2); + this.MaximizeBox = false; + this.Name = "frmCallBackgroundWorkerAsTask"; + this.Text = "Call BackgroundWorker as Task"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnGetData; + private System.Windows.Forms.TextBox txtStatus; + private System.Windows.Forms.ProgressBar prgStatus; + } +} \ No newline at end of file diff --git a/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.cs b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.cs new file mode 100644 index 0000000..f7d9184 --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace TaskCompletion +{ + /// + /// Calling the Task provided by the SpaceTask class that hides the BackgroundWorker. + /// + public partial class frmCallBackgroundWorkerAsTask : Form + { + SpaceTask task; + + public frmCallBackgroundWorkerAsTask() + { + InitializeComponent(); + } + + private async void btnGetData_Click(object sender, EventArgs e) + { + if (task?.IsRunning ?? false) + { + task.CancelTask(); + btnGetData.Enabled = false; + return; + } + + btnGetData.Text = "Cancel operation"; + prgStatus.Value = 0; + prgStatus.Show(); + txtStatus.Text = "Retrieving ISS data...\r\n\r\n"; + + try + { + var progressHandler = new Progress(statusUpdate => + { + txtStatus.AppendText($"{statusUpdate}\r\n"); + }); + + task = new(); + + var data = await task.GetData(progressHandler); + + var location = data.Item1.Position; + var astronauts = data.Item2.People; + txtStatus.AppendText($"\r\nThe ISS is positioned over ({location.Latitude}, {location.Longitude}) with {astronauts.Count} astronauts aboard.\r\n\r\n"); + } + catch (TaskCanceledException) + { + txtStatus.AppendText("Cancelled!"); + } + catch (Exception ex) + { + txtStatus.AppendText($"Failed: {ex.Message}"); + } + + btnGetData.Text = "Get Latest ISS Data (task)"; + btnGetData.Enabled = true; + prgStatus.Hide(); + } + } +} diff --git a/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.resx b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/Threading/TaskCompletion/TaskCompletion/frmCallBackgroundWorkerAsTask.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file