{"id":13457989,"url":"https://github.com/alex-oswald/WindowsFormsLifetime","last_synced_at":"2025-03-24T14:33:02.420Z","repository":{"id":42655094,"uuid":"239383080","full_name":"alex-oswald/WindowsFormsLifetime","owner":"alex-oswald","description":"Windows Forms hosting extensions for the .NET Generic Host","archived":false,"fork":false,"pushed_at":"2024-04-28T05:35:30.000Z","size":105,"stargazers_count":111,"open_issues_count":0,"forks_count":17,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-28T07:41:17.924Z","etag":null,"topics":["csharp","dotnet","winforms"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alex-oswald.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-09T22:19:34.000Z","updated_at":"2024-05-31T01:37:50.670Z","dependencies_parsed_at":"2024-01-13T17:35:30.275Z","dependency_job_id":"3caeb75a-dd1c-4f49-9114-4023ba5ebf63","html_url":"https://github.com/alex-oswald/WindowsFormsLifetime","commit_stats":{"total_commits":53,"total_committers":4,"mean_commits":13.25,"dds":"0.18867924528301883","last_synced_commit":"4d7ace8485296b57fe1103d919ddb2a5bae5e793"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oswald%2FWindowsFormsLifetime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oswald%2FWindowsFormsLifetime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oswald%2FWindowsFormsLifetime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-oswald%2FWindowsFormsLifetime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alex-oswald","download_url":"https://codeload.github.com/alex-oswald/WindowsFormsLifetime/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245040693,"owners_count":20551308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","winforms"],"created_at":"2024-07-31T09:00:41.575Z","updated_at":"2025-03-24T14:33:02.387Z","avatar_url":"https://github.com/alex-oswald.png","language":"C#","funding_links":[],"categories":["C\\#","Validation / DataBinding / MVx / Interactions"],"sub_categories":[],"readme":"# WindowsFormsLifetime\n\n[![Build Status](https://dev.azure.com/oswaldtechnologies/WindowsFormsLifetime/_apis/build/status/alex-oswald.WindowsFormsLifetime?branchName=main)](https://dev.azure.com/oswaldtechnologies/WindowsFormsLifetime/_build/latest?definitionId=21\u0026branchName=main)\n[![Nuget](https://img.shields.io/nuget/v/OswaldTechnologies.Extensions.Hosting.WindowsFormsLifetime)](https://www.nuget.org/packages/OswaldTechnologies.Extensions.Hosting.WindowsFormsLifetime/)\n[![Nuget](https://img.shields.io/nuget/dt/OswaldTechnologies.Extensions.Hosting.WindowsFormsLifetime)](https://www.nuget.org/packages/OswaldTechnologies.Extensions.Hosting.WindowsFormsLifetime/)\n\nA Windows Forms hosting extension for the [.NET Generic Host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host).\nThis library enables you to configure the generic host to use the lifetime of Windows Forms. When configured,\nthe generic host will start an `IHostedService` that runs Windows Forms in a separate UI specific thread.\n\n- The Generic Host will use Windows Forms as it's lifetime (when the main form closes, the host shuts down)\n- All the benefits of .NET and the Generic Host, dependency injection, configuration, logging...\n- Easier multi-threading in Windows Forms\n\n## Quick Start\n\nInstall the `OswaldTechnologies.Extensions.Hosting.WindowsFormsLifetime` package from NuGet.\n\nUsing Powershell\n\n```powershell\nInstall-Package OswaldTechnologies.Extensions.Hosting.WindowsFormsLifetime\n```\n\nUsing the .NET CLI\n\n```\ndotnet add package OswaldTechnologies.Extensions.Hosting.WindowsFormsLifetime\n``` \n\nCreate a new **Windows Forms App**.\n\nReplace the contents of `Program.cs` with the following.\n\n```csharp\nusing Microsoft.Extensions.Hosting;\nusing WinFormsApp1;\nusing WindowsFormsLifetime;\n\nvar builder = Host.CreateApplicationBuilder(args);\nbuilder.UseWindowsFormsLifetime\u003cForm1\u003e();\n\nvar app = builder.Build();\napp.Run();\n```\n\n**Run the app!**\n\n**Your Windows Forms app is now running with the Generic Host!**\n\n### Passing options\n\nYou can further configure the Windows Forms lifetime by passing `Action\u003cWindowsFormsLifeTimeOptions\u003e`.\nFor example, with the default options:\n\n```csharp\nbuilder.UseWindowsFormsLifetime\u003cForm1\u003e(options =\u003e\n{\n    options.HighDpiMode = HighDpiMode.SystemAware;\n    options.EnableVisualStyles = true;\n    options.CompatibleTextRenderingDefault = false;\n    options.SuppressStatusMessages = false;\n    options.EnableConsoleShutdown = true;\n});\n```\n\n`EnableConsoleShutdown`\nAllows the use of Ctrl+C to shutdown the host while the console is being used.\n\n### Instantiating and Showing Forms\n\nAdd more forms to the DI container.\n\n```csharp\nvar builder = Host.CreateApplicationBuilder(args);\nbuilder.UseWindowsFormsLifetime\u003cForm1\u003e();\nbuilder.Services.AddTransient\u003cForm2\u003e();\nvar app = builder.Build();\napp.Run();\n```\n\nTo get a form, use the `IFormProvider`. The form provider fetches an instance of the form from the DI\ncontainer on the GUI thread. `IFormProvider` has the method, `GetFormAsync\u003cT\u003e`, used to fetch a form\ninstance.\n\nIn this example, we inject `IFormProvider` into the main form, and use that to instantiate a new\ninstance of `Form2`, then show the form.\n\n```csharp\npublic partial class Form1 : Form\n{\n    private readonly ILogger\u003cForm1\u003e _logger;\n    private readonly IFormProvider _formProvider;\n\n    public Form1(ILogger\u003cForm1\u003e logger, IFormProvider formProvider)\n    {\n        InitializeComponent();\n        _logger = logger;\n        _formProvider = formProvider;\n    }\n\n    private async void button1_Click(object sender, EventArgs e)\n    {\n        _logger.LogInformation(\"Show Form2\");\n        var form = await _formProvider.GetFormAsync\u003cForm2\u003e();\n        form.Show();\n    }\n}\n```\n\n### Invoking on the GUI thread\n\nSometimes you need to invoke an action on the GUI thread. Say you want to spawn a form from a background\nservice. Use the `IGuiContext` to invoke actions on the GUI thread.\n\nIn this example, a form is fetched and shown, in an action that is invoked on the GUI thread. Then a second\nform is shown. This example shows how the GUI does not lock up during this process.\n\n```csharp\npublic class HostedService1 : BackgroundService\n{\n    private readonly ILogger _logger;\n    private readonly IFormProvider _fp;\n    private readonly IGuiContext _guiContext;\n\n    public HostedService1(\n        ILogger\u003cHostedService1\u003e logger,\n        IFormProvider formProvider,\n        IGuiContext guiContext)\n    {\n        _logger = logger;\n        _fp = formProvider;\n        _guiContext = guiContext;\n    }\n\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        int count = 0;\n        while (!stoppingToken.IsCancellationRequested)\n        {\n            await Task.Delay(5000, stoppingToken);\n            if (count \u003c 5)\n            {\n                await _guiContext.InvokeAsync(async () =\u003e\n                {\n                    var form = await _fp.GetFormAsync\u003cForm2\u003e();\n                    form.Show();\n                });\n            }\n            count++;\n            _logger.LogInformation(\"HostedService1 Tick 1000ms\");\n        }\n    }\n}\n```\n\n## Only use the Console while debugging\n\nI like to configure my `csproj` so that the `Console` runs only while my configuration is set to `Debug`,\nand doesn't run when set to `Release`. Here is an example of how to do this. Setting the `OutputType` to\n`Exe` will run the console, while setting it to `WinExe` will not.\n\n```xml\n\u003cPropertyGroup Condition=\" '$(Configuration)' == 'Debug' \"\u003e\n  \u003cOutputType\u003eExe\u003c/OutputType\u003e\n\u003c/PropertyGroup\u003e\n\n\u003cPropertyGroup Condition=\" '$(Configuration)' == 'Release' \"\u003e\n  \u003cOutputType\u003eWinExe\u003c/OutputType\u003e\n\u003c/PropertyGroup\u003e\n```\n\n## Credits\n\nThe layout of the `WindowsFormsLifetime` class is based on .NET Core's\n[ConsoleLifetime](https://github.com/dotnet/extensions/blob/b83b27d76439497459fe9cf7337d5128c900eb5a/src/Hosting/Hosting/src/Internal/ConsoleLifetime.cs).\n\n[ExecutionContext vs SynchronizationContext](https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/)\n\n[Implementing a SynchronizationContext.SendAsync method](https://devblogs.microsoft.com/pfxteam/implementing-a-synchronizationcontext-sendasync-method/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-oswald%2FWindowsFormsLifetime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falex-oswald%2FWindowsFormsLifetime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-oswald%2FWindowsFormsLifetime/lists"}