How to Define Shared Application Blocks for WinForms and Blazor

A desktop application and a browser application may have the same business modules without having the same controls. Products, stocktakes and reporting still need navigation, screen registrations and a lifetime policy. A Windows tab and a Razor component, however, have different rendering and ownership rules. Share the application model while retaining the rendering and ownership requirements of each presentation technology.

Sphere10 separates shared application contracts from its WinForms and Blazor adapters. We will construct an inventory block with two screen definitions and two activation policies, then explain where the platform host takes over. The executable example exercises the shared metadata layer. It intentionally does not claim that a plain shared screen renders as a complete native or browser interface.

Shared application modules with distinct desktop and browser presentations.
Shared application modules with distinct desktop and browser presentations.

Contents

ℹ️ This article uses the shared application model in Sphere10.Framework.Application 3.1.3. Install the Application package for the shared metadata example, then reference the appropriate WinForms or Blazor adapter package in each platform host.

Separate Definitions from Instances

A screen definition says what can be opened: its screen type, title, activation policy and kind. A screen instance is the actual retained object hosted at runtime. A command or menu item refers to a definition and should not create another registration as a hidden side effect. That distinction prevents several menus from accidentally describing different copies of what the application intended to be one screen.

A block groups related registrations and navigation. A plugin can contribute several blocks and startup services. The running application provides the outer lifetime, while a platform screen host manages activation and retention. This hierarchy makes ownership visible instead of requiring each screen to scan a global collection of forms or resolve a hard-coded command string.

Build a Small Shared Block

The sample defines a block and two plain screens implementing the shared contract. Each screen exposes the Host and Block ownership slots, while its visual adapter supplies the platform-specific requirements. The complete downloadable project contains these small types separately, matching the source organisation used throughout the Framework.


using System;
using Sphere10.Framework.Application.UI;

namespace ArticleSample;

public static class Program {
	public static void Main() {
		// Shared metadata describes navigation without constructing native or web controls.
		var block = new InventoryBlock { Name = "Inventory", Position = 10 };
		var products = new ApplicationScreenDefinition(typeof(ProductsScreen), "Products", ScreenActivationMode.SingleInstance);
		var stocktake = new ApplicationScreenDefinition(typeof(StocktakeScreen), "Stocktake", ScreenActivationMode.MultiInstance);
		block.AddScreenDefinition(products);
		block.AddScreenDefinition(stocktake);
		block.DefaultScreenDefinition = products;
		ApplicationScreenDefinition.ValidateDefinitions(block.ScreenDefinitions);
		Console.WriteLine($"Block: {block.Name}");
		Console.WriteLine($"Screens: {block.ScreenDefinitions.Length}");
		Console.WriteLine($"Default: {block.DefaultScreenDefinition.Title}");
		Console.WriteLine($"Products policy: {products.ActivationMode}");
		Console.WriteLine($"Stocktake policy: {stocktake.ActivationMode}");
	}
}

Products is single-instance because the example treats the product browser as a retained workspace view. Stocktake is multi-instance because several stocktake documents might be open independently. Those are application decisions, not universal rules attached to the class names. Another product can choose different policies through its own registrations.

The shared verification output is:


Block: Inventory
Screens: 2
Default: Products
Products policy: SingleInstance
Stocktake policy: MultiInstance

The program validates the definitions before reporting them. That proves the shared registration model can represent the intended module. It is not a screenshot test of tabs, a browser-circuit test or a replacement for launching a host. The distinction keeps a small executable example useful without inflating its acceptance claim.

Add the Appropriate Presentation Host

For WinForms, derive real native screens from the adapter's ApplicationScreen, register them through its block builder and use the existing application runner and main form. Native controls, images, designer resources and UI-thread dispatch remain native responsibilities. A shared interface does not make a user control safe to construct on a worker thread.

For Blazor, derive Razor screens from the web adapter's ApplicationScreen and register the workspace through the existing Blazor application builder. The renderer creates and disposes components, while the host coordinates activation, navigation and retained sessions. Browser routes and circuit-scoped state belong to the web adapter, not to a new global form registry.

Both adapters can consume the same core IDataSource<T> contract where the business operation is genuinely shared. The application still owns persistence, permissions, validation and domain rules. Share those services deliberately, keeping product-specific decisions in the application even when both presentations call them.

Make Lifetime Policies Explicit

💡 Hiding a retained screen is not the same event as destroying it. Tie timers, subscriptions and worker leases to the lifetime they actually serve.

One screen can own a view subscription while the application owns the underlying shared data provider. Closing a view must not dispose a provider still used by another view. An unsaved-change guard decides whether navigation may proceed. Release resources only after the guards have accepted the transition.

Startup registration and runtime execution are also different moments. Configure plugin services through the supported registration callback, then resolve per-user or scoped services through the executing application's provider. Capturing a circuit-specific service in a singleton definition would cross the very boundary the shared model is intended to clarify.

Take the Next Step

Start with one useful block, two screens and one real data source. Add a second presentation only when the product needs it. The shared Application guide , Blazor workspace guide and WinForms guide provide the concrete host APIs and demonstrations. Together they show how Sphere10 Framework can supply consistent application composition while each platform retains its own native strengths.

Download the Example

Download the complete NuGet sample project. The archive contains the project, its model files and run instructions. Keep the pinned package version when reproducing the example.

sample.zip
Download the complete worked example and build instructions (ZIP).

Continue the Series

How to Build a Blazor Business Screen with Custom Editors

How to Build a Wizard That Changes Its Route at Runtime

How to Build an Embedded Object Database in C#