πŸͺŸ Glass.Message β€” Modern MessageBox Replacement for WinForms (C#/.NET)

Glass.Message is a C# Windows Forms dialog library for .NET Framework 4.8.1 and .NET 8, 9 and 10. It provides themed message dialogs, inline inputs, progress dialogs and toast notifications.

Mica & Acrylic backdrops Β· automatic dark/light theming Β· fluent builder Β· async dialogs Β· inline inputs Β· progress Β· toasts Β· full RTL


Latest version Downloads Platform .NET UI License


⬇️ Download the latest release Β· πŸ“œ Changelog


Glass.Message feature gallery

Themed dialogs, inline inputs, progress and toast notifications.


✨ The one-line pitch

For supported MessageBox.Show(...) overloads, use GlassMessage.Show(...) to display a themed, DPI-aware dialog. Install the package and import its namespace as shown in the quick start, then choose a standard overload or use the fluent builder for additional options.

😴 Before β€” MessageBox πŸͺŸ After β€” GlassMessage
MessageBox.Show(
    "The printer is offline.",
    "Printer Offline",
    MessageBoxButtons.OK,
    MessageBoxIcon.Warning);
GlassMessage.Show(
    "The printer is offline.",
    "Printer Offline",
    MessageBoxIcon.Warning);
Flat, dated, fixed grey chrome Glass.Message dialog

The Show(...) overloads are signature-compatible with System.Windows.Forms.MessageBox, so most call sites compile untouched and still return a DialogResult.


πŸ“Š Why Glass.Message?

Β  MessageBox Glass.Message
Drop-in Show(...) API βœ… βœ…
Dark / Light / custom themes ❌ βœ…
Auto-follow OS theme ❌ βœ…
Windows 11 rounded corners + Mica ❌ βœ…
Open / close animations ❌ βœ…
Inline text / password / dropdown input ❌ βœ…
Checkbox (β€œdon’t show again”) ❌ βœ…
Progress bar (in the dialog) ❌ βœ…
Live progress updates (controller) ❌ βœ…
Expandable β€œShow details” panel ❌ βœ…
Countdown auto-close ❌ βœ…
Custom bitmap icon ❌ βœ…
async / awaitable + cancellable (incl. rich dialogs) ❌ βœ…
Toast notifications (multi-monitor aware) ❌ βœ…
Full RTL layout ⚠️ partial βœ…
Ctrl+C copies message βœ… βœ…

πŸ†• What’s new in v1.0.6

A quality, performance, and concurrency hardening release β€” no new features, no public API changes. All of the fixes below were verified with real stress/concurrency tests and live-rendered DPI screenshots, not code review alone.

Area Change
Concurrency GlassProgressController.SetValue/SetMessage/SetActivity now coalesce rapid or concurrent calls so at most one UI update per kind is ever queued, instead of flooding the UI thread with one BeginInvoke per call under high-frequency updates. Verified with 6 concurrent worker threads plus a live message pump: no deadlock, no hang, no cross-thread exception.
Async lifecycle Verified that cancellation before showing, immediately after showing, while visible/animating, concurrently with a button click, and during owner disposal all reach a terminal task state β€” no hangs.
Memory / GDI+ Toast notifications and the dialog’s close-button/countdown-ring pens no longer allocate new paths/brushes/pens on every animation frame or repaint β€” they’re now built once and reused, like GlassDialog/GlassButton already do. Verified GDI/USER handle counts stay bounded across 300 dialog and 200 toast create/dispose cycles.
Rendering / DPI Verified from actual rendered screenshots that dialogs rescale cleanly with sharp corners, crisp borders, and no artifacts at 100%, 125%, 150%, 175%, and 200% DPI.
Reliability Verified no stale toast entries after 25 simultaneous and 50 rapid-sequential toast show/dismiss cycles, and no corruption when a DPI change fires while progress updates and an open toast are concurrently active.

See the full CHANGELOG for details, including the prior v1.0.5 progress-animation release and v1.0.4 activity-aware progress release.

// Live progress now glides between values instead of jumping:
var progress = GlassMessage.Create("Uploading to OneDrive…")
    .Title("Backup")
    .Progress(0, 100)
    .ProgressActivity(GlassProgressActivity.Upload)   // glowing packets flow β†’
    .Buttons("Cancel")
    .ShowProgress();

progress.SetValue(60);                                // fill eases up smoothly
progress.SetActivity(GlassProgressActivity.Sync);     // ripple eases both ways

Fluent builder + custom buttons
Fluent builder
Inline text input
Text input
Password + reveal eye + Caps Lock hint
Password input
Drop-down picker
Dropdown input
Expandable detail (stack traces)
Detail panel
Determinate progress
Progress bar
Countdown auto-close
Countdown
"Don't show again" checkbox
Checkbox
Async builder β€” awaitable rich dialogs
Async builder
Live progress controller
Live progress controller

Toasts β€” non-modal, auto-stacking notifications at any corner

Toasts at four corners


🎨 Five themes out of the box

Dark
Dark
Light
Light
Mica
Mica
High Contrast
High Contrast
Windows Classic
Classic
…or build your own
Custom GlassTheme β†’
GlassMessage.Create("Mica backdrop applied.")
    .Title("Mica Backdrop Active")
    .Icon(MessageBoxIcon.Information)
    .Theme(GlassTheme.Mica)   // GlassTheme.Dark (= Default) Β· Light Β· Mica Β· HighContrast Β· WindowsClassic
    .Show();

πŸ“‘ Table of Contents

Features Β· Installation Β· Quick Start Β· Usage Guide Β· Demo Walkthrough Β· API Β· Configuration Β· Styling Β· Best Practices Β· Structure Β· Requirements Β· Run the Demo Β· FAQ Β· Full Example


πŸš€ Features

Everything below is backed by real code in Glass.Message and shown live in Glass.Demo.

Category Capability
Drop-in API GlassMessage.Show(...) overloads that mirror MessageBox.Show exactly
Fluent builder GlassMessage.Create(...) for composable dialogs
Async ShowAsync(...) on the facade and the builder (ShowAsync() / ShowExAsync()) β€” non-blocking, awaitable, cancellable
Rich result ShowEx() / ShowExAsync() return button + checkbox state + typed input
Live progress ShowProgress() β†’ a thread-safe GlassProgressController you update while work runs
Activity-aware progress ProgressActivity(...) animates the bar to match the operation (upload, download, sync, compress, …) β€” 6 distinct families, switchable live
Theming Dark, Light, Mica, HighContrast, WindowsClassic + custom themes
Auto theme GlassTheme.AutoDetect() follows the Windows light/dark/HC setting
Modern chrome Windows 11 rounded corners (DWM), Mica backdrop, Acrylic fallback
Inputs Single-line, password (reveal + configurable Caps Lock hint), multi-line, drop-down
Checkbox β€œDon’t show again”-style opt-in under the message
Detail panel Expandable β€œShow details” for stack traces / diagnostics
Progress Determinate and indeterminate (marquee) bars, with optional activity-matched flow animations
Countdown Auto-confirm the default button with a live circular countdown
Custom icon Any 48Γ—48 Bitmap (e.g. a product logo)
System sounds Sound() / GlassMessage.PlaySystemSounds play the icon’s Windows sound on open
Animations Fade (default), SlideDown, Scale, None
Keyboard Ctrl+C copies title + message; Enter/Esc map to sensible results
RTL Full right-to-left mirrored layout
Toasts Six anchor positions, auto-stacking, multi-monitor aware, click actions, async variant
Cross-target .NET Framework 4.8.1 and .NET 8 / 9 / 10 (Windows)

πŸ“¦ Installation

Option A β€” Download from Releases (no account, no NuGet needed)


Grab the latest build straight from the repo’s Releases page β†’ Latest. Each release includes:

Download Use it for
Glass.Message.<ver>.nupkg The NuGet package β€” all four frameworks in one file (AnyCPU: x86 + x64)
Glass.Message-<ver>-net481.zip Just the .NET Framework 4.8.1 DLL + XML docs (AnyCPU)
Glass.Message-<ver>-net8.0-windows.zip Just the .NET 8 DLL (AnyCPU)
Glass.Message-<ver>-net9.0-windows.zip Just the .NET 9 DLL (AnyCPU)
Glass.Message-<ver>-net10.0-windows.zip Just the .NET 10 DLL (AnyCPU)
Glass.Demo-<ver>-win-x64.zip The runnable demo gallery, 64-bit β€” unzip and run Glass.Demo.exe (no .NET install required)
Glass.Demo-<ver>-win-x86.zip The runnable demo gallery, 32-bit β€” for older / x86 Windows

To use a raw DLL, unzip it and reference Glass.Message.dll from your project:

<ItemGroup>
  <Reference Include="Glass.Message">
    <HintPath>libs\Glass.Message.dll</HintPath>
  </Reference>
</ItemGroup>
Option B β€” Project reference (how the demo is wired today)


<ItemGroup>
  <ProjectReference Include="..\Glass.Message\Glass.Message.csproj" />
</ItemGroup>
dotnet add reference ..\Glass.Message\Glass.Message.csproj
Option C β€” NuGet package (local or private feed)


The downloadable .nupkg carries full NuGet metadata (PackageId = Glass.Message, MIT-licensed). Drop it into a local folder feed, then:

dotnet add package Glass.Message
Install-Package Glass.Message   # Package Manager Console

Then add a single using Glass; and you’re ready. See the CHANGELOG for what’s new in each version.


⚑ Quick Start

using Glass;
using System.Windows.Forms;

// 1) (Optional) set global defaults once at startup, e.g. in Program.Main():
GlassMessage.UseRoundedCorners = true;                    // Windows 11 rounded corners
GlassMessage.DefaultTheme      = GlassTheme.AutoDetect(); // follow the OS theme

// 2) Show a basic, MessageBox-style dialog:
DialogResult result = GlassMessage.Show(
    "Your changes have been saved.",
    "Success",
    MessageBoxIcon.Information);

// 3) React to the result exactly like a classic MessageBox:
if (result == DialogResult.OK) { /* ... */ }

Expected behaviour: a themed dialog fades in, centred on the screen containing the mouse cursor (multi-monitor aware), with a glossy OK button. Enter, OK, or Esc all return DialogResult.OK for a single-button dialog.


πŸ“š Complete Usage Guide

Grouped and collapsible β€” expand what you need.

1 Β· Drop-in replacement β€” Show


All the classic MessageBox overloads exist.

GlassMessage.Show(
    "The selected printer 'HP LaserJet Pro M404dn' is offline.\n" +
    "Check that it is powered on and connected to the network, then try again.",
    "Printer Offline",
    MessageBoxIcon.Warning);

Params: message, title, icon, buttons, defaultButton, optional owner/theme. Best practice: keep using the returned DialogResult exactly as before.

2 Β· Fluent builder β€” Create(...)


Fluent builder

Every method returns the builder, so calls chain.

GlassMessage.Create(
        "Annual_Report_Q4_2025.xlsx has unsaved changes.\n\n" +
        "Closing now will discard every edit made since the last save.")
    .Title("Unsaved Changes")
    .Icon(MessageBoxIcon.Warning)
    .Buttons("Save", "Don't Save", "Cancel")
    .Default(MessageBoxDefaultButton.Button1)
    .Show();

Custom button mapping β€” the number of labels picks the nearest standard layout, so each click still maps to a meaningful DialogResult:

Labels Layout Returned results (in order)
1 OK OK
2 OK / Cancel OK, Cancel
3+ Yes / No / Cancel Yes, No, Cancel

⚠️ With three labels, the first button returns DialogResult.Yes (not OK). Always confirm which result your labels map to.

3 Β· Auto-detect the OS theme


Auto theme

var theme    = GlassTheme.AutoDetect();
var modeName = GlassTheme.IsSystemDark() ? "Dark" : "Light";

GlassMessage.Create($"Windows is currently in {modeName} mode…")
    .Title("System Theme Detected")
    .Icon(MessageBoxIcon.Information)
    .Theme(theme)
    .Show();

Helpers: GlassTheme.AutoDetect() (returns a preset) Β· GlassTheme.IsSystemDark() (bool). Best practice: set GlassMessage.DefaultTheme = GlassTheme.AutoDetect(); once at startup.

4 Β· Countdown auto-close β€” AutoClose(...)


Countdown

Auto-confirms the default button after a delay, with a live circular countdown and a (Ns) suffix.

GlassMessage.Create("Your Contoso Portal session is about to expire.")
    .Title("Session Expiring β€” Contoso Portal")
    .Icon(MessageBoxIcon.Warning)
    .Buttons("Stay Signed In", "Sign Out Now")
    .AutoClose(10_000)                 // 10 seconds
    .Animation(GlassAnimation.SlideDown)
    .Show();

Params: AutoClose(milliseconds) (clamped β‰₯ 0); target = the Default(...) button.

5 Β· Checkbox β€” CheckBox(...) + ShowEx()


Checkbox

var r = GlassMessage.Create("Drive C: has only 4.2 GB of free space remaining.")
    .Title("Low Disk Space β€” C:\\")
    .Icon(MessageBoxIcon.Warning)
    .Buttons("Open Disk Cleanup", "Dismiss")
    .CheckBox("Don't warn me again for drive C:\\")
    .ShowEx();

if (r.CheckBoxChecked) { /* persist the preference */ }

Params: CheckBox(label, defaultChecked = false). Use ShowEx() to read GlassResult.CheckBoxChecked.

6 Β· Inputs β€” text Β· password Β· dropdown Β· multiline


Text
Text
Password (Caps Lock hint on)
Password
Password (Caps Lock hint off)
Password without Caps Lock hint
Drop-down
Dropdown
// Single-line text
var r = GlassMessage.Create("Enter a new name for the selected folder.")
    .Title("Rename Folder")
    .Icon(MessageBoxIcon.Question)
    .InputText("Folder name", "Client Projects β€” Q1 2026")
    .Buttons("Rename", "Cancel")
    .ShowEx();
if (r.Button == DialogResult.OK && !string.IsNullOrWhiteSpace(r.InputText)) { /* r.InputText */ }

// Masked password β€” reveal eye + Caps Lock badge are shown by default
GlassMessage.Create("Authentication is required to connect to Contoso ERP.")
    .Title("Sign In β€” Contoso ERP")
    .InputPassword("Active Directory password")
    .Buttons("Connect", "Cancel")
    .ShowEx();

// Suppress the Caps Lock badge (e.g. kiosk / PIN entry where it's not helpful)
GlassMessage.Create("Enter your PIN to unlock the device.")
    .Title("Device Locked")
    .InputPassword("PIN", showCapsLockHint: false)
    .Buttons("Unlock", "Cancel")
    .ShowEx();

// Drop-down picker
GlassMessage.Create("Choose the output format.")
    .Title("Export Document")
    .InputDropdown(
        ["PDF β€” Portable Document Format",
         "Word (.docx) β€” Microsoft Word",
         "Excel (.xlsx) β€” Microsoft Excel"],
        "PDF β€” Portable Document Format")   // pre-selected
    .Buttons("Export", "Cancel")
    .ShowEx();

Methods: InputText(placeholder, defaultValue) Β· InputPassword(placeholder, showCapsLockHint = true) Β· InputMultiline(placeholder, defaultValue) Β· InputDropdown(items, defaultItem). Read back: GlassResult.InputText (never null). Best practice: treat password InputText as a secret β€” don’t log it. Set showCapsLockHint: false to hide the Caps Lock badge (e.g. for PIN or kiosk flows).

7 Β· Expandable detail β€” Detail(...)


Detail

Hides verbose diagnostics behind a β€œShow details ▼” link.

GlassMessage.Create("OneDrive failed to sync 'Annual_Report_Q4_2025.xlsx'.")
    .Title("Sync Error β€” OneDrive")
    .Icon(MessageBoxIcon.Error)
    .Detail(
        "System.IO.IOException: The process cannot access the file ...\n" +
        "Win32 error:     0x80070020 β€” ERROR_SHARING_VIOLATION\n" +
        "Correlation ID:  a3f8b2e1-4d9c-47f2-8b1a-c6e5d0f92341")
    .Buttons(MessageBoxButtons.RetryCancel)
    .Show();

Best practice: user-facing guidance in the message; raw diagnostics in Detail(...).

8 Β· Progress β€” determinate, indeterminate & activity-aware


Determinate
Determinate
Indeterminate (marquee)
Indeterminate
GlassMessage.Create("Backing up 'Documents' to OneDrive…")
    .Title("OneDrive Backup in Progress")
    .Progress(75, 100)                 // determinate
    .Buttons("Cancel Backup")
    .Show();

GlassMessage.Create("Verifying your Microsoft 365 licence…")
    .Title("Activating Microsoft 365")
    .ProgressIndeterminate()           // marquee
    .Buttons("Cancel")
    .Show();

GlassMessage.Create("Downloading update package…")
    .Title("Downloading Update")
    .Progress(40, 100)
    .ProgressActivity(GlassProgressActivity.Download)   // backward-flowing "incoming" stripes
    .Buttons("Cancel")
    .Show();

Methods: Progress(value, max = 100) Β· ProgressIndeterminate() Β· ProgressActivity(activity).

ProgressActivity layers an animated overlay on either bar so it matches the operation in progress. Activities are grouped into visual families, each with its own distinct animation; direction and speed then give every activity its own character (e.g. Upload and Download are the same glowing-packet style flowing opposite ways):

Family Animation Activities
Packets glowing dots glide along the bar Upload (β†’) Β· Download (←) Β· Stream
Chevrons diagonal barber-pole bands march FileTransfer (β†’) Β· Import (←)
Segments rounded blocks march (pack/unpack) Compress (β†’) Β· Extract (←) Β· Export
Wave a sinusoidal brightness ripple Backup (β†’) Β· Restore (←) Β· Sync (sloshes both ways)
Pulse the fill breathes in and out Encrypt Β· Decrypt Β· Connecting
Comet a soft shine sweeps across Install Β· Search Β· Processing
Download flow (incoming packets)
Download activity flow
Activity gallery β€” every family
Activity gallery

Use None for a plain bar. Change the activity live from the controller via SetActivity(...) as an operation moves between phases (e.g. Compress β†’ Upload).

9 Β· Custom bitmap icon β€” Icon(Bitmap)


Custom icon

using var bmp = new Bitmap(48, 48);
using (var g = Graphics.FromImage(bmp))
{
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.FillEllipse(Brushes.DodgerBlue, 2, 2, 44, 44);
    g.DrawString("G", new Font("Segoe UI", 26f, FontStyle.Bold), Brushes.White, new PointF(10, 6));
}

GlassMessage.Create("Your custom branding has been applied.")
    .Title("Glass.Message β€” Ready")
    .Icon(bmp)                         // Bitmap overload
    .Buttons(MessageBoxButtons.OK)
    .Show();

Best practice: dispose the bitmap with using after the dialog returns.

10 Β· Keyboard β€” Ctrl+C, Enter, Esc


Ctrl+C copy

No code required:

  • Ctrl+C copies the title + message to the clipboard (great for support tickets).
  • Enter activates the default button.
  • Esc (or Γ—) closes with the least-destructive result for the current button set.
11 Β· Animations β€” Animation(...)


SlideDown
SlideDown
Scale
Scale
GlassMessage.Create("Glass.Message 1.0 is available.")
    .Title("Update Available")
    .Buttons("Install Now", "Later")
    .Animation(GlassAnimation.SlideDown)   // Fade (default) Β· SlideDown Β· Scale Β· None
    .Show();

Best practice: use GlassAnimation.None in automated UI tests for deterministic timing.

12 Β· Rich result β€” ShowEx()


ShowEx rich result

ShowEx() returns a GlassResult with the button + checkbox + input at once.

var r = GlassMessage.Create("Adobe Acrobat Reader DC will be permanently removed.")
    .Title("Uninstall Adobe Acrobat Reader DC")
    .Icon(MessageBoxIcon.Warning)
    .Buttons("Uninstall", "Repair", "Cancel")   // β†’ Yes / No / Cancel
    .CheckBox("Also remove personal settings and reading history")
    .ShowEx();

if (r.Button != DialogResult.Cancel)            // user chose Uninstall (Yes) or Repair (No)
{
    bool alsoRemovePrefs = r.CheckBoxChecked;
    // ...
}

The Security Alert demo uses the same pattern β€” the second button (Secure account) returns DialogResult.No:

Security alert

var r = GlassMessage.Create("We noticed a new sign-in to your Contoso account.")
    .Title("New Sign-in to Your Account")
    .Icon(MessageBoxIcon.Warning)
    .Buttons("This was me", "Secure account", "Review activity")
    .ShowEx();

if (r.Button == DialogResult.No) { /* "Secure account" */ }
13 Β· Async, non-blocking β€” ShowAsync(...)


Async

Shows without blocking the UI thread; await the chosen button. A CancellationToken closes it (β†’ DialogResult.Cancel).

private static async void SyncWorkspace()
{
    var r = await GlassMessage.ShowAsync(
        "Your local workspace has uncommitted changes.",
        "Sync Workspace β€” Contoso DevOps",
        MessageBoxIcon.Question,
        MessageBoxButtons.OKCancel);

    GlassMessage.Show(r == DialogResult.OK ? "Push started." : "Push skipped.",
                      r == DialogResult.OK ? "Pushing…" : "Sync Deferred",
                      MessageBoxIcon.Information);
}

Signature: Task<DialogResult> ShowAsync(string message, string title = "", MessageBoxIcon icon = None, MessageBoxButtons buttons = OK, CancellationToken ct = default) (+ a theme-aware overload). Rich dialogs: for awaitable dialogs with input / checkbox / dropdown, use the builder’s ShowAsync() / ShowExAsync() β€” see Β§17.

14 Β· Right-to-left β€” RightToLeft()


RTL

GlassMessage.Create("فشل حفظ الملف: ΨͺΩ‚Ψ±ΩŠΨ±_Ψ§Ω„Ψ±Ψ¨ΨΉ_Ψ§Ω„Ψ±Ψ§Ψ¨ΨΉ.docx")
    .Title("فشل الحفظ β€” Ψ§Ω„Ω‚Ψ±Ψ΅ Ω…Ω…ΨͺΩ„Ψ¦")
    .Icon(MessageBoxIcon.Error)
    .Buttons(MessageBoxButtons.RetryCancel)
    .RightToLeft()
    .Show();
15 Β· Real-world compositions


Windows Update β€” Release Notes
Release notes
Storage Migration Wizard
Migration

The EULA dialog pairs a Detail(...) panel with a required CheckBox(...):

EULA

GlassMessage.Create("Please review and accept the licence terms to continue.")
    .Title("End-User Licence Agreement")
    .Icon(MessageBoxIcon.Information)
    .Detail("CONTOSO SUITE 2026 β€” END-USER LICENCE AGREEMENT\n...")
    .CheckBox("I have read and accept the licence terms")
    .Buttons("Accept and install", "Decline")
    .Show();

The Migration Wizard combines a drop-down, a checkbox, and a follow-up progress dialog:

var r = GlassMessage.Create("The Storage Migration Wizard will move your user profile…")
    .Title("Storage Migration Wizard")
    .Icon(MessageBoxIcon.Question)
    .InputDropdown(
        ["Move profile and verify (recommended)",
         "Move profile without verification (faster)",
         "Copy only β€” keep originals in place"],
        "Move profile and verify (recommended)")
    .CheckBox("Restart automatically when the migration finishes")
    .Buttons("Start migration", "Cancel")
    .ShowEx();

if (r.Button == DialogResult.OK)
{
    GlassMessage.Create($"Strategy: {r.InputText}\nAuto-restart: {(r.CheckBoxChecked ? "Yes" : "No")}")
        .Title("Migration Started")
        .Progress(12, 100)
        .Buttons("Run in background")
        .Show();
}
16 Β· Toasts β€” GlassToast


Single
Single toast
Auto-stacking
Stacking toasts
// Single, fully-configured toast
GlassToast.Show(new GlassToastOptions
{
    Message    = "Invoice_March_2026.pdf saved to SharePoint Β· Finance",
    Title      = "Upload Complete",
    Icon       = MessageBoxIcon.Information,
    DurationMs = 4_000,
    Position   = ToastPosition.BottomRight,
});

// Fire several β€” they stack and re-pack automatically
GlassToast.Show("Build succeeded β€” 0 errors, 2 warnings", "Glass.Message", MessageBoxIcon.Information);
GlassToast.Show("50 / 50 tests passed Β· Coverage 91.4 %",  "Test Run Complete", MessageBoxIcon.Information);
GlassToast.Show("Deploying to staging-01.contoso.com…",    "CI/CD Pipeline", MessageBoxIcon.Warning);

Positions: BottomRight (default), BottomLeft, TopRight, TopLeft, BottomCenter, TopCenter. Click action: set OnClick β€” the toast runs your action and dismisses. Await one: await GlassToast.ShowAsync(options) completes when it closes. Multi-monitor: toasts auto-target the active window’s screen (then the cursor’s, then primary) and stack per-screen. Pin one to a specific monitor with Screen:

GlassToast.Show(new GlassToastOptions
{
    Message  = "Render finished on the second display.",
    Title    = "Done",
    Icon     = MessageBoxIcon.Information,
    Position = ToastPosition.TopRight,
    Screen   = Screen.AllScreens[1],   // null = auto (active window β†’ cursor β†’ primary)
});
17 Β· Async builder β€” ShowAsync() / ShowExAsync()


Async builder

The whole builder is awaitable too β€” so input / checkbox / dropdown dialogs run without blocking the UI thread. ShowExAsync() returns the full GlassResult; both accept a CancellationToken (cancelling yields DialogResult.Cancel).

var r = await GlassMessage.Create("Create a shared link for 'Annual_Report_Q4_2025.xlsx'.")
    .Title("Create Shared Link")
    .Icon(MessageBoxIcon.Question)
    .InputText("Link label", "Finance review β€” Q1")
    .CheckBox("Allow downloading", defaultChecked: true)
    .Buttons("Create link", "Cancel")
    .ShowExAsync();                     // or .ShowAsync() for just the DialogResult

if (r.Button == DialogResult.OK)
{
    // r.InputText, r.CheckBoxChecked
}

Methods: Task<DialogResult> ShowAsync(CancellationToken = default) Β· Task<GlassResult> ShowExAsync(CancellationToken = default).

18 Β· Live progress β€” ShowProgress() + GlassProgressController


Live progress controller

ShowProgress() opens a non-blocking progress dialog and hands back a thread-safe GlassProgressController. Drive the bar and caption from a worker, then Complete() it β€” or detect a user cancel via WasCanceledByUser. Pair it with ProgressActivity(...) (see Β§8 Β· Progress) and SetActivity(...) so the bar’s animation matches the work β€” and switches as it moves between phases.

var progress = GlassMessage.Create("Preparing backup…")
    .Title("OneDrive Backup")
    .Icon(MessageBoxIcon.Information)
    .Progress(0, 100)
    .ProgressActivity(GlassProgressActivity.Compress)   // packing files: marching blocks
    .Buttons("Cancel")
    .ShowProgress();

for (var i = 0; i <= 100 && !progress.WasCanceledByUser; i += 5)
{
    if (i == 50) progress.SetActivity(GlassProgressActivity.Upload);  // now sending: packets flow β†’
    progress.SetValue(i);
    progress.SetMessage($"Backing up…  {i}%");
    await Task.Delay(80);
}

if (!progress.WasCanceledByUser) progress.Complete();
await progress.Completion;            // completes when the dialog closes

Members: SetValue(int) Β· SetMessage(string) Β· SetActivity(GlassProgressActivity) Β· Complete() Β· Close(DialogResult) Β· Completion (Task<GlassResult>) Β· WasCanceledByUser Β· IsClosed. All update methods marshal onto the UI thread, so they’re safe to call from any thread.

19 Β· System sounds β€” Sound()


Icon system sound

Play the Windows system sound that matches the icon (Information / Warning / Error / Question) when the dialog opens, just like the classic MessageBox.

GlassMessage.Create("This dialog played the 'Critical Stop' sound as it opened.")
    .Title("Icon System Sound")
    .Icon(MessageBoxIcon.Error)
    .Sound()                           // per-dialog opt-in
    .Buttons(MessageBoxButtons.OK)
    .Show();

// …or enable it for every dialog at startup:
GlassMessage.PlaySystemSounds = true;

Per-dialog: Sound(bool enable = true) wins over the global. Global: GlassMessage.PlaySystemSounds (default false).


🧭 Demo Walkthrough

Glass.Demo is a single WinForms window β€” a 2-column grid of buttons, one per feature.

  • Entry point: Program.Main() calls ApplicationConfiguration.Initialize(), sets two global defaults, and runs DemoForm.
  • DemoForm builds its UI from one (string Label, Action Action)[] table β€” the single place to edit when adding a showcase.
  • Each Demo_* method is self-contained and demonstrates exactly one capability with realistic copy.
Full demo β†’ feature map (32 demos)


Demo button Method Feature
Drop-in Replace (Show) Demo_Basic GlassMessage.Show
Dark / Light / Mica / HC / Classic Themes Demo_Themes Theme(...) presets
Auto-detect OS Theme Demo_AutoTheme GlassTheme.AutoDetect()
Fluent Builder API Demo_Builder Create(...) + custom buttons
Countdown Auto-Close (10 s) Demo_Countdown AutoClose(...)
β€œDon’t Show Again” Checkbox Demo_CheckBox CheckBox(...) + ShowEx()
Inline Text Input Demo_Input InputText(...)
Password Input Demo_Password InputPassword(...)
Password β€” Caps Lock Hint Off Demo_PasswordNoCapsLock InputPassword(..., showCapsLockHint: false)
Drop-down Input Demo_Dropdown InputDropdown(...)
Expandable Detail Section Demo_Detail Detail(...)
Determinate Progress Bar Demo_Progress Progress(value, max)
Indeterminate Progress Bar Demo_ProgressMarquee ProgressIndeterminate()
Custom Bitmap Icon Demo_CustomIcon Icon(Bitmap)
Ctrl+C Copy to Clipboard Demo_Copy clipboard copy
SlideDown Animation Demo_Slide Animation(SlideDown)
Scale Animation Demo_Scale Animation(Scale)
RTL (Right-to-Left) Layout Demo_RTL RightToLeft()
Security Alert β€” Sign-in Demo_SecurityAlert 3-button mapping + ShowEx()
Windows Update β€” Release Notes Demo_ReleaseNotes Default(Button2)
End-User Licence Agreement Demo_Eula Detail(...) + CheckBox(...)
Storage Migration Wizard Demo_Migration dropdown + checkbox + progress
Toast β€” Bottom-Right Demo_Toast GlassToast.Show(options)
Toast β€” Stacking Demo_ToastStack auto-stacking toasts
Toast β€” Four Corners Demo_ToastCorners ToastPosition
Async ShowAsync Demo_Async await GlassMessage.ShowAsync
Async Builder (ShowExAsync) Demo_AsyncBuilder builder ShowExAsync()
Live Progress Controller Demo_ProgressController ShowProgress() + GlassProgressController (live SetActivity)
Progress Activity β€” Download Flow Demo_ProgressDownload ProgressActivity(Download)
Progress Activity β€” Gallery (all styles) Demo_ProgressActivityGallery every GlassProgressActivity family
Icon System Sound Demo_Sound Sound()
All Buttons + ShowEx Rich Result Demo_ShowEx full GlassResult

πŸ”§ API Overview

GlassMessage β€” static facade


// MessageBox-compatible (several overloads):
DialogResult Show(string message);
DialogResult Show(string message, string title);
DialogResult Show(string message, string title, MessageBoxIcon icon);
DialogResult Show(string message, string title, MessageBoxIcon icon, MessageBoxButtons buttons);
DialogResult Show(string message, string title, MessageBoxIcon icon, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton);
DialogResult Show(IWin32Window owner, string message, string title, MessageBoxIcon icon, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton);
DialogResult Show(IWin32Window owner, string message, string title, MessageBoxIcon icon, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton, GlassTheme theme);

// Async (non-blocking):
Task<DialogResult> ShowAsync(string message, string title = "", MessageBoxIcon icon = None, MessageBoxButtons buttons = OK, CancellationToken ct = default);
Task<DialogResult> ShowAsync(string message, string title, MessageBoxIcon icon, MessageBoxButtons buttons, GlassTheme theme, CancellationToken ct = default);

// Fluent builder:
GlassBuilder Create(string message);

// Global defaults:
static GlassTheme DefaultTheme      { get; set; }  // = GlassTheme.Default
static bool       UseRoundedCorners { get; set; }  // = false
static bool       PlaySystemSounds  { get; set; }  // = false
GlassBuilder β€” fluent builder


Method Purpose
Title(string) Title-bar caption
Icon(MessageBoxIcon) / Icon(Bitmap) System icon or custom 48Γ—48 bitmap
Buttons(MessageBoxButtons) / Buttons(params string[]) Standard set or custom labels
Default(MessageBoxDefaultButton) Focused / auto-close target button
Theme(GlassTheme) Per-dialog theme override
Owner(IWin32Window) Owner window (centres on / stays above it)
Animation(GlassAnimation) Open/close animation
RoundedCorners(bool = true) Per-dialog rounded-corner override
Sound(bool = true) Play the icon’s system sound when the dialog opens
AutoClose(int ms) Auto-confirm the default button after a delay
CheckBox(string label, bool defaultChecked = false) Add a checkbox
InputText(string placeholder = "", string defaultValue = "") Single-line input
InputPassword(string placeholder = "", bool showCapsLockHint = true) Masked input + reveal toggle; Caps Lock badge shown while active (pass false to suppress)
InputMultiline(string placeholder = "", string defaultValue = "") Multi-line input
InputDropdown(IEnumerable<string> items, string defaultItem = null) Drop-down list
Detail(string) Expandable β€œShow details” panel
Progress(int value, int max = 100) Determinate progress bar
ProgressIndeterminate() Marquee progress bar
ProgressActivity(GlassProgressActivity) Directional flow animation (Transfer/Upload/Download/Sync/…)
RightToLeft(bool = true) Mirror layout for RTL
Show() Show modally β†’ DialogResult
ShowEx() Show modally β†’ GlassResult
ShowAsync(CancellationToken = default) Show non-blocking β†’ Task<DialogResult>
ShowExAsync(CancellationToken = default) Show non-blocking β†’ Task<GlassResult>
ShowProgress() Show non-blocking β†’ GlassProgressController (live updates)
GlassResult, GlassToast & enums


// GlassResult (from ShowEx / ShowExAsync) β€” implicitly converts to DialogResult
DialogResult Button          { get; }   // the button pressed
bool         CheckBoxChecked { get; }   // checkbox state
string       InputText       { get; }   // typed/selected value (never null)

// GlassProgressController (from builder.ShowProgress()) β€” thread-safe, non-blocking
void               SetValue(int value);          // update the determinate bar
void               SetMessage(string message);   // update the caption
void               SetActivity(GlassProgressActivity); // change the flow animation live
void               Complete();                   // close with DialogResult.OK
void               Close(DialogResult = OK);      // close with an explicit result
Task<GlassResult>  Completion        { get; }    // completes when the dialog closes
bool               WasCanceledByUser { get; }    // user dismissed vs Complete()/Close()
bool               IsClosed          { get; }

// GlassToast β€” static facade
GlassToast.Show(string message, int durationMs = 4_000);
GlassToast.Show(string message, string title, int durationMs = 4_000);
GlassToast.Show(string message, string title, MessageBoxIcon icon, int durationMs = 4_000);
GlassToast.Show(GlassToastOptions options);
Task        GlassToast.ShowAsync(GlassToastOptions options);

// GlassToastOptions
class GlassToastOptions
{
    string         Message           { get; set; }
    string         Title             { get; set; }
    MessageBoxIcon Icon              { get; set; } = None;
    GlassTheme     Theme             { get; set; }
    int            DurationMs        { get; set; } = 4_000;
    ToastPosition  Position          { get; set; } = BottomRight;
    Action         OnClick           { get; set; }
    bool?          UseRoundedCorners { get; set; }
    Screen         Screen            { get; set; }   // null = auto-target the active screen
}

// Enums
enum GlassAnimation { Fade, SlideDown, Scale, None }
enum GlassInputMode { None, Text, Password, Multiline, Dropdown }
enum ToastPosition  { BottomRight, BottomLeft, TopRight, TopLeft, BottomCenter, TopCenter }

GlassInputMode is selected for you by the Input* builder methods β€” you rarely set it directly.


βš™οΈ Configuration

Global defaults (set once at startup):

Setting Type Default Effect
GlassMessage.DefaultTheme GlassTheme GlassTheme.Default (dark) Theme when none is specified
GlassMessage.UseRoundedCorners bool false Global rounded-corners default
GlassMessage.PlaySystemSounds bool false Play the icon’s system sound on open
GlassMessage.UseRoundedCorners = true;
GlassMessage.DefaultTheme      = GlassTheme.AutoDetect();
GlassMessage.PlaySystemSounds  = true;   // optional β€” off by default

Everything else is per-dialog via the builder. The only required value is the message.

Toast options (GlassToastOptions): Message, Title, Icon (None), Theme (falls back to DefaultTheme), DurationMs (4000), Position (BottomRight), OnClick, UseRoundedCorners (null β†’ global), Screen (null β†’ auto-target the active screen).


🎨 Styling and Customization

Beyond the five presets, build a fully custom GlassTheme β€” every colour, font, corner radius, and the opacity is settable.

var brand = new GlassTheme
{
    BackgroundTop    = Color.FromArgb(28, 16, 42),
    BackgroundBottom = Color.FromArgb(14, 8, 24),
    TitleBarTop      = Color.FromArgb(48, 24, 78),
    TitleBarBottom   = Color.FromArgb(28, 14, 48),
    BorderColor      = Color.FromArgb(180, 120, 255),
    AccentColor      = Color.FromArgb(150, 90, 240),   // focus, progress, links
    TitleColor       = Color.FromArgb(235, 225, 255),
    MessageColor     = Color.FromArgb(220, 210, 240),
    ButtonForeColor  = Color.FromArgb(240, 235, 255),
    ButtonFillTop    = Color.FromArgb(60, 36, 96),
    ButtonFillBottom = Color.FromArgb(36, 20, 60),
    CheckBoxColor    = Color.FromArgb(180, 120, 255),
    InputBackColor   = Color.FromArgb(20, 12, 34),
    InputForeColor   = Color.FromArgb(220, 210, 240),
    CornerRadius       = 10,    // window corner radius (0 = square)
    ButtonCornerRadius = 6,
    Opacity            = 0.97,
};

GlassMessage.DefaultTheme = brand;   // apply everywhere
Group Properties
Window body BackgroundTop, BackgroundBottom
Title bar TitleBarTop, TitleBarBottom, TitleColor
Accent / edge BorderColor, AccentColor
Text MessageColor
Buttons ButtonForeColor, ButtonFillTop, ButtonFillBottom
Inputs / checkbox InputBackColor, InputForeColor, CheckBoxColor
Shape CornerRadius, ButtonCornerRadius
Fonts TitleFont, MessageFont, ButtonFont
Window Opacity

Built-in presets: GlassTheme.Default Β· GlassTheme.Dark (alias for Default) Β· GlassTheme.Light Β· GlassTheme.Mica Β· GlassTheme.HighContrast Β· GlassTheme.WindowsClassic Β· GlassTheme.AutoDetect().

Tip: Built-in presets are shared singletons (exempt from disposal). Custom themes implement IDisposable and free their fonts on Dispose().

Modern chrome: with rounded corners enabled, the dialog requests crisp Windows 11 DWM corners and a Mica backdrop (falling back to Acrylic blur on Windows 10), degrading to a software-rounded region on older systems. The OS version is detected with RtlGetVersion, so the modern chrome still activates even when the host app ships without a Windows 10/11 compatibility manifest (in which case Environment.OSVersion would otherwise mis-report Windows 8 and quietly disable it).


βœ… Error Handling / Best Practices

  • Use ShowEx() when you need data back β€” Show() returns only the button.
  • Validate input β€” GlassResult.InputText is never null, but still check content.
  • Know your button mapping β€” custom labels map by count (1β†’OK, 2β†’OK/Cancel, 3+β†’Yes/No/Cancel).
  • Prefer ShowAsync in async handlers so the UI thread keeps pumping; pass a CancellationToken to dismiss.
  • Set global defaults once at startup rather than per call.
  • Dispose custom bitmap icons with using.
  • Use GlassAnimation.None in tests for deterministic dialogs.
  • ⚠️ Windows-only (WinForms) Β· treat passwords as secrets (don’t log InputText).

πŸ—‚οΈ Project Structure

Glass/
β”œβ”€β”€ Glass.sln                     # Solution: library + demo + tests
β”œβ”€β”€ README.md  Β·  LICENSE         # This file Β· MIT
β”œβ”€β”€ Glass.Message/                # ── The library ──
β”‚   β”œβ”€β”€ GlassMessage.cs           # Static facade (Show / ShowAsync / Create)
β”‚   β”œβ”€β”€ GlassBuilder.cs           # Fluent builder
β”‚   β”œβ”€β”€ GlassDialog.cs            # WinForms dialog (rendering, layout, animations)
β”‚   β”œβ”€β”€ GlassDialogConfig.cs      # Internal settings bag
β”‚   β”œβ”€β”€ GlassResult.cs            # Rich result (button + checkbox + input)
β”‚   β”œβ”€β”€ GlassProgressController.cs# Live handle for non-blocking progress dialogs
β”‚   β”œβ”€β”€ GlassTheme.cs             # Palette + presets + AutoDetect
β”‚   β”œβ”€β”€ GlassToast.cs             # Toast facade, options, and toast window
β”‚   β”œβ”€β”€ GlassButton.cs            # Themed button control
β”‚   β”œβ”€β”€ OsVersion.cs              # True Windows version (RtlGetVersion) for DWM chrome
β”‚   β”œβ”€β”€ GlassAnimation.cs         # enum: Fade / SlideDown / Scale / None
β”‚   β”œβ”€β”€ GlassInputMode.cs         # enum: None / Text / Password / Multiline / Dropdown
β”‚   └── GlassProgressActivity.cs  # enum: progress flow activities (Upload / Download / Sync / …)
β”œβ”€β”€ Glass.Demo/                   # ── WinForms feature gallery ──
β”‚   └── Program.cs                # DemoForm + one Demo_* method per feature
β”œβ”€β”€ Glass.Message.Tests/          # ── Unit tests ──
β”œβ”€β”€ Images/                       # Screenshots used by this README
└── tools/                        # Build/signing helper scripts

πŸ’» Requirements

Β  Β 
OS Windows (Windows-only by design)
UI stack Windows Forms
Library targets net481, net8.0-windows, net9.0-windows, net10.0-windows
Demo target net8.0-windows
High-DPI PerMonitorV2
Dependencies None beyond the .NET / WinForms framework assemblies

▢️ Running the Demo

Visual Studio: open Glass.sln β†’ set Glass.Demo as startup β†’ press F5.

Command line:

dotnet run --project Glass.Demo     # launch the gallery
dotnet build Glass.sln -c Release   # build everything

The projects include an optional post-build Authenticode signing step (tools\Sign-Output.ps1) that runs with ContinueOnError β€” if you haven’t set up a dev certificate, the build still succeeds and signing is simply skipped.


❓ FAQ

Can I really just replace MessageBox with GlassMessage?


Yes β€” GlassMessage.Show(...) is signature-compatible with MessageBox.Show(...), so most call sites compile unchanged and still return a DialogResult.

How do I get the checkbox state or typed input back?


Call ShowEx() instead of Show() and read GlassResult.CheckBoxChecked / GlassResult.InputText.

My three-button dialog returns odd DialogResult values. Why?


Custom labels map to the nearest standard layout by count: 1β†’OK, 2β†’OK/Cancel, 3+β†’Yes/No/Cancel. With three labels the buttons return Yes / No / Cancel in order.

How do I make dialogs follow the user's Windows theme?


GlassMessage.DefaultTheme = GlassTheme.AutoDetect(); at startup.

Are toasts modal? Does it block the UI thread?


Toasts are non-modal, top-most, auto-dismissing windows. Show()/ShowEx() are modal (blocking); use ShowAsync(...) for a non-blocking, awaitable dialog.

How do I close an async dialog from code?


Pass a CancellationToken to ShowAsync; cancelling it closes the dialog and yields DialogResult.Cancel.

Can I hide the Caps Lock hint on password fields?


Yes β€” pass showCapsLockHint: false to InputPassword():
.InputPassword("placeholder", showCapsLockHint: false)

Which .NET versions are supported?


.NET Framework 4.8.1 and .NET 8 / 9 / 10 (Windows).


🀝 Contributing

  1. Fork and create a feature branch.
  2. Make your change in Glass.Message; for user-visible features, add a Demo_* showcase by appending an entry to the demos table in Glass.Demo/Program.cs.
  3. Add or update tests in Glass.Message.Tests.
  4. Build the solution and run the demo to smoke-test visually.
  5. Open a PR describing the change, with a screenshot for any UI work.

The demo’s demos array is the single list to edit when adding or removing a feature showcase β€” keep it in sync with the library.


🚒 Release pipeline

Releases are produced by .github/workflows/release.yml, triggered by pushing a version tag (git push origin v1.2.3), publishing a GitHub Release, or a manual run. The pipeline is split into focused jobs so independent work runs in parallel and a failure points straight at the stage that broke:

metadata ─► test ─┬─► package ─┐
                  └─► demo ────┴─► release ─► publish-nuget
Job Runner What it does
metadata Linux Resolves the version and extracts the matching CHANGELOG.md section once, sharing them via a job output + an artifact.
test Windows Builds the library (all four TFMs) and runs the unit tests β€” the quality gate for everything downstream.
package Windows Packs the .nupkg / .snupkg (CHANGELOG notes embedded) and zips the per-framework DLLs.
demo Windows Publishes the self-contained demo app for win-x64 and win-x86. Runs in parallel with package.
release Linux Gathers every artifact and publishes the GitHub Release. Skipped on manual runs.
publish-nuget Linux Pushes the package to NuGet.org (only when the NUGET_API_KEY secret is present).

Windows-only jobs build the library because it targets .NET Framework 4.8.1 and Windows Forms; the lightweight metadata/release/publish jobs run on Linux.


πŸ“„ License

Licensed under the MIT License β€” see LICENSE. Copyright (c) 2026 Gehan Fernando


🧩 Full Example

A single, practical flow combining a custom theme, the builder, an input, a checkbox, the rich result, async, and a confirming toast.

using Glass;
using System.Threading.Tasks;
using System.Windows.Forms;

internal static class Program
{
    [STAThread]
    private static void Main()
    {
        ApplicationConfiguration.Initialize();

        // 1) Global defaults β€” modern corners + follow the OS theme.
        GlassMessage.UseRoundedCorners = true;
        GlassMessage.DefaultTheme      = GlassTheme.AutoDetect();

        Application.Run(new MainForm());
    }
}

internal sealed class MainForm : Form
{
    public MainForm()
    {
        Text = "Glass.Message β€” Full Example";
        var btn = new Button { Text = "Export report…", Dock = DockStyle.Fill };
        btn.Click += async (_, _) => await ExportAsync();
        Controls.Add(btn);
    }

    private async Task ExportAsync()
    {
        // 2) Rich dialog: drop-down + checkbox, read back via ShowEx().
        var choice = GlassMessage.Create(
                "Choose the output format for 'Annual_Report_Q4_2025'.")
            .Title("Export Document")
            .Icon(MessageBoxIcon.Question)
            .InputDropdown(
                ["PDF β€” Portable Document Format",
                 "Word (.docx) β€” Microsoft Word",
                 "Excel (.xlsx) β€” Microsoft Excel"],
                "PDF β€” Portable Document Format")
            .CheckBox("Open the file when export finishes")
            .Buttons("Export", "Cancel")
            .Animation(GlassAnimation.Scale)
            .ShowEx();

        if (choice.Button != DialogResult.OK)
        {
            GlassToast.Show("Export cancelled.", "Export", MessageBoxIcon.Warning);
            return;
        }

        // 3) Non-blocking progress confirmation.
        var confirm = await GlassMessage.ShowAsync(
            $"Exporting as:\n{choice.InputText}\n\nThis runs in the background.",
            "Export Started", MessageBoxIcon.Information, MessageBoxButtons.OKCancel);

        // 4) Notify without interrupting β€” a toast.
        if (confirm == DialogResult.OK)
        {
            GlassToast.Show(new GlassToastOptions
            {
                Title    = "Export Complete",
                Message  = $"{choice.InputText}\nSaved to Downloads"
                           + (choice.CheckBoxChecked ? " Β· opening now…" : ""),
                Icon     = MessageBoxIcon.Information,
                Position = ToastPosition.BottomRight,
                OnClick  = () => { /* open the file / folder */ },
            });
        }
    }
}

Glass.Message β€” built with care by Gehan Fernando Β· MIT Licensed

A modern message box, the way Windows dialogs should look.


Read all Glass.Message guides online Β· Project overview Β· All developer projects