Skip to content

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

@KlausLoeffelmann

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.

API Proposal

namespace System.Windows.Forms;

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

    public ContainerControl? ContainerControl { get; set; }
    public bool EscapeExitsFullScreen { get; set; }
    public bool HideTaskbar { get; set; }
    public bool IsFullScreen { get; }
    public int MousePointerAutoHideDelay { get; set; }
    public bool SuppressPowerSaving { get; set; }
    public Keys ToggleFullScreenKey { get; set; }
    public bool TopMostInFullScreen { get; set; }

    public event EventHandler? ContainerControlChanged;
    public event EventHandler? EscapeExitsFullScreenChanged;
    public event EventHandler? FullScreenChanged;
    public event EventHandler? HideTaskbarChanged;
    public event EventHandler? MousePointerAutoHideDelayChanged;
    public event EventHandler? SuppressPowerSavingChanged;
    public event EventHandler? ToggleFullScreenKeyChanged;
    public event EventHandler? TopMostInFullScreenChanged;
    public event KioskModeWakeupEventHandler? Wakeup;

    public void ToggleFullScreen();

    protected virtual void OnContainerControlChanged(EventArgs e);
    protected virtual void OnEscapeExitsFullScreenChanged(EventArgs e);
    protected virtual void OnFullScreenChanged(EventArgs e);
    protected virtual void OnHideTaskbarChanged(EventArgs e);
    protected virtual void OnMousePointerAutoHideDelayChanged(EventArgs e);
    protected virtual void OnSuppressPowerSavingChanged(EventArgs e);
    protected virtual void OnToggleFullScreenKeyChanged(EventArgs e);
    protected virtual void OnTopMostInFullScreenChanged(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);

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.IsFullScreen)
        {
            _kioskModeManager.ToggleFullScreen();
        }
    }
}

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 IsFullScreen plus ToggleFullScreen is sufficient for callers that need an explicit target state.
  • 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
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