Skip to content

Commit 32996b4

Browse files
committed
Refine AGENTS.md for WPF FBA template: enhance clarity, update directives, and emphasize critical initialization steps.
1 parent b4d9057 commit 32996b4

File tree

2 files changed

+242
-320
lines changed

2 files changed

+242
-320
lines changed

content/winforms-fba/AGENTS.md

Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
11
# GitHub Copilot Instructions — Windows Forms File-based App (FBA) Template
22

3-
> Opinionated, production-ready guidance for building **.NET 10** Windows Forms apps as a **single C# file**.
3+
> Production-ready guidance for building **.NET 10** WinForms apps in a **single C# file**.
4+
> **Note:** Per on-device verification, this template **requires the two-step apartment state init (`Unknown``STA`)**.
45
56
---
67

7-
## 1) Project Overview
8+
## 1) Overview
89

9-
Author a full Windows Forms desktop app using the **File-based App (FBA)** model: keep build/runtime directives at the top of one `.cs` file; put top-level program code first; types follow.
10+
Build a full WinForms desktop app with the **File-based App (FBA)** model: directives at the top of one `.cs` file, top-level bootstrapping first, types below.
1011

1112
---
1213

1314
## 2) FBA Directives (Windows-only GUI)
1415

1516
```csharp
16-
// Shebang optional for Windows-only GUI apps
17+
// Shebang optional for Windows-only apps
1718
1819
#:sdk Microsoft.NET.Sdk
1920
#:property OutputType=WinExe
2021
#:property TargetFramework=net10.0-windows
2122
#:property UseWindowsForms=True
2223
#:property UseWPF=False
23-
#:property PublishAot=False // WinForms is not AOT-compatible
24+
#:property PublishAot=False
2425
#:property Nullable=enable
2526
```
2627

2728
**Notes**
2829

29-
* Prefer `net10.0-windows` to light up Windows-only APIs.
30-
* Keep directives at the very top.
31-
* No packages are required to start; pin any added packages with exact versions.
30+
* Use `net10.0-windows` to light up Windows APIs.
31+
* Keep all directives at the very top.
32+
* Pin any added packages with exact versions.
3233

3334
---
3435

35-
## 3) Quick Start (single file, DI + proper WinForms init)
36+
## 3) Quick Start (single file, DI + required WinForms init)
3637

3738
```csharp
38-
// winforms.cs
39-
// FBA directives go here (see section 2)
39+
// winforms.cs (FBA directives above)
4040
4141
using System;
4242
using System.Drawing;
@@ -48,32 +48,32 @@ using Microsoft.Extensions.Logging;
4848

4949
// --- Top-level program must precede type declarations ---
5050
51-
// WinForms requires STA.
51+
// **CRITICAL (empirically required)**: two-step apartment initialization
52+
Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown);
5253
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
5354

54-
// Minimal host for DI/logging/config (modern .NET pattern)
55+
// Minimal host for DI/logging/config
5556
using var host = Host.CreateApplicationBuilder(args)
5657
.ConfigureServices(s =>
5758
{
58-
s.AddLogging(b => b.AddDebug().AddEventLog().AddSimpleConsole());
59-
s.AddSingleton<MainForm>(); // register forms in DI
60-
s.AddSingleton<IBannerService, BannerService>();
59+
s.AddLogging(b => b.AddSimpleConsole());
60+
s.AddSingleton<MainForm>();
61+
s.AddSingleton<IBannerService, BannerService>();
6162
})
6263
.Build();
6364

6465
// WinForms bootstrap
65-
Application.OleRequired();
66+
Application.OleRequired(); // enables OLE features (Clipboard, DragDrop, etc.)
6667
Application.EnableVisualStyles();
6768
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
6869
Application.SetCompatibleTextRenderingDefault(false);
6970

70-
// Run via ApplicationContext for clean lifetime control
71+
// Run via ApplicationContext for deterministic lifetime
7172
using var scope = host.Services.CreateScope();
7273
var context = new SingleFormContext(scope.ServiceProvider.GetRequiredService<MainForm>());
73-
7474
Application.Run(context);
7575

76-
// --- Types follow here ---
76+
// --- Types follow ---
7777
7878
sealed class SingleFormContext(Form main) : ApplicationContext(main)
7979
{
@@ -84,23 +84,19 @@ sealed class SingleFormContext(Form main) : ApplicationContext(main)
8484
}
8585
}
8686

87-
public sealed class MainForm(IBannerService banner) : Form
87+
public sealed class MainForm(IBannerService svc) : Form
8888
{
8989
private readonly Button _btn = new() { Text = "Click me", AutoSize = true, Dock = DockStyle.Top };
9090
private readonly TextBox _log = new() { Multiline = true, ReadOnly = true, Dock = DockStyle.Fill, ScrollBars = ScrollBars.Vertical };
9191

92-
public MainForm : this(null!) { } // designer compatibility (not used here)
93-
94-
public MainForm(IBannerService? _ = null) : this(new BannerService()) { } // FBA convenience
95-
96-
public MainForm(IBannerService svc) : this()
92+
public MainForm(IBannerService banner) : this()
9793
{
9894
Text = "WinForms FBA (.NET 10)";
9995
StartPosition = FormStartPosition.CenterScreen;
10096
ClientSize = new Size(640, 360);
10197

10298
Controls.AddRange([ _log, _btn ]);
103-
_btn.Click += (_, __) => Append(svc.Banner());
99+
_btn.Click += (_, __) => Append(banner.Banner());
104100
Append("Ready.");
105101
}
106102

@@ -122,115 +118,116 @@ dotnet run winforms.cs
122118

123119
---
124120

125-
## 4) Application Initialization (must-haves)
121+
## 4) Initialization (must-haves)
122+
123+
* **Apartment state (CRITICAL):**
126124

127-
* **STA**: `Thread.CurrentThread.SetApartmentState(ApartmentState.STA)`.
128-
* **OLE**: `Application.OleRequired()` if you touch Clipboard/Drag-Drop/etc.
129-
* **Styles/DPI**: `EnableVisualStyles()`, `SetHighDpiMode(PerMonitorV2)`.
125+
* `Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown);`
126+
* `Thread.CurrentThread.SetApartmentState(ApartmentState.STA);`
127+
* Rationale: matches real-world behavior you validated; avoids intermittent COM issues.
128+
* **OLE**: `Application.OleRequired()` when using Clipboard/Drag-Drop/Embedding (safe to call once).
129+
* **Styles/DPI**: `EnableVisualStyles()` and `SetHighDpiMode(PerMonitorV2)`.
130130
* **Text rendering**: `SetCompatibleTextRenderingDefault(false)`.
131131

132-
Use `ApplicationContext` for multi-form or background-work lifecycles.
132+
Use `ApplicationContext` to coordinate multi-form lifecycles.
133133

134134
---
135135

136136
## 5) Layout & Controls
137137

138-
* Prefer **Dock/Anchor** and layout panels (**TableLayoutPanel**, **FlowLayoutPanel**) over absolute sizes.
139-
* Initialize via **object initializers** and **collection expressions** (`Controls.AddRange([ … ])`).
140-
* Center main window with `StartPosition = CenterScreen`.
138+
* Prefer **Dock/Anchor** and layout panels (**TableLayoutPanel**, **FlowLayoutPanel**) over fixed sizes.
139+
* Use object initializers and collection expressions (`Controls.AddRange([ … ])`).
140+
* Center the main window (`StartPosition = CenterScreen`).
141141

142142
---
143143

144144
## 6) Events & Async
145145

146-
* Keep UI work on the UI thread; use `BeginInvoke/Invoke` when crossing threads.
147-
* For long work: `await Task.Run(...)` with `CancellationToken` and progress updates.
148-
* **Do not** sprinkle `ConfigureAwait(false)` in UI code; you usually need the captured context.
146+
* Keep UI work on the UI thread; background work via `await Task.Run(...)` with `CancellationToken`.
147+
* Marshal back with `Control.BeginInvoke/Invoke`.
148+
* Don’t blanket-apply `ConfigureAwait(false)` in UI code.
149149

150150
---
151151

152152
## 7) Data Binding
153153

154-
* Use `Control.DataBindings` for simple scenarios; validate via `IDataErrorInfo`/attributes.
155-
* For larger apps, adopt MVP/MVVM-like separation for testability.
154+
* `Control.DataBindings` for simple cases; validation via `IDataErrorInfo` or data annotations.
155+
* For larger apps, prefer MVP/MVVM-style separation for testability.
156156

157157
---
158158

159-
## 8) High-DPI
159+
## 8) High DPI
160160

161-
* Use `HighDpiMode.PerMonitorV2`.
162-
* Avoid pixel constants; prefer layout containers and autosizing.
163-
* Provide scalable icons/images (ICO with multiple sizes or vector where possible).
161+
* `HighDpiMode.PerMonitorV2`, avoid pixel constants, prefer autosizing.
162+
* Provide multi-size `.ico` or vector assets.
164163

165164
---
166165

167166
## 9) Threading & Resilience
168167

169-
* Use `async` handlers for I/O; report progress via `IProgress<T>` or UI updates.
170-
* Catch exceptions in background tasks and surface friendly messages (not MessageBox storms).
168+
* Make long operations async; show progress (`IProgress<T>` or UI updates).
169+
* Catch exceptions in background tasks; present friendly errors (no dialog storms).
171170

172171
---
173172

174173
## 10) Security & Reliability
175174

176-
* Validate all user inputs.
177-
* Avoid storing secrets in config; use Windows DPAPI/credential stores if needed.
178-
* Dispose `IDisposable` controls/resources (e.g., timers, streams).
175+
* Validate inputs.
176+
* Keep secrets out of config; use DPAPI/Windows credential stores.
177+
* Dispose `IDisposable` controls/resources (timers, streams, etc.).
179178

180179
---
181180

182181
## 11) Testing
183182

184-
* **Unit**: isolate business logic from UI; inject services.
183+
* **Unit**: isolate business logic; inject services.
185184
* **UI**: optional automation (WinAppDriver/Playwright); verify keyboard navigation & accessibility.
186-
* **Integration**: end-to-end workflows (startup → action → shutdown).
185+
* **Integration**: full flows (startup → action → shutdown).
187186

188187
---
189188

190189
## 12) Deployment
191190

192-
* **ClickOnce** or MSIX/installer for distribution.
193-
* Document **Windows version** and **.NET** requirements.
194-
* Handle updates gracefully; preserve user settings.
191+
* ClickOnce/MSIX/installer—document Windows and .NET requirements.
192+
* Graceful updates; preserve user settings.
195193

196194
---
197195

198196
## 13) Accessibility & UX
199197

200-
* Set `AccessibleName/Description` and tab order.
201-
* Keyboard shortcuts (e.g., `&File`, `AcceptButton`, `CancelButton`).
202-
* Clear validation errors and status feedback.
198+
* Set `AccessibleName/Description`; correct tab order.
199+
* Keyboard shortcuts (`&File`, `AcceptButton`, `CancelButton`).
200+
* Clear, actionable validation errors.
203201

204202
---
205203

206204
## 14) File-based Dev Standards
207205

208-
* Stay **single-file** unless asked to convert.
209-
* If the file grows, add a `Directory.Build.props` for shared MSBuild settings—**FBA stays authoritative**.
210-
* For conversion: `dotnet project convert winforms.cs` (only on explicit request; keep original).
206+
* Stay **single-file** unless explicitly asked to convert.
207+
* If the file grows, add `Directory.Build.props` (FBA file remains authoritative).
208+
* Conversion on request: `dotnet project convert winforms.cs` (preserve original FBA).
211209

212210
---
213211

214212
## 15) Agent Execution Compatibility
215213

216214
* Don’t add `Console.ReadLine()`/`ReadKey()` blockers.
217-
* Exit deterministically (`Application.ExitThread()` / `Application.Exit()` when your scenario completes).
215+
* Exit deterministically when done (`Application.ExitThread()` / `Application.Exit()`).
218216

219217
---
220218

221219
## 16) Review Checklist
222220

223221
* [ ] `TargetFramework=net10.0-windows`, `UseWindowsForms=True`, `PublishAot=False`
224-
* [ ] STA thread, OLE, styles, **PerMonitorV2** DPI
225-
* [ ] Top-level code **before** types; DI wired via `Host.CreateApplicationBuilder`
226-
* [ ] `ApplicationContext` for lifetime; main form centered; controls laid out with Dock/Anchor
227-
* [ ] Long operations off UI thread; safe marshaling back to UI
228-
* [ ] Accessibility, keyboard navigation, and proper error feedback
229-
* [ ] Single-file FBA; no unnecessary packages; pinned versions if any are added
222+
* [ ] **Apartment state set: `Unknown``STA`**
223+
* [ ] Top-level bootstrapping before types; DI via `Host.CreateApplicationBuilder`
224+
* [ ] `ApplicationContext` for lifetime; centered window; Dock/Anchor layout
225+
* [ ] Long work off UI thread; safe marshaling back
226+
* [ ] Accessibility and clear error feedback
227+
* [ ] Single-file FBA; exact package versions pinned if any are added
230228

231229
---
232230

233-
## 17) MCP Integration (NuGet & Docs)
231+
### Callout
234232

235-
* **Never guess package versions.** If you add packages (logging, settings, etc.), resolve exact versions via your **`nuget` MCP server** and pin them with `#:package [email protected]`.
236-
* Use **`microsoft_learn` MCP** to verify current WinForms DPI, accessibility, and deployment guidance.
233+
Your field result matters. This template **codifies** the two-step apartment initialization you confirmed so others get the same stable behavior.

0 commit comments

Comments
 (0)