Runic.Application.Views.Wpf 0.8.0-preview.6

This is a prerelease version of Runic.Application.Views.Wpf.
dotnet add package Runic.Application.Views.Wpf --version 0.8.0-preview.6
                    
NuGet\Install-Package Runic.Application.Views.Wpf -Version 0.8.0-preview.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Runic.Application.Views.Wpf" Version="0.8.0-preview.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Runic.Application.Views.Wpf" Version="0.8.0-preview.6" />
                    
Directory.Packages.props
<PackageReference Include="Runic.Application.Views.Wpf" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Runic.Application.Views.Wpf --version 0.8.0-preview.6
                    
#r "nuget: Runic.Application.Views.Wpf, 0.8.0-preview.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Runic.Application.Views.Wpf@0.8.0-preview.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Runic.Application.Views.Wpf&version=0.8.0-preview.6&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Runic.Application.Views.Wpf&version=0.8.0-preview.6&prerelease
                    
Install as a Cake Tool

Runic.Application.Views.Wpf

Experimental (RUNICWPF001) optional WPF child presentation for a Runic web View. Targets .NET 10 and Windows. The containing WPF application owns its dispatcher, shell, navigation engine, ViewModels, services and native dialogs. Toolkit, ReactiveUI, or plain observable models can use the same presentation adapter. Suppress RUNICWPF001 where you use it; the package suppresses it in the code that the WPF markup compiler generates for a named RunicWebView. The XAML namespace is https://runic-artifex.eu/xaml/views, with the prefix rv.

Host a web View in XAML

RunicViewHost presents an existing model through its generated web View:

<UserControl xmlns:rv="https://runic-artifex.eu/xaml/views"
             xmlns:local="clr-namespace:MyApp">
  <rv:RunicViewHost Model="{Binding}">
    
    <local:NativeEditorView />
  </rv:RunicViewHost>
</UserControl>

Give it the application's service provider once, on the window, or on the host from a View that received its provider (host.Services = services):

RunicViewHost.SetServices(mainWindow, services);

The host owns the hosting lifecycle:

  • It opens when it is loaded with a Model and Services, and waits for the native child's Loaded before starting the presentation.
  • It closes when it is unloaded or Model becomes null. Changing Model, Services, ModelContext, ContentRoot, EntryPage or SurfaceOptions closes the session and opens a fresh one.
  • Changes run one at a time. A newer change cancels an open in progress, and changes that queue behind a running one collapse into the latest.
  • Closing releases the Bridge session first, which drains its accepted operations, then the DesktopHost, and only then removes and disposes the native child.
  • When the presentation can't open (no WebView2 Runtime, no Bridge registered for the model, a missing frontend), the host shows its content, the fallback, sets State to Failed and keeps the exception in Error. ReloadAsync() retries, and also reconnects an open View through a fresh session.

ContentRoot (default www) is the built frontend's directory relative to the application's base directory, and EntryPage the page to open in it (default index.html). Set SurfaceOptions for anything else. ModelContext defaults to the IRunicModelContext registered in Services; pass the navigator's dispatcher context when navigation and the web View present the same model. State, Error and StateChanged report progress; Surface is the open presentation's DesktopSurface, and WaitForPresentationAsync() returns when no change is pending. The host borrows the model, services and context: it never creates or disposes a ViewModel, a scope or a navigation entry.

Lower-level API

RunicViewHost composes RunicWebView, DesktopHost and CreateWpfViewAsync. Use them directly only when you need another lifecycle. The view and host live as long as the presentation, so keep them in fields and dispose them in this order when the control unloads, rather than with await using in the method that opens them:

var webView = new RunicWebView();
editorContainer.Content = webView;
// The native child exists only after layout: open from its Loaded event.
webView.Loaded += async (_, _) =>
{
    _desktop = await DesktopHost.StartAsync(new DesktopHostOptions
    {
        WindowHostFactory = webView.CreateWindowHostFactory(),
    });
    _view = await services.CreateWpfViewAsync(_desktop,
        new DesktopSurfaceOptions { Content = new DesktopContent.Directory(Path.Combine(AppContext.BaseDirectory, "www")) },
        existingEditorViewModel, modelContext: existingDispatcherContext);
    await _view.OpenAsync();
};

// On unload: session, then host, then the native child.
await _view.DisposeAsync();
await _desktop.DisposeAsync();
editorContainer.Content = null;
webView.Dispose();

You then also serialize swaps, cancel an open on unload and fall back yourself.

Register generated Bridges using AddRunicViews() from the application's generated composition. A logical RunicView<TModel> is sufficient; no additional Runic Window or navigation engine is required. The frontend uses the existing Desktop Views client and generated connect...() function.

Referencing this package turns Bridge generation on, as the other host adapters do. In a single-project application, declare the View next to the WPF code and keep the frontend in Frontend/: the build writes the client to Frontend/src/generated, builds the frontend and copies Frontend/dist to www/. WPF's temporary *_wpftmp markup-compile project reuses the generated code instead of generating again. Views and ViewModels cannot depend on WPF types; the generator skips such types. A ViewModel may still have WPF-typed members and methods that the Bridge does not expose, such as a [RunicIgnore] ImageSource; one that the Bridge would expose is reported as RUNICBRIDGE005 at that member. When the Views live in a WPF-free model project that references Runic.Application.Views, that project generates and builds the frontend, and the WPF application copies its dist to www/. The application can then set RunicApplicationFrontendGenerationEnabled to false. If the model's composition has the application's default name (<application project>.RunicBridgeComposition, common in apps from 0.7.0-preview.6), the application's AddRunicViews() call uses it. An application that also declares Views gets RUNICBRIDGE018 until one of the two compositions is renamed. See the Runic.Application.Views README's build properties for the frontend folder and diagnostics.

The adapter uses DesktopHost's existing asset server, authenticated surface, WebSocket transport and Views session. Its HwndHost reuses Runic Desktop's native WebView2 controller and native loader, including origin-bound document start credentials and permission policy. Install Microsoft Edge WebView2 Runtime on the Windows machine. It creates no separate WPF window or UI thread.

CreateWpfViewAsync borrows the exact supplied model and service provider. It does not create or dispose a DI scope, ViewModel, navigator, or application-owned model context. Supply the shared dispatcher model context when navigation and web bindings present the same model. Disposing the child or unloading its control closes presentation admission promptly and drains accepted operations before releasing its session. Keep application services alive until that completion. Reloading the page reconnects within the same presentation; a new view after unload uses a fresh surface/session.

Each factory presents one surface at a time in its control. WPF owns layout, DPI, visibility, focus routing and the containing window. The child advertises focus only; it does not expose a top-level native handle or minimize, maximize, move, or resize the shell. Window layout options, including DesktopWindowOptions.Placement, report warnings and are configured on the WPF shell instead; the WPF Window saves and restores its own placement with RestoreBounds, WindowState and its LocationChanged, SizeChanged and StateChanged events, and the child presentation reports no DesktopWindow.State. Files dropped on the child WebView do not reach the WPF shell's Drop event, which the WebView's window covers; the borrowed ViewModel receives them, with their paths, when it implements IRunicFileDropTarget. Browser fallback is not supported by WpfBridgeView.OpenAsync. Standard HWND airspace constraints apply; WPF overlays cannot draw over the child WebView. Tab enters the document's first or last tab stop. WebView2 moves that focus asynchronously: the document's focused element changes shortly after TabInto returns, not during the call. Returning from the document's tab boundary to the surrounding WPF controls is not yet integrated; mouse focus and explicit shell focus actions remain available. Native authentication, reload, focus and unload checks run on Windows; Linux can compile the Windows target and run the linked portable session checks.

Bring the window forward for a second launch

A WPF application that keeps one copy running with ApplicationInstance.ClaimAsync brings its window forward when another launch arrives with WpfPlatformServices.PresentAsync(window), from any thread:

// For each launch the running instance receives:
var result = await WpfPlatformServices.PresentAsync(mainWindow);

It shows a hidden window, or one not shown yet, with Window.Show() and restores a minimized one to its previous size, then asks Windows to put it in the foreground on the window's dispatcher, as PlatformServices.PresentAsync does for Runic Desktop windows. Windows lets the running instance take the foreground because the forwarding launch allows it (AllowSetForegroundWindow); otherwise the taskbar button flashes and the result is Failed with PermissionDenied. A closed window, or one whose dispatcher shut down, reports OwnerClosed. Window.Activate() alone neither restores the window nor reports a refusal.

Product Compatible and additional computed target framework versions.
.NET net10.0-windows7.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0-preview.6 0 10/19/2026
0.8.0-preview.5 71 10/17/2026
0.8.0-preview.4 454 10/17/2026
0.8.0-preview.3 435 10/15/2026
0.8.0-preview.2 69 10/15/2026
0.8.0-preview.1 101 10/15/2026
0.7.0-preview.9 358 10/14/2026
0.7.0-preview.8 85 10/12/2026
0.7.0-preview.7 52 10/12/2026