Skip to content
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ Conventions for contributors:

### Added

- **Getting Started documents the single-file path.** A Reactor app does not need
a `.csproj`: .NET 10 runs a lone `.cs` file whose `#:package` / `#:property`
header supplies what a project file otherwise would. The header also references
`Microsoft.Windows.SDK.BuildTools.WinApp`, so `dotnet run counter.cs` launches
the app packaged, just like `winapp run counter.cs`; `WindowsPackageType=None`
runs it unpackaged (winappCli#794 / #874, shipped in winapp 0.7.0). The
agent-kit skills use the same header.

- **Validation now works on any control, not only inside `FormField`** — the
Forms guide's "Validation Context" example needed new surface to work as
written (issue #1262):
Expand Down Expand Up @@ -149,6 +157,16 @@ Conventions for contributors:

### Fixed

- **Apps that reference only `Microsoft.UI.Reactor` are framework-dependent
again** (regression from #822). The package now depends on
`Microsoft.WindowsAppSDK.Runtime`; without it the Windows App SDK silently
built such apps self-contained, so a plain `dotnet run` failed with
*"WindowsAppSDKSelfContained requires a supported Windows architecture"*. To
keep bundling the Windows App Runtime, set `WindowsAppSDKSelfContained=true`.
To move to a newer WinUI, bump the full `Microsoft.WindowsAppSDK` package;
bumping `Microsoft.WindowsAppSDK.WinUI` on its own now fails the Windows App
SDK's version check.

- **The Forms guide's "Validation Context" example now works as written**
(issue #1262). Clicking **Register** on an empty form submitted successfully
with no errors, because five defects compounded: `.Validate()` was inert
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
`Directory.Build.props`)
- Visual Studio 2022 (17.8+) or VS Code with C# Dev Kit

> **Windows App SDK packages:** The right package is injected centrally by `Directory.Build.targets` based on each project's shape: framework-dependent **libraries** (including the shipped `Microsoft.UI.Reactor`) reference the lean `Microsoft.WindowsAppSDK.WinUI` sub-package; framework-dependent **apps** additionally reference `Microsoft.WindowsAppSDK.Runtime`; **self-contained / MSIX** projects keep the full `Microsoft.WindowsAppSDK` metapackage (it bundles the runtime + DWriteCore redist). The metapackage's AI/ML/Widgets/DWrite slices are otherwise unused. Versions are pinned in `Directory.Build.props` (`WindowsAppSDKWinUIVersion` and `WindowsAppSDKVersion`) — update them there to change the version for every project at once.
> **Windows App SDK packages:** The right package is injected centrally by `Directory.Build.targets` based on each project's shape: framework-dependent projects — **libraries and apps alike**, including the shipped `Microsoft.UI.Reactor` — reference the lean `Microsoft.WindowsAppSDK.WinUI` sub-package plus `Microsoft.WindowsAppSDK.Runtime` (a library's dependencies become its consumers', and without Runtime the SDK silently builds a consumer app self-contained); **self-contained / MSIX** projects keep the full `Microsoft.WindowsAppSDK` metapackage (it bundles the runtime + DWriteCore redist). The metapackage's AI/ML/Widgets/DWrite slices are otherwise unused. Versions are pinned in `Directory.Build.props` (`WindowsAppSDKWinUIVersion` and `WindowsAppSDKVersion`) — update them there to change the version for every project at once.

> **Clone to a short path (Windows):** Clone into a short root directory such as
> `C:\src\` (for example `C:\src\microsoft-ui-reactor`) rather than a deeply
Expand Down
27 changes: 16 additions & 11 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,24 @@
which transitively brings Base + Foundation + InteractiveExperiences + WebView2
(the bootstrapper, deployment manager, MRT Core and app-lifecycle MSBuild
targets). The metapackage also aggregates AI / ML / Widgets / DWrite, which the
repo never uses — so framework-dependent projects reference only
Microsoft.WindowsAppSDK.WinUI and let the machine-wide installed
Microsoft.WindowsAppRuntime.2 supply native bits (DWriteCore, etc.) at runtime.
repo never uses — so framework-dependent projects reference
Microsoft.WindowsAppSDK.WinUI + Microsoft.WindowsAppSDK.Runtime rather than the
metapackage, and let the machine-wide installed Microsoft.WindowsAppRuntime.2
supply native bits (DWriteCore, etc.) at runtime.

Runtime is what makes an app framework-dependent: it sets
MicrosoftWindowsAppSDKPackageDir, and without it the SDK's
Microsoft.WindowsAppSDK.Base.targets silently defaults WindowsAppSDKSelfContained
to true (it only errors when a project sets that property to something other
than true explicitly). Libraries take it too, even though they don't produce an
app, because a library's dependencies become its consumers' dependencies: when
the published Microsoft.UI.Reactor carried WinUI alone, every app that
referenced only Reactor was silently built self-contained, which also demands a
RuntimeIdentifier.

Projects that BUNDLE the native runtime keep the full metapackage, which carries
the Runtime redist + DWriteCore: WindowsAppSDKSelfContained=true, or
framework-dependent MSIX (WindowsPackageType=MSIX). A framework-dependent WinUI
*executable* additionally needs Microsoft.WindowsAppSDK.Runtime — the SDK's
Microsoft.WindowsAppSDK.Base.targets rejects a framework-dependent app that
references neither Runtime nor WindowsAppSDKSelfContained. Libraries don't produce
an app, so they take the lean WinUI reference alone.
framework-dependent MSIX (WindowsPackageType=MSIX).

Evaluating the conditions here in .targets (a late import) means per-project
OutputType / WindowsAppSDKSelfContained / WindowsPackageType values are already
Expand All @@ -80,14 +87,12 @@
<PropertyGroup Condition="'$(UseWinUI)' == 'true'">
<_ReactorWinAppSDKMetapackage>false</_ReactorWinAppSDKMetapackage>
<_ReactorWinAppSDKMetapackage Condition="'$(WindowsAppSDKSelfContained)' == 'true' or '$(WindowsPackageType)' == 'MSIX'">true</_ReactorWinAppSDKMetapackage>
<_ReactorWinAppSDKFrameworkDependentApp>false</_ReactorWinAppSDKFrameworkDependentApp>
<_ReactorWinAppSDKFrameworkDependentApp Condition="'$(_ReactorWinAppSDKMetapackage)' != 'true' and ('$(OutputType)' == 'Exe' or '$(OutputType)' == 'WinExe')">true</_ReactorWinAppSDKFrameworkDependentApp>
</PropertyGroup>

<ItemGroup Condition="'$(UseWinUI)' == 'true' and '$(ManagePackageVersionsCentrally)' == 'true' and '$(ReactorSkipWinAppSDKInjection)' != 'true'">
<PackageReference Include="Microsoft.WindowsAppSDK" Condition="'$(_ReactorWinAppSDKMetapackage)' == 'true'" />
<PackageReference Include="Microsoft.WindowsAppSDK.WinUI" Condition="'$(_ReactorWinAppSDKMetapackage)' != 'true'" />
<PackageReference Include="Microsoft.WindowsAppSDK.Runtime" Condition="'$(_ReactorWinAppSDKFrameworkDependentApp)' == 'true'" />
<PackageReference Include="Microsoft.WindowsAppSDK.Runtime" Condition="'$(_ReactorWinAppSDKMetapackage)' != 'true'" />
Comment thread
azchohfi marked this conversation as resolved.
</ItemGroup>

</Project>
19 changes: 14 additions & 5 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -541,11 +541,10 @@ For lightweight demos, skip the `.csproj` entirely. Add a file-level header:

```csharp
#:package Microsoft.UI.Reactor@0.0.0-local
#:package Microsoft.WindowsAppSDK@2.2.0
#:package Microsoft.Windows.SDK.BuildTools.WinApp@0.7.0
#:property OutputType=WinExe
#:property TargetFramework=net10.0-windows10.0.22621.0
#:property UseWinUI=true
#:property WindowsPackageType=None

using Microsoft.UI.Reactor;
using static Microsoft.UI.Reactor.Factories;
Expand All @@ -557,9 +556,19 @@ ReactorApp.Run("Hello", ctx =>
});
```

Run with `dotnet run MyApp.cs -p:Platform=ARM64` (or `x64`). In selfhost
the version is `0.0.0-local` — run `mur pack-local` first if the package
isn't found. Outside the clone, replace the version with the published
Run with `dotnet run MyApp.cs` — no architecture flag. This is the header the
[Getting Started guide](https://microsoft.github.io/microsoft-ui-reactor/getting-started/#one-file-no-project)
documents: `Microsoft.Windows.SDK.BuildTools.WinApp` makes `dotnet run` launch
the app packaged, with package identity, which needs Developer Mode. If
Developer Mode is off, or you don't need identity, add
`#:property WindowsPackageType=None` to run it as a plain unpackaged `.exe`.
With neither, the app builds, then dies at startup with `REGDB_E_CLASSNOTREG`.
Either way the app uses the Windows App Runtime installed on the machine. To
bundle it instead, add `#:property WindowsAppSDKSelfContained=true`; that mode
also needs an architecture (`-r win-x64` or `-r win-arm64`), and `-r` alone
bundles nothing.
In selfhost the version is `0.0.0-local` — run `mur pack-local` first if the
package isn't found. Outside the clone, replace the version with the published
release you depend on.

> **Always capture `dotnet run` output.** Build errors exit with code 1.
Expand Down
80 changes: 80 additions & 0 deletions docs/_pipeline/templates/getting-started.md.dt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,86 @@ identity — the same thing F5 does in Visual Studio. That requires **Developer
Mode** (Settings → System → For developers). See [Packaging](packaging.md) for
the unpackaged alternative.

## One file, no project

A Reactor app does not need a `.csproj`. .NET 10 runs a single `.cs` file
directly, and file-level `#:` directives supply the configuration a project file
otherwise would:

```csharp
#:package Microsoft.UI.Reactor@{{reactorVersion}}
Comment thread
azchohfi marked this conversation as resolved.
#:package Microsoft.Windows.SDK.BuildTools.WinApp@0.7.0
#:property OutputType=WinExe
#:property TargetFramework=net10.0-windows10.0.22621.0
#:property UseWinUI=true

using Microsoft.UI.Reactor;
using Microsoft.UI.Reactor.Core;
using static Microsoft.UI.Reactor.Factories;

ReactorApp.Run<MyApp>("Hello Reactor");

class MyApp : Component
{
public override Element Render()
{
var (count, setCount) = UseState(0);

return VStack(
Heading($"Count: {count}"),
HStack(8,
Button("-", () => setCount(count - 1)),
Button("+", () => setCount(count + 1))
)
);
}
}
```

Save it as `counter.cs` and run it:

```powershell
dotnet run counter.cs
```

That is the whole app — one file, no scaffolding, no build output to manage.
It is the fastest way to try an idea, reproduce a bug, or paste a complete
runnable example into an issue. Like a scaffolded app, it runs packaged:
`Microsoft.Windows.SDK.BuildTools.WinApp` makes `dotnet run` synthesize an
appxmanifest from the `#:property` values, register the app, and launch it with
package identity, which needs **Developer Mode**. If you have the
[winapp CLI](https://github.com/microsoft/winappCli) 0.7.0 or later,
`winapp run counter.cs` does the same.

`Microsoft.UI.Reactor` brings the Windows App SDK in transitively, exactly as in
the [minimal `.csproj`](index.md#minimal-setup). The rest of the header:

| Directive | Why it is required |
|---|---|
| `Microsoft.Windows.SDK.BuildTools.WinApp` | Lets `dotnet run` package the app. It carries its own copy of the winapp CLI, so that does not need installing. Without it, plain `dotnet run` cannot package the app, and it dies at startup with `COMException … REGDB_E_CLASSNOTREG` — an error that says nothing about the missing package — unless you run it unpackaged (below). |
| `OutputType=WinExe` | Links the app for the Windows GUI subsystem. Without it the build still succeeds, but the `.exe` is a console binary, so Windows opens a console window alongside your UI for as long as the app runs. |
| `TargetFramework=net10.0-windows10.0.22621.0` | Reactor ships `net10.0-windows10.0.22621`. A lower Windows version compiles against nothing and you get `CS0234: the namespace 'Reactor' does not exist`, as if the package were missing. |
| `UseWinUI=true` | Brings in the WinUI 3 targets. |

### Running it unpackaged

`WindowsPackageType` decides whether the app runs packaged, and `dotnet run` and
`winapp run` both obey it. Declare the app unpackaged to run it as a plain `.exe`:

```csharp
#:property WindowsPackageType=None
```

An unpackaged app has no package identity, so `Package.Current` and `ms-appx:`
URIs are unavailable.

Either way the app is framework-dependent: it uses the Windows App Runtime
installed on the machine. To carry it with the app instead, add
`#:property WindowsAppSDKSelfContained=true`. That mode also needs the
architecture named with `-r win-x64` or `-r win-arm64`, because the runtime it
copies is native code built separately for each CPU — `-r` on its own does not
bundle anything.

## Contributor setup (one-time)

Working *on* Reactor rather than with it? Clone and bootstrap:
Expand Down
4 changes: 3 additions & 1 deletion docs/_pipeline/templates/index.md.dt
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ project and edit the `.csproj`:
One package reference is enough — `Microsoft.UI.Reactor` brings the Windows App
SDK in transitively, so you only add `Microsoft.WindowsAppSDK` yourself when you
want to pin a specific WinUI patch or bundle the runtime with
`WindowsAppSDKSelfContained`. See [Packaging](packaging.md) for the
`WindowsAppSDKSelfContained`. Pin WinUI through that full package, not
`Microsoft.WindowsAppSDK.WinUI` on its own, which fails the Windows App SDK's
version check. See [Packaging](packaging.md) for the
`<Platforms>`, ARM64, and self-contained shapes the project template uses.

Replace `App.cs` with a component and a `ReactorApp.Run<T>()` call, and run with
Expand Down
17 changes: 12 additions & 5 deletions docs/_pipeline/templates/packaging.md.dt
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ machines, with ARM64 second for Snapdragon X). The
sub-package brings the WinUI 3 SDK — reference assemblies plus the MSBuild
build/props/targets — while the native WinUI runtime is supplied by the
machine-wide Windows App Runtime install (or bundled into the publish output
when `WindowsAppSDKSelfContained=true`). That sub-package is what a consumer
gets transitively from `Microsoft.UI.Reactor`; the scaffolded template adds the
full
when `WindowsAppSDKSelfContained=true`). That sub-package, together with
`Microsoft.WindowsAppSDK.Runtime` (which keeps the app framework-dependent), is
what a consumer gets transitively from `Microsoft.UI.Reactor`; the scaffolded
template adds the full
[`Microsoft.WindowsAppSDK`](https://www.nuget.org/packages/Microsoft.WindowsAppSDK)
metapackage on top at scaffold time (see below), because the self-contained
shape needs the Runtime redist the metapackage carries. Inside this repo the
Expand Down Expand Up @@ -295,9 +296,15 @@ frozen when the template shipped. The two references then sit
side-by-side in your CSPROJ, so the SDK version stays an obvious knob —
edit it there when you need a specific WinUI patch. If you skip the
template and reference `Microsoft.UI.Reactor` by hand, the SDK still
arrives transitively as `Microsoft.WindowsAppSDK.WinUI`; add the full
arrives transitively as `Microsoft.WindowsAppSDK.WinUI` plus
`Microsoft.WindowsAppSDK.Runtime`, so the app builds framework-dependent; add the full
metapackage yourself when you want `WindowsAppSDKSelfContained=true` or
an MSIX, both of which need the Runtime redist. The repo-internal
an MSIX, both of which need the Runtime redist. The metapackage is also
how you move to a newer WinUI, because it moves Runtime with it: a newer
`Microsoft.WindowsAppSDK.WinUI` on its own leaves Runtime behind, and the
build fails with *"One or more referenced Windows App SDK components are
newer than the versions expected by the Microsoft.WindowsAppSDK.Runtime
package"*. The repo-internal
`WindowsAppSDKVersion` MSBuild property only governs projects under this
clone (`Directory.Build.props`); consumer projects pick their version
directly.
Expand Down
4 changes: 3 additions & 1 deletion docs/guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,9 @@ project and edit the `.csproj`:
One package reference is enough — `Microsoft.UI.Reactor` brings the Windows App
SDK in transitively, so you only add `Microsoft.WindowsAppSDK` yourself when you
want to pin a specific WinUI patch or bundle the runtime with
`WindowsAppSDKSelfContained`. See [Packaging](packaging.md) for the
`WindowsAppSDKSelfContained`. Pin WinUI through that full package, not
`Microsoft.WindowsAppSDK.WinUI` on its own, which fails the Windows App SDK's
version check. See [Packaging](packaging.md) for the
`<Platforms>`, ARM64, and self-contained shapes the project template uses.

Replace `App.cs` with a component and a `ReactorApp.Run<T>()` call, and run with
Expand Down
80 changes: 80 additions & 0 deletions docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,86 @@ identity — the same thing F5 does in Visual Studio. That requires **Developer
Mode** (Settings → System → For developers). See [Packaging](packaging.md) for
the unpackaged alternative.

## One file, no project

A Reactor app does not need a `.csproj`. .NET 10 runs a single `.cs` file
directly, and file-level `#:` directives supply the configuration a project file
otherwise would:

```csharp
#:package Microsoft.UI.Reactor@0.1.0-preview.16
#:package Microsoft.Windows.SDK.BuildTools.WinApp@0.7.0
#:property OutputType=WinExe
#:property TargetFramework=net10.0-windows10.0.22621.0
#:property UseWinUI=true

using Microsoft.UI.Reactor;
using Microsoft.UI.Reactor.Core;
using static Microsoft.UI.Reactor.Factories;

ReactorApp.Run<MyApp>("Hello Reactor");

class MyApp : Component
{
public override Element Render()
{
var (count, setCount) = UseState(0);

return VStack(
Heading($"Count: {count}"),
HStack(8,
Button("-", () => setCount(count - 1)),
Button("+", () => setCount(count + 1))
)
);
}
}
```

Save it as `counter.cs` and run it:

```powershell
dotnet run counter.cs
```

That is the whole app — one file, no scaffolding, no build output to manage.
It is the fastest way to try an idea, reproduce a bug, or paste a complete
runnable example into an issue. Like a scaffolded app, it runs packaged:
`Microsoft.Windows.SDK.BuildTools.WinApp` makes `dotnet run` synthesize an
appxmanifest from the `#:property` values, register the app, and launch it with
package identity, which needs **Developer Mode**. If you have the
[winapp CLI](https://github.com/microsoft/winappCli) 0.7.0 or later,
`winapp run counter.cs` does the same.

`Microsoft.UI.Reactor` brings the Windows App SDK in transitively, exactly as in
the [minimal `.csproj`](index.md#minimal-setup). The rest of the header:

| Directive | Why it is required |
|---|---|
| `Microsoft.Windows.SDK.BuildTools.WinApp` | Lets `dotnet run` package the app. It carries its own copy of the winapp CLI, so that does not need installing. Without it, plain `dotnet run` cannot package the app, and it dies at startup with `COMException … REGDB_E_CLASSNOTREG` — an error that says nothing about the missing package — unless you run it unpackaged (below). |
| `OutputType=WinExe` | Links the app for the Windows GUI subsystem. Without it the build still succeeds, but the `.exe` is a console binary, so Windows opens a console window alongside your UI for as long as the app runs. |
| `TargetFramework=net10.0-windows10.0.22621.0` | Reactor ships `net10.0-windows10.0.22621`. A lower Windows version compiles against nothing and you get `CS0234: the namespace 'Reactor' does not exist`, as if the package were missing. |
| `UseWinUI=true` | Brings in the WinUI 3 targets. |

### Running it unpackaged

`WindowsPackageType` decides whether the app runs packaged, and `dotnet run` and
`winapp run` both obey it. Declare the app unpackaged to run it as a plain `.exe`:

```csharp
#:property WindowsPackageType=None
```

An unpackaged app has no package identity, so `Package.Current` and `ms-appx:`
URIs are unavailable.

Either way the app is framework-dependent: it uses the Windows App Runtime
installed on the machine. To carry it with the app instead, add
`#:property WindowsAppSDKSelfContained=true`. That mode also needs the
architecture named with `-r win-x64` or `-r win-arm64`, because the runtime it
copies is native code built separately for each CPU — `-r` on its own does not
bundle anything.

## Contributor setup (one-time)

Working *on* Reactor rather than with it? Clone and bootstrap:
Expand Down
Loading
Loading