Winforms: Make appearance of MenuStrip & ContextMenuStrip look native when RenderMode = System

Created on 21 Dec 2019  路  24Comments  路  Source: dotnet/winforms

(split off from #2476)

Windows 7

See #2476 for screenshots if needed.

Windows 10

| Native | Strip (RenderMode = System) |
| --- | --- |
| NativeMenu10 | StripSystemMenu10 |
| NativeContext10 | StripSystemContext10 |
| a | b |
| bb | aa |

List of Differences

It's probably not worth matching the look of Windows 7, so I will only consider differences from the Windows 10 look.

I have done measurements on 100% and 150% DPI, and marked those that change. I have not measured widths/heights of items, those seem mostly fine.

  • Main Menu

    • [ ] Menu height should be (19px * DPI)

    • [ ] Make menu items flush with the left and top side of the window (no padding/border between them)

    • [ ] The menu background from top to bottom is (19px * DPI - 1px) of RGB(255, 255, 255), and then 1px bottom border of RGB(242, 242, 242)

    • Note that item highlight overlays the bottom border and the top of sub-menus is just below the bottom border

    • [ ] Set highlighted item background to RGB(204, 232, 255)

    • [ ] Set highlighted item border to 1px of RGB(153, 209, 255)

    • [ ] Do not invert text color of highlighted items

    • [ ] Reduce left/right padding of menu items (appears to be 1px for border + (6px * DPI) of padding)

  • Context Menu & Main Menu sub-menus

    • [ ] Set border color to RGB(204, 204, 204)

    • [ ] Set overall background color to RGB(242, 242, 242), so that non-highlighted items and padding between items and border is this color

    • [ ] Set checkmark/image column background color to RGB(240, 240, 240)

    • [ ] Set highlighted item background color to RGB(144, 200, 246)

    • [ ] Fix the weird white line on the left of highlighted items

    • [ ] Do not invert text color of highlighted items

    • [ ] Increase padding between the items and border (1px -> 2px)

    • [ ] Update icon of arrow on items with sub-menus and move it up by 1 px to center it

    • [ ] Make splitter 1px tall

    • [ ] Set splitter color to RGB(215, 215, 215)

    • [ ] Fix splitter padding (it should not leak into the checkmark/image column, and be 2px from the right border)

    • [ ] Set checkmark color to RGB(51, 51, 51)

    • [ ] Checkmark should have a background color RGB(144, 200, 246) when checked, or RGB(86, 176, 250) when checked and highlighted at the same time

    • [ ] Sub-menus should overlap their parent menu item by 6 pixels on the left, and be flush with the top

    • [ ] After highlighting an item with a sub-menu and then moving cursor to a sibling item above/below, the original highlighted item should immediately become unhighlighted

    • Currently the highlight lingers until the sub-menu disappears (see last screenshot)

It's a lot ^.^ but it should make them almost completely visually indistinguishable from native controls.

(Updated) Test Project

winforms.zip

bug theming tenet-OS-compat tenet-compatibility

Most helpful comment

@vatsan-madhavan that's pretty much on the money as far as I know. Recent high contrast fixes have generally been done with an effort to only use SystemColors wherever possible. I actually do not remember any fix that we made in the last few years that used anything else, aside from when accessibility standards demanded a specific color that was not provided by the SystemColors.

All 24 comments

Thank you for the detailed report

FYI: setting supported OS in the manifest file does not fix this issue, most likely we are not getting there glyphs or colors from the theme data.

While debugging another issues I was stepping through TooStrip implementations and observed that that it defaults to ProfessionalRendererType:
https://github.com/dotnet/winforms/blob/62c17fe35b2f00f8156828148a5fbbb910cc52a1/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripManager.cs#L46-L51
https://github.com/dotnet/winforms/blob/62c17fe35b2f00f8156828148a5fbbb910cc52a1/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripManager.cs#L510-L516
On a separate note, it looks to me that InitializeThread should be relying on DefaultRendererType instead of using ProfessionalRendererType directly.

And I think at this point it is natural for one to wonder why we are not using SystemRendererType as the default.
@merriemcgaw @Tanya-Solyanik any background on this?

Can/should we default to the "System" instead?
Obviously such change has wider repercussions - all existing consumers migrating to .NET will be affected. Though one can argue the look and feel of the menus and toolbars will be more inline with the OS (once we address the theming issues).

  • The menu background from top to bottom is (19px * DPI - 1px) of RGB(255, 255, 255), and then 1px bottom border of RGB(242, 242, 242)

I've looked at this, we do all the painting with standard predefined colours, e.g.:
https://github.com/dotnet/winforms/blob/b666dc7a94d8ac87a7d300cfb4fa86332fb79bae/src/System.Windows.Forms/src/System/Windows/Forms/ToolStripSystemRenderer.cs#L233-L237

 SystemColors.MenuBar => {Name=Menu, ARGB=(255, 240, 240, 240)}

This is how the colours are read by the runtime:
https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/Common/src/System/Drawing/KnownColorTable.cs#L175-L182
https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/Common/src/System/Drawing/KnownColorTable.cs#L228-L229
...using these constants:
https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/Common/src/Interop/Windows/User32/Interop.Win32SystemColors.cs#L9-L45
which are the same as defined here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor

Executed locally I get the same result:
image

I'm not quite sure how native apps get the different colours (i.e. RGB(242, 242, 242))
Obviously Notepad has it differently:
image

@JeremyKuhne @Tanya-Solyanik @weltkante any thoughts on this?

I'm not entirely sure if SystemColors (and SystemRenderer) will properly reflect theming. Some colors may be reused by theming, but the general model I have in mind is that theming uses a lot of hardcoded colors and rendering code which you only can get by going through the theming API. From a quick look you have no renderer using the theming API for menus/toolbars/statusbars (but I may have missed something).

If I remember right the general idea is that

  • you have the "theming off" style which is entirely based on SystemColors (you still get that sometimes over remote connections or server farms)
  • you have the "theming on" style which is whatever the OS does natively, but I don't know if you can reproduce that in WinForms without using the deprecated HMENU based classes. You might have to invest some work to reproduce the "theming on" style.
  • you have the "ProfessionalRenderer" which I think is based on Office (back when it still had menus). The point was that at that time Office was the reference application when you wanted modern UI and it was moving at a faster pace than the OS, so WinForms probably wanted to allow taking part of that modern look and feel. (On the native side MFC had similar classes to replicate the more modern look and feel of Office, separately of the OS controls.)

Its been a long time since Office had menus and the OS has caught up and now has its own theming and UI styling rules separate from Office. I don't know if there is much value maintaining the historic Office rendering styles beyond providing it for backward compatibility to existing WinForms users.

In my opinion the main goal should be to achieve parity with the OS, which obviously has been neglected in the menu/toolbar area because for a long time Office was seen as having the superior style. Using Office as reference for UI theming has no point anymore if you don't provide the kind of controls used in Office.

So someone will probably have to look into how the theming API is to be used for rendering menus/toolbars/statusbars. It may take more than just picking colors, you may have to call the theming rendering APIs (which I think for menus is done nowhere in WinForms currently)

PS: take everything I said here with a lot of caution, its driven by 10-15 year old memory and I didn't take a close look what WinForms does today in its menu rendering.

PS: take everything I said here with a lot of caution, its driven by 10-15 year old memory and I didn't take a close look what WinForms does today in its menu rendering.

I think we're in real troubles now... 馃槃

Some colors may be reused by theming, but the general model I have in mind is that theming uses a lot of hardcoded colors and rendering code which you only can get by going through the theming API. From a quick look you have no renderer using the theming API for menus/toolbars/statusbars (but I may have missed something).

Windows Forms have only rudimentary theming support, and that is something we're planning to look at going forward.
My OS is configured to use "dark" mode, and Notepad and Git Extensions are the only retina burning apps, so following the logic I guess I'd expect menus to be rendered in dark colours as well
image
(Outlook uses own theming, so for the most part irrelevant, though it uses the same colour palette as the OS, and in Git Extensions we use custom colours)

In my opinion the main goal should be to achieve parity with the OS, which obviously has been neglected in the menu/toolbar area because for a long time Office was seen as having the superior style. Using Office as reference for UI theming has no point anymore if you don't provide the kind of controls used in Office.

Agree 馃挴

We need to step back and agree on what we consider to be a "native Windows look and feel".
I don't believe it is correct just to look at how .NET Framework MainMenu et al. used to look. It had been designed in a completely different world with completely different useability and accessibility requirements than what we have today.

Let's look at "native" Windows 10 1903 apps.

| | Windows default "light" mode | Windows "dark" mode |
| --- | ---- | --- |
| Windows Explorer

It is used to possible to show menus,
however now they seem to be completely removed
even though the setting is still there 馃槖 | image | image |
| Internet Explorer 11 | image | image |
| Notepad | image | image |
| MMC | image | image |
| Control Panel | image | image |
| Visual Studio 2019

whilst technically VS is an owner-draw
included it for completeness | image | image |

There is some consistency in the light theme, however hardly any handle the dark theme correctly.

鉂楋笍 And that without the rabbit hole of the High Contrast (HC) themes, where we can truly immerse ourselves in the world of accessibility nightmares.
Whilst the above apps do some form of HC theming, not all of them correctly implement all accessibility requirements.
The Windows team has a backlog is accessibility bugs, and we need to be careful not to copy their bugs into the Windows Forms stack.

We can't arbitrary change colours and are bound to use the colours provided to us by the Runtime (i.e. SystemColours, etc.) because those colours meet accessibility requirements, and behave in the HC themes.
We also need to investigate and bridge the theming gap, and see how that affects the available colour palette.

I'd argue that a Windows Forms application must use the same colours and theming parameters as Windows Explorer, after all it _is_ the Windows shell.
An app must also change colours similar to how Edge and Chrome change their colours in response to the underlying Windows color theme change (their colours are not even anywhere near the Explorer's colours but they look on par with the underlying OS):

| | Windows default "light" mode | Windows "dark" mode |
| --- | ---- | --- |
| Windows Explorer | image | image |
| Edge | image | image |
| Chrome | image | image |

With all being said, we are planning to modify the "System" renderer so that *-strip controls have more native look, once we agree on what that means.

@merriemcgaw @OliaG we likely need to get with in touch with someone from Windows UX team and discuss the approved colours.

@vatsan-madhavan how does WPF decide on the default colours for menus and alike?

Not sure if you understood me correctly, so TLDR for my previous post: there are two APIs, SystemColors and theming API, using SystemColors may give you the colors from the non-themed UI in cases where the OS theming overrides colors, which is probably what is happening here.

What I'm suggesting is using the official theming API instead of starting to replicate code/colors. There are parts for menus/toolbars/statusbars (like MENU_BARBACKGROUND or SP_PANE) which I'd expect you to be able to use. Using the theming API is what most other "common controls" classes do in the WinForms codebase so why implement menus etc. differently from the approach you are taking with other controls?

That said, if you decide to roll your own implementation instead of calling the OS theming API thats of course a valid solution.

I'm not an expert in this, /cc @rladuca to comment in case I make substantive mistakes here.

--

Here is where the Style's for Menu are defined for various pre-defined themes:

For Aero theme (which was used in Windows Vista - WPF switched to Aero2 for Win8+), this is what we've got. You can see that it relies on SystemColors and SystemFonts.

    <Style x:Key="{x:Type Menu}"
           TargetType="{x:Type Menu}">
        <Setter Property="Background"
                Value="{StaticResource MenuBackground}"/>
        <Setter Property="FontFamily"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontFamilyKey}}"/>
        <Setter Property="FontSize"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontSizeKey}}"/>
        <Setter Property="FontStyle"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontStyleKey}}"/>
        <Setter Property="FontWeight"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontWeightKey}}"/>
        <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Menu}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Padding="{TemplateBinding Padding}"
                            SnapsToDevicePixels="true">
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Classic is defined this way, which is used in High Contrast modes. This also relies on SystemColors and SystemFonts.

    <Style x:Key="{x:Type Menu}"
           TargetType="{x:Type Menu}">
        <Setter Property="Background"
                Value="{DynamicResource {x:Static SystemColors.MenuBrushKey}}"/>
        <Setter Property="FontFamily"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontFamilyKey}}"/>
        <Setter Property="FontSize"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontSizeKey}}"/>
        <Setter Property="FontStyle"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontStyleKey}}"/>
        <Setter Property="FontWeight"
                Value="{DynamicResource {x:Static SystemFonts.MenuFontWeightKey}}"/>
        <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}"/>
        <Setter Property="VerticalContentAlignment"
                Value="Center"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Menu}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Padding="{TemplateBinding Padding}"
                            SnapsToDevicePixels="true">
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

And Aero2 is defined like this, which is the default in WPF.

Now there is reliance on predefined colors (i.e., special theme-specific constants) in addition to SystemColors and SystemFonts. These constants were likely selected to match the look-and-feel of Windows 8 better than what would otherwise have been acheivable by relying solely on SystemColors.

<!-- [[Aero2.NormalColor]] -->


    <SolidColorBrush x:Key="Menu.Static.Background" Color="#FFF0F0F0" />
    <SolidColorBrush x:Key="Menu.Static.Border" Color="#FF999999" />
    <SolidColorBrush x:Key="Menu.Static.Foreground" Color="#FF212121" />


    <SolidColorBrush x:Key="Menu.Static.Separator" Color="#FFD7D7D7" />


    <SolidColorBrush x:Key="Menu.Disabled.Background" Color="#3DDADADA" />
    <SolidColorBrush x:Key="Menu.Disabled.Border" Color="#FFDADADA" />
    <SolidColorBrush x:Key="Menu.Disabled.Foreground" Color="#FF707070" />


    <SolidColorBrush x:Key="MenuItem.Selected.Background" Color="#3D26A0DA" />
    <SolidColorBrush x:Key="MenuItem.Selected.Border" Color="#FF26A0DA" />


    <SolidColorBrush x:Key="MenuItem.Highlight.Background" Color="#3D26A0DA" />
    <SolidColorBrush x:Key="MenuItem.Highlight.Border" Color="#FF26A0DA" />


    <SolidColorBrush x:Key="MenuItem.Highlight.Disabled.Background" Color="#0A000000" />
    <SolidColorBrush x:Key="MenuItem.Highlight.Disabled.Border" Color="#21000000" />




<!--=================================================================
        Menu
    ==================================================================-->


    <Style TargetType="{x:Type Menu}">
        <Setter Property="Background" Value="{StaticResource Menu.Static.Background}" />
        <Setter Property="FontFamily" Value="{DynamicResource {x:Static SystemFonts.MenuFontFamilyKey}}"/>
        <Setter Property="FontSize" Value="{DynamicResource {x:Static SystemFonts.MenuFontSizeKey}}"/>
        <Setter Property="FontStyle" Value="{DynamicResource {x:Static SystemFonts.MenuFontStyleKey}}"/>
        <Setter Property="FontWeight" Value="{DynamicResource {x:Static SystemFonts.MenuFontWeightKey}}"/>
        <Setter Property="Foreground" Value="{StaticResource Menu.Static.Foreground}" />
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Menu}">
                    <Border
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Padding="{TemplateBinding Padding}"
                        SnapsToDevicePixels="true">
                        <ItemsPresenter
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

note: The links above are to sources that act as inputs to a generator which eventually generates the actual theme xaml files like Aero2.NormalColor.xaml.

Thank you @vatsan-madhavan, so is it fair to say WPF has colours hard-coded and relies on colours defined by the Runtime instead of loading the OS theme defined colours?

No, that's not a good generalization. It's more nuanced than that 馃榿

Normal operation (i.e., the OS is running a non-High Contrast theme): WPF uses a mix of self-defined and OS defined colors.

<<edited based on https://github.com/dotnet/winforms/issues/2543#issuecomment-582730461>>
OS is running a High contrast theme: WPF uses OS defined colors by and large; there have been several accessibility related bug-fixes that have ~likely~ introduced <<edit>> _rare deviations from this norm in the form of_ WPF defined colors ~as well~.

馃憤 thank you for the clarification.

@vatsan-madhavan that's pretty much on the money as far as I know. Recent high contrast fixes have generally been done with an effort to only use SystemColors wherever possible. I actually do not remember any fix that we made in the last few years that used anything else, aside from when accessibility standards demanded a specific color that was not provided by the SystemColors.

In summary: For theme off and high Contrast WPF uses System Colors as does WinForms, themed colors are hardcoded like <SolidColorBrush x:Key="Menu.Static.Background" Color="#FFF0F0F0" /> Note that WPF has different styles for different OS releases - if themes differ the hardcoded colors can differ between OS versions, thats something WinForms would need to do as well if it choses to replicate colors instead of calling theming APIs.

I don't want to interfere too much, but here's my perspective as a developer using WinForms, and someone who cares about consistency:

We need to step back and agree on what we consider to be a "native Windows look and feel".
Let's look at "native" Windows 10 1903 apps.

Notepad and MMC seem to just use the native control, which looks the most consistent today. If you're leaning towards Explorer to also support dark theme, it'd be good to know how those colors were chosen, whether the design is stable now, and how consistent it is with other modern Microsoft apps.

The Control Panel's (0, 120, 255) with black text is unreadable and to me it's horrifying that it's actually in the OS; at the same time, it's the only example of a MainMenu-style control with dark theme support, so maybe worth investigating.

The question I have is whether dark theme support is within the scope of this issue - if WinForms were to properly support dark theme, it would have to be applied to every control and not just the menus/tool strips, and provide proper colors to user controls that will also take time to implement.

Dark menus and light everything else looks very jarring, examples being the Control Panel and early versions of dark Explorer. IMO it's much better to have a consistent light theme instead, and give control to developers to decide whether their app is ready to respond to system-wide light/dark theme setting (unless the intention is to force all devs to implement dark theme for all new apps and apps ported from .NET Framework, but that seems extreme).

Notepad and MMC seem to just use the native control, which looks the most consistent today.

That is my assessment as well. And for now there is no support for dark theme, but they both support High Contrast. I think for this issue and the related ContextStrip/MenuStrip discussions I'm happy to consider them as our baseline, and move to Explorer for when we support Dark Theme. I'll work with @OliaG to see if we can find design guidance for the colors once it's time to implement a dark theme support.

The question I have is whether dark theme support is within the scope of this issue - if WinForms were to properly support dark theme, it would have to be applied to every control and not just the menus/tool strips, and provide proper colors to user controls that will also take time to implement.

I fully agree, but I think it's worth investigating and designing an approach that can then be extended to the other controls we own. We are considering putting the larger workitem on the roadmap and attempting to pick up things like the dark theme in addition to high contrast theming. Whatever we implement we won't have dark theme support without some sort of flag or property that turns it on.

the strip items should use the same renderer as the deprecated versions when using rendermode system

  • tool strip => tool bar
  • menu strip => main menu
  • context strip => context menu

Then the native render should be updated to fit windows 10's current theme. (meaning a consistent look for winforms and wpf)

note I think this should support older versions of windows for backwards compatibility.
literally just use the same renderer and the old versions

You cannot use the theme rendering from older OS versions on a newer OS, its literally integrated into the OS, you can only use whatever is "current". WinForms would only call the provided OS function and not reimplement the theming from scratch. When running the code on older OS versions you obviously would get "their" version of the theme rendering.

Doesn't win32,mfc, or winforms have a native rendering class?
if so just use that.

if not then the native render should be reimplemented for all os versions.
WinForms might be able to be compiled to native code someday and it would be nice to be able to write native apps that are backwards compatible with win32 versions

Edge is probably the new bar Win32 and UXTheme should aim for.

With the "Sun Valley" UI Refresh being reported, the Windows 10 look should be updated to resemble WinUI and FluentUI * - so when this happens, the UXTheme should update, Dark Mode should be formalised and consistent for Win32 apps.

I would also expect the default system WPF Theme and WinForms controls should all match.

* Padding, Sizing, Margins, FontSizes, and other layout metrics - will probably be unchanged to not break existing UI

not on older systems.
on older systems it should use the native win32 style of that os

@RussKie Here is a example of native menu bars on older versions of windows
image
I'm sure you can run the various versions of windows in a emulator to find out how the rest of the controls look
This is only relevant to older versions of windows.
Anyways anything that's is written in wpf, blend, winform's newer controls, uwp is not native. (at least not yet)

I'm not sure about windows 10 but I would assume a style needs to be agreed upon by the teams for win32 and other GUI frameworks need to have themes that match for their supported versions of windows

@Shadowblitz16 thank you for the examples.

.NET supports only as far back as W7SP1 (which will probably too be dropped at some point in not too distant future).
Theming has also significantly changed with each version of Windows, i.e. Windows 7 theming is very different from Windows 10 theming, both in terms of user experiences and API. 'Sun Valley' will likely throw a spanner into works redefining what the new Windows UX and UI are, and it is totally unclear at this stage what it'd mean for Win32 apps.

I totally agree that Win32 GUI SDKs need to have a shared set of UI, UX, a11y and styling guidelines, in reality it is almost unattainable. That's said, we're engaging with Windows teams to see how we can bridge the gap.

Was this page helpful?
0 / 5 - 0 ratings