dotnet / dotnet/winforms

[API Proposal] Introduce ISymbol and ToolStrip Symbol support

Open
#14,921 1 comment 0 reactions 0 assignees View on GitHub
api-suggestion NewApi-Net12 untriaged
Dominant language
C#
Stars
4.9k
Forks
1.1k
Avg merge
1d 13m
Merged PRs (30d)
85

Description

# Background and motivation

WinForms ToolStrip APIs are image-centric, while modern applications increasingly need semantic, reusable symbols backed by vectors, font glyphs, bitmaps, or future sources. Applications currently rasterize those assets independently, losing reusable accessibility metadata and duplicating DPI/theme-aware caching.

A backend-neutral symbol contract would let ToolStrip consume semantic assets without binding WinForms to SVG, GDI+, Direct2D, or a particular renderer. WARP has been used as an experimental implementation proving the layering, but this proposal stands alone and does not require a WARP dependency.

# API Proposal

```csharp
namespace System.Windows.Forms;

public interface ISymbol
{
string? AccessibilityName { get; set; }

string? AccessibilityDescription { get; set; }

Size GetPreferredSize(Size proposedSize);
}

public partial class ToolStripItem
{
public ISymbol? Symbol { get; set; }
}
```

`ISymbol` describes the asset, not its rendering backend. Renderer discovery and caching remain separate so application-defined symbol types and future rendering backends can be added without changing the interface.

Proposed sizing semantics:

- `Size.Empty` requests the symbol's natural/default size.
- One zero dimension leaves that axis unconstrained.
- Two positive dimensions constrain the result; aspect-preserving symbols fit within them.

Proposed coexistence rule: a non-null, renderable `Symbol` wins over `Image`, but does not destroy the assigned `Image`; clearing `Symbol` restores normal image behavior.

Proposed accessibility fallback:

```text
ToolStripItem.AccessibleName
↓ if unset
ISymbol.AccessibilityName
↓ if unset
existing WinForms fallback
```

and equivalently for `AccessibleDescription`. The hosting item always remains authoritative.

Current image-centric source paths:

- [`ToolStripItem.PreferredImageSize`](https://github.com/dotnet/winforms/blob/f724c2277a0695ca71d1816575636afc802d73b9/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripItem.cs#L1552-L1575)
- [`ToolStripMenuItem` image layout](https://github.com/dotnet/winforms/blob/f724c2277a0695ca71d1816575636afc802d73b9/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripMenuItem.ToolStripMenuItemInternalLayout.cs#L117-L127)
- [`ToolStrip.ImageScalingSize` propagation](https://github.com/dotnet/winforms/blob/f724c2277a0695ca71d1816575636afc802d73b9/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs#L920-L935)

Open API questions:

1. Should `Symbol` live on `ToolStripItem` or a narrower set of derived types?
2. Should accessibility metadata remain mutable directly on `ISymbol`?
3. How should built-in rendering discover renderers for application-defined symbol implementations?
4. Should concrete symbol types be provided by WinForms, or only the abstraction and ToolStrip integration?
5. Are the proposed zero/unconstrained size semantics consistent with existing WinForms preferred-size conventions?

# API Usage

```csharp
ISymbol addSymbol = GetAddSymbol();

var addButton = new ToolStripButton
{
DisplayStyle = ToolStripItemDisplayStyle.ImageAndText,
Text = "Add",
Symbol = addSymbol,
AccessibleName = "Add customer",
};

var menuItem = new ToolStripMenuItem
{
Text = "Add customer",
Symbol = addSymbol,
};
```

The same semantic asset can be reused in multiple contexts while each host can provide a more specific accessible name.

# Alternative Designs

- Keep `Image` as the only surface and require pre-rasterization. This loses semantic metadata and pushes DPI/theme/cache policy to every caller.
- Add `Draw(Graphics, ...)` to `ISymbol`. This permanently binds the abstraction to GDI+.
- Add SVG-specific APIs directly to ToolStrip. This makes one source format part of the host abstraction and inhibits font/procedural/future symbol sources.
- Make `Image` and `Symbol` destructively clear each other. This is simple but loses the caller's image when temporarily switching to a symbol.

# Risks

- Renderer discovery must be defined so a valid `ISymbol` never becomes a silent blank asset.
- Cache identity must include size, DPI, foreground/current color, theme, high contrast, and source content changes.
- Designer serialization, reset behavior, resource ownership, and preview rendering need an explicit design.
- Accessibility fallback must not promote stale symbol metadata into an item-level override when symbols change.
- The `Image`/`Symbol` precedence rule must preserve existing item layout and serialization predictably.

# Will this feature affect UI controls?

Yes. ToolStrip-based controls and image-bearing items would understand `Symbol` alongside `Image`, including `ToolStrip`, `MenuStrip`, `StatusStrip`, `ContextMenuStrip`, and dropdowns.

The implementation/test matrix should cover:

- symbol-only and mixed `Image`/`Symbol` items;
- symbol swap/clear and designer round-tripping;
- accessibility fallback and item-level overrides;
- 100%, 150%, 200%, and per-monitor DPI changes;
- light mode, dark mode, and high contrast;
- ToolStripButton, ToolStripMenuItem, dropdown items, status items, and custom derivatives;
- cache invalidation, ownership, disposal, and application-defined symbols.

Companion layout proposal: dotnet/winforms#14922.

Contributor guide

Open the contributing guide

Research direction

Start with ToolStripItem.cs, ToolStripMenuItem.ToolStripMenuItemInternalLayout.cs, and ToolStrip.cs at the linked image-sizing and propagation paths. Review companion issue #14922, then resolve the open API, renderer, accessibility, serialization, and sizing questions before defining the implementation and test matrix. Done means an agreed design that covers symbol/image coexistence, accessibility, DPI and theme changes, supported item types, and application-defined symbols.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
desktop
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.