Skip to content

Preview Flyout Settings

andre edited this page Jun 21, 2026 · 3 revisions

Preview, flyout & settings

A widget can offer three surfaces. Only the preview is really essential — a widget with no preview has nothing to put on the taskbar — but most widgets use at least the preview and the flyout.

The preview

CreatePreviewContent() returns whatever you want sitting in that little slot on the taskbar. The catch, and it's the one thing people trip over, is that the preview is rasterised: the SDK snapshots your XAML into a bitmap and WidBar paints that. So shapes, text, images, borders, gradients — all fine. Anything backed by a swap chain — Win2D's CanvasControl, MediaPlayerElement, SwapChainPanel — comes out blank, because there's no live surface to capture. If you need that kind of content, put it in the flyout instead.

Design for the size you ask for in PreviewLogicalWidth, and remember it's re-read after settings change, so a "compact mode" toggle can genuinely shrink the slot:

public override int PreviewLogicalWidth => _settings.Compact ? 120 : 188;
public override bool IsPreviewVisible    => !_settings.HideWhenIdle || _isActive;
public override UIElement? CreatePreviewContent() => _preview = new MyPreview(_settings);

WidBar owns the slot itself — placement, sizing, hover, the click that opens the flyout. You just draw. When your data changes, call Context.RequestPreviewRefresh(); it's coalesced to around 20 fps, so you don't need to be precious about how often you call it. And if there's nothing worth showing, set IsPreviewVisible to false rather than rendering an empty box — the instance stays alive and configurable, it just disappears from the taskbar until you flip it back (call RequestPreviewRefresh() after).

The flyout

CreateFlyoutContent() is the popup that opens when someone clicks the preview. Return null if your widget doesn't need one. Unlike the preview, this is a real window with real input, so anything goes here — animations, media, swap chains, the lot.

Size it with FlyoutWidth/FlyoutHeight and pick a backdrop. The backdrop is just the window material behind your content; you still bring your own padding and colours.

public override int FlyoutWidth  => 360;
public override int FlyoutHeight => 420;
public override WidgetFlyoutBackdrop FlyoutBackdrop => WidgetFlyoutBackdrop.Acrylic;
public override UIElement? CreateFlyoutContent() => new MyFlyoutView(_settings);

The SDK keeps the flyout warm after the first open so it reappears instantly. If your flyout does ongoing work, implement IWidgetFlyoutLifecycle (see the plugin contract) and pause it in OnFlyoutHidden.

Settings

Implement IConfigurableWidgetPlugin.CreateSettingsContent and WidBar hosts your UI in a tidy Mica window with Save and Cancel buttons (it localises those labels for you). The same window opens from the flyout's gear button and from Configure in WidBar.

public UIElement? CreateSettingsContent(IWidgetSettingsContext ctx)
{
    var view = new MySettingsView(MySettings.FromJson(ctx.SettingsJson));
    view.Changed += s => ctx.SaveSettings(s.ToJson());   // persisted when the user hits Save
    return view;
}

The schema is yours; WidBar only ever sees a JSON string and hands it back on the next init. The nice part is live preview: OnSettingsDraftChanged(json) fires on every edit, so the taskbar updates as the user drags a slider. It's also called once with the original JSON if they cancel, which means re-applying it naturally undoes the draft:

public override void OnSettingsDraftChanged(string json)
{
    _settings = MySettings.FromJson(json);
    MyPreview.Apply(_preview, _settings);
}

If you use a control library

Some XAML control libraries need their resource dictionary merged in. Do it in App.xaml the usual way, or call MergeResourceDictionaryFromFile(relativePath) from your App to load a loose dictionary shipped alongside the executable.

Clone this wiki locally