Reactor Windowing
Use this skill when a task involves top-level windows, placement, shell chrome, taskbar features, displays, or native pickers.
Core APIs
var win = ReactorApp.OpenWindow(
new WindowSpec { Title = "Settings", Width = 520, Height = 420 },
() => new SettingsWindow());
win.Activate();
win.Close();
WindowSpec is immutable startup intent. ReactorWindow is the live handle for
runtime mutators (SetSize, SetPosition, SetAspectRatio, BeginDragMove,
SavePlacement, Update).
Size and resize
new WindowSpec
{
ResizeMode = WindowResizeMode.CanMinimize, // CanResize | NoResize | CanMinimize
AspectRatio = 16.0 / 9.0,
MinWidth = 480,
SizeToContent = WindowSizeToContent.Manual, // Manual | Width | Height | WidthAndHeight
};
UseWindow()?.SetAspectRatio(4.0 / 3.0);
UseWindowAspectRatio(1.0); // scoped; unmount clears
Rules:
AspectRatiocannot combine withResizeMode.NoResize.AspectRatiocannot combine withSizeToContent.SizeToContentignores maximized windows and may settle one frame after mount.
Movement, drag, and persistence
new WindowSpec
{
StartPosition = WindowStartPosition.CenterOnCurrent,
IsMovableByBackground = true,
};
var (x, y) = UseWindowPosition();
var drag = UseWindowDragMove();
Button("Drag", drag);
Border(customEditor).Drag(false); // opt out of background drag
var spec = new WindowSpec { Title = "Shell" }
.WithPersistence("shell-main", WindowStartPosition.CenterOnCurrent);
UseWindow()?.SavePlacement();
Persistence requires PersistPlacement; use .WithPersistence(...) for the
common case. PersistenceId alone is only identity for persistence systems.
Z-order, taskbar, and chrome
new WindowSpec
{
ShowInTaskbar = false,
ShowInSwitcher = true,
Level = WindowLevel.Floating, // Normal | Floating | AlwaysOnTop
Style = WindowStyle.ToolWindow, // Default | None | ToolWindow
CornerStyle = WindowCornerStyle.RoundedSmall,
};
Notes:
Floatingstays above owners and sibling Reactor app windows.ToolWindowhides from the taskbar by default unlessShowInTaskbaris explicit.WindowStyle.Noneshould normally setIsMovableByBackground = true.UseIsCovered()is a z-order hint, not pixel-accurate occlusion.
Title bar and backdrop
VStack(
TitleBar("My App"),
Body());
A TitleBar(...) element infers ExtendsContentIntoTitleBar = true when the
spec value is null. Explicit true or false wins.
Close-safety caveat (#537):
ExtendsContentIntoTitleBar = falsewith a mountedTitleBar(...)is allowed and previously crashed on close (STATUS_HEAP_CORRUPTION); Reactor now flips the window back into content-extended mode just before the native close (every close/exit/dispose path) so it is safe. Prefer omittingTitleBar(...)when you want the system title bar.
Title bar icon
ReactorApp.Run<App>("My App", icon: WindowIcon.FromPath("Assets/AppIcon.ico"));
TitleBar("My App"); // inherits the window icon -- nothing to declare
TitleBar("My App").NoIcon(); // opt out, for a deliberately bare title bar
A TitleBar(...) with no .Icon(...) inherits the window's icon:
WindowSpec.Icon if declared, otherwise the Assets\AppIcon.ico convention
beside the app. Do not restate the app icon on the title bar — that is the
default. Use .Icon(...) only to show a different mark there.
Not inherited: an icon that exists only as an executable PE resource
(<ApplicationIcon>), because that stage yields a raw HICON with no path and a
XAML IconSource needs an image source; and an embedded window
(WindowSpec.Embed), which never receives a window icon at all.
Icon sources
WindowIcon.FromPath("Assets/AppIcon.ico"); // file beside the app
WindowIcon.FromResource("ms-appx:///Assets/A.ico"); // packaged resource
WindowIcon.FromBytes(icoOrPngBytes); // encoded data in memory
WindowIcon.FromRgba(pixels, 16, 16); // raw RGBA8, top-down
Not every surface takes every kind, because they need different primitives:
| Surface | FromPath |
FromResource |
FromBytes / FromRgba |
|---|---|---|---|
| Window caption / Alt-Tab | yes | yes | no |
| Tray icon, taskbar overlay, thumbnail toolbar | yes | no | yes |
| Jump-list entry | unpackaged only | packaged only | no |
The three shell surfaces need a raw HICON (LoadImageW on a file, or
CreateIconFromResourceEx on in-memory data), neither of which reads an
ms-appx: URI. Jump lists need a Uri. AppWindow.SetIcon needs a filesystem
path. An unusable source is skipped with a diagnostic, never thrown — on the
window that means falling through to Assets\AppIcon.ico or the PE icon, so a
binary icon: leaves the window no barer than declaring none.
Reach for the binary factories when the icon is an embedded resource, a
download, or drawn at runtime: they avoid writing a temporary file. The bytes
are copied at construction and held for the WindowIcon's lifetime.
Tall title bar
TitleBar("My App").WithNavigation(nav).PaneToggleButtonVisible(true).Tall();
new WindowSpec { ExtendsContentIntoTitleBar = true, TitleBarHeight = WindowTitleBarHeight.Tall };
.Tall() / .HeightOption(WindowTitleBarHeight) (Standard / Tall /
Collapsed) is the layout for a title bar hosting a back button or pane toggle.
It sets both the system caption (AppWindow.TitleBar.PreferredHeightOption) and
the WinUI title-bar control's own height — the control does not follow the
caption, so setting one alone leaves them disagreeing. An explicit .Height(...)
wins over the implied 48; WindowSpec.TitleBarHeight wins over the element.
Requires a content-extended window — the native setter throws
ERROR_INVALID_STATEotherwise. Reactor applies it after its own content-extension flip and warns instead of throwing, so prefer this over settingPreferredHeightOptionfrom aUseEffect.
Custom title-bar content and drag regions
TitleBar(...) accepts custom Content. Interactive controls are excluded from
the drag region automatically (WinApp SDK ≥ 2.1.3). Override per element with
.IsDragRegion(false) (force clickable) or .IsDragRegion(true) (force draggable),
and add .AutoRefreshDragRegions() when the content changes across renders:
(TitleBar("Gallery") with
{
Content = HStack(8,
AutoSuggestBox("", _ => {}).Width(200),
Button(Icon(FontIcon("\uE713", fontSize: 16)), OnSettings)
.AutomationName("Settings").IsDragRegion(false)),
}).AutoRefreshDragRegions();
VStack(...).Backdrop(BackdropKind.Mica);
new WindowSpec { Backdrop = BackdropChoice.Of(BackdropKind.DesktopAcrylic) };
BackdropKind.Transparent falls back to no backdrop when unsupported by the
referenced Windows App SDK.
Taskbar, displays, and pickers
var taskbar = UseWindow()!.TaskbarItem;
taskbar.Description = "Exporting";
taskbar.Progress.State = TaskbarProgressState.Normal;
taskbar.Progress.Value = 0.25;
var displays = UseDisplays();
var nearest = ReactorDisplay.NearestTo(window.Position.X, window.Position.Y);
var file = await UseFilePickerAsync(new FilePickerOptions(FileTypeFilter: [".txt"]));
var folder = await UseFolderPickerAsync(new FolderPickerOptions());
Picker hooks must run on the owning window's UI thread and use the owning HWND; there is no arbitrary HWND parameter.
Recipe: Command Palette
PowerToys Run-style launcher. See samples/apps/command-palette-window/.
new WindowSpec
{
Style = WindowStyle.None,
IsMovableByBackground = true,
Level = WindowLevel.AlwaysOnTop,
CornerStyle = WindowCornerStyle.Rounded,
StartPosition = WindowStartPosition.CenterOnCurrent,
ShowInTaskbar = false,
ShowInSwitcher = false,
};
Recipe: Tool Palette
Photoshop-style owned floating palette. See samples/apps/tool-palette/.
var main = ReactorApp.OpenWindow(new WindowSpec { Title = "Editor" }, () => new Editor());
ReactorApp.OpenWindow(new WindowSpec
{
Title = "Tools",
Owner = main,
Style = WindowStyle.ToolWindow,
Level = WindowLevel.Floating,
CornerStyle = WindowCornerStyle.RoundedSmall,
}, () => new ToolPalette());
Recipe: Media Player (aspect-locked)
new WindowSpec
{
Title = "Player",
Width = 960,
Height = 540,
AspectRatio = 16.0 / 9.0,
};
UseWindow()?.SetAspectRatio(videoWidth / (double)videoHeight);
UseWindowAspectRatio(16.0 / 9.0);