Skip to content

[.NET 11 API Proposal] Introduce KioskModeManager component #14586

Description

@KlausLoeffelmann

Rationale

WinForms has always been used far beyond classic back-office desktop forms. Many production applications run as a single, unattended workstation for production data acquisition: booking times, reading data from production machines, or working with I/O cards to count, measure, weigh, and convert physical signals. WinForms is also used for display machines and information boards such as timetables in railway stations, bus stations, airports, advertising displays, stadiums, theaters, malls, and public directories. It is also widely used for point-of-sales and ticketing UIs, for example in public transportation, clubs, swimming halls, and other public venues.

These scenarios often need a predictable kiosk-style surface: a fullscreen form, optional taskbar coverage, controlled topmost behavior, suppressed display/system sleep, mouse pointer hiding after inactivity, and reliable notification when the user or system wakes the kiosk experience. Customers have often solved these needs with outlandish or fragile approaches that fight the shell, power management, input routing, or modern Windows behavior. Those workarounds can become migration blockers when customers move long-lived WinForms applications to newer Windows versions.

KioskModeManager provides a first-party WinForms component for these scenarios. It gives applications a designer-friendly way to manage the common kiosk mechanics while preserving WinForms composition patterns. The component intentionally exposes ContainerControl instead of Form so it can be placed on either a Form or a UserControl at design time. A component placed on a UserControl can resolve the containing form at runtime, which keeps designer behavior consistent across common WinForms layouts. When the component is sited (for example, dropped onto a designer surface) and ContainerControl has not been set explicitly, it automatically resolves the root component of the designer host (typically the owning Form or UserControl) and assigns it as the ContainerControl. An explicitly assigned ContainerControl is never overwritten.

The FullScreen and WakeUpCommand members are bindable so the component fits modern, MVVM-style WinForms applications: FullScreen supports two-way data binding to a view-model state, and WakeUpCommand lets a view-model react to wakeup activity without subscribing to the Wakeup event in code-behind.

API Proposal

namespace System.Windows.Forms;

using System.Windows.Input;

public class KioskModeManager : Component, ISupportInitialize
{
    public KioskModeManager();
    public KioskModeManager(IContainer container);

    public ContainerControl? ContainerControl { get; set; }
    public bool EscapeExitsFullScreen { get; set; }

    [Bindable(true)]
    public bool FullScreen { get; set; }

    public bool HideTaskbar { get; set; }
    public int MousePointerAutoHideDelay { get; set; }
    public bool SuppressPowerSaving { get; set; }
    public Keys ToggleFullScreenKey { get; set; }
    public bool TopMostInFullScreen { get; set; }

    [Bindable(true)]
    public ICommand? WakeUpCommand { get; set; }

    public override ISite? Site { get; set; }

    public event EventHandler? ContainerControlChanged;
    public event EventHandler? FullScreenChanged;
    public event KioskModeWakeupEventHandler? Wakeup;

    public void ToggleFullScreen();

    protected virtual void OnContainerControlChanged(EventArgs e);
    protected virtual void OnFullScreenChanged(EventArgs e);
    protected virtual void OnWakeup(KioskModeWakeupEventArgs e);
}

public enum KioskModeWakeupSource
{
    Keyboard = 0,
    Mouse = 1,
    PowerResume = 2,
    Session = 3,
}

public class KioskModeWakeupEventArgs : EventArgs
{
    public KioskModeWakeupEventArgs(KioskModeWakeupSource source);
    public KioskModeWakeupSource Source { get; }
}

public delegate void KioskModeWakeupEventHandler(object? sender, KioskModeWakeupEventArgs e);

Changes from the original proposal

  • The read-only IsFullScreen property is replaced by a bindable, read/write FullScreen property so it can participate in two-way data binding. Setting FullScreen = true enters fullscreen and FullScreen = false restores the saved form state.
  • A bindable WakeUpCommand (ICommand) is added. It is executed on every Wakeup, receiving the wakeup source name (KioskModeWakeupSource.ToString()) as the command parameter so the command stays independent of the WinForms-specific enum type.
  • The per-property …Changed events and their On…Changed methods are removed for EscapeExitsFullScreen, HideTaskbar, MousePointerAutoHideDelay, SuppressPowerSaving, ToggleFullScreenKey, and TopMostInFullScreen. Only ContainerControlChanged, FullScreenChanged, and Wakeup remain.
  • Site is overridden so that, when the component is sited and ContainerControl has not been set explicitly, the designer host's root component is assigned as the ContainerControl.

API Usage

public partial class MainForm : Form
{
    private readonly KioskModeManager _kioskModeManager;

    public MainForm()
    {
        InitializeComponent();

        _kioskModeManager = new KioskModeManager
        {
            ContainerControl = this,
            HideTaskbar = true,
            TopMostInFullScreen = true,
            EscapeExitsFullScreen = true,
            MousePointerAutoHideDelay = 3000,
            SuppressPowerSaving = true,
        };

        _kioskModeManager.Wakeup += (sender, e) =>
        {
            if (e.Source is KioskModeWakeupSource.Mouse or KioskModeWakeupSource.Keyboard)
            {
                // Refresh kiosk UI, restart inactivity timers, or check whether fullscreen should remain active.
            }
        };

        if (!_kioskModeManager.FullScreen)
        {
            _kioskModeManager.ToggleFullScreen();
        }
    }
}

MVVM-style usage with data binding:

// Two-way binding of the fullscreen state and a command that runs on every wakeup.
_kioskModeManager.DataBindings.Add(
    nameof(KioskModeManager.FullScreen),
    viewModel,
    nameof(MainViewModel.IsKioskFullScreen),
    formattingEnabled: false,
    DataSourceUpdateMode.OnPropertyChanged);

_kioskModeManager.WakeUpCommand = viewModel.WakeUpCommand;

Alternative Designs

  • Expose a Form property instead of ContainerControl. This was rejected because the component can be placed on a UserControl at design time and resolve the containing form later.
  • Expose separate EnterFullScreen and ExitFullScreen methods. This was rejected because the bindable FullScreen property plus ToggleFullScreen is sufficient for callers that need an explicit target state.
  • Keep IsFullScreen as a read-only property. This was rejected because a read/write, bindable FullScreen property enables two-way data binding for MVVM-style applications.
  • Expose per-property …Changed events for every setting. This was rejected as unnecessary API surface; data binding for the bindable members and the FullScreenChanged/ContainerControlChanged events cover the meaningful change scenarios.
  • Use only the form's KeyDown event and KeyPreview. This is simple but invasive because it changes form keyboard routing and does not provide a general wakeup signal for mouse or power/session activity.
  • Use a simple EventHandler for Wakeup. A dedicated event args type is preferred so applications can distinguish keyboard, mouse, power resume, and session activity.
  • Include voice wake, network wake, or explicit wake timers in the first version. These were excluded because voice and network wake are hardware/OS/application-specific, and wake timers require a separate native timer/power-policy design.

Risks

  • Incorrect native handle lifetime could leak power request or hook handles; implementation must close handles and unhook deterministically.
  • Cursor hiding must remain balanced because Cursor.Hide/Cursor.Show affect an internal display counter.
  • Hook/message observation can affect input performance or behavior if implemented globally; thread-scoped observation is preferred.
  • Power management APIs may fail due to OS policy or permissions and must not leave the component in a misleading partial state.

Will this feature affect UI controls?

Yes. The component changes fullscreen state for the resolved form, can hide the mouse pointer while fullscreen, and can suppress display/system sleep. Designer support is required through localized property categories/descriptions, CodeDOM serialization-safe defaults, and behavior that works when the component is dropped on either a Form or a UserControl. No new localized runtime UI text is expected beyond design-time descriptions and exception messages.

Status Checklist

  • API proposal has api-suggestion label
  • Rationale, API Proposal, API Usage, and Risks sections are complete
  • API shape has been discussed with the team
  • Review the issue for compatibility with what the API review board expects
  • Change label to api-ready-for-review
  • If late in the release cycle, also add the blocking label to expedite the review appointment
  • API review completed — label changed to api-approved

Metadata

Metadata

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions