Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Suggestion] Introduce Dark Mode and A11Y compatible Visual Styles for Apps targeting .NET 9 and Win 11 #7641

Closed
17 of 20 tasks
Tracked by #6270
KlausLoeffelmann opened this issue Aug 23, 2022 · 57 comments · Fixed by #11857
Closed
17 of 20 tasks
Tracked by #6270
Assignees
Labels
api-approved (4) API was approved in API review, it can be implemented area-Theming blocking Used by the API Review Board
Milestone

Comments

@KlausLoeffelmann
Copy link
Member

KlausLoeffelmann commented Aug 23, 2022

Updated 8/10/2024]

After careful, long and intensive discussions, all of us decision makers around this feature came to the conclusion that we needed to move this feature to .NET 10. I don't exaggerate by saying, that I am probably not only one of the people most disappointed by that (still correct) decision, but also one of the most effected (@RussKie FYI), as you can imagine, since I need to reconceptualize quite some work which is based on this.

While nothing is planned to change in terms of APIs, we already know we will separate ColorMode and VisualStylesMode out into separate PRs, because it makes backwards compatibility testing and maintaining WAY easier.

That's why I've just close the respective PR #10985, and then I will cherry-pick commits starting end of next week (I need a few days to grief and recharge), so that we will have 2 new PRs ASAP, resembling the same work, which we aim to bring directly into Preview 1 for .NET 10.

Rationale

With a few new APIs which the Windows Team now made publicly available, we can start to introduce Dark Mode functionality for WinForms. This is one of our most requested customer features. By introducing CsWin32 to our WinForms repo, adapting more and more Windows Dark Mode Features overtime to WinForms is also comparatively easy to achieve.

What this feature is and is not

  • We have NO intention to introduce full-blown theming support in WinForms with this.
  • We want to be very mindful with this topic, since there are a lot of theming standards already out there.
  • The API is meant as an opt in: You can use it, but if you do not, nothing should break. Existing theming concepts should continue to work as they are.
  • For .NET 9, the new APIs will be all under the Experimental attribute, so they might be subject to change or even to be replaced.

Goals and Non-Goals of this first experimental iteration

Goal:

  • In .NET 9+, any app just recompiling (or rolling forward) retains light theme.
  • Allow an app wide dark theme setting (either explicitly dark or "use system light/dark").
  • When high contrast is enabled, ignore this setting and use the high-contrast colors.
  • Avoid having a mess of light & dark mode but allow overrides on a per window/control basis.

Non-Goals

  • Support theming more than just light / dark mode.
  • Support dark mode prior to Windows 11.

In addition, we figure that not only Dark Mode in particular is one of the most requested features, but it can also impose a real accessibility challenge for people - all the more, when users with visual impairments or on the neuro-diversity spectrum need to deal for example with an 80% dark and 20% bright control (like control's content dark, scrollbars light). We know, we cannot address every control, since some of them are not really themable, but we want to standardize with this what we can, to honor those requirements as best as possible, und make sure a control can be completely dark themed (or not at all).

General approach for applying Dark Mode

As with the existing APIs on Application, we have a Set method and a correlating property to control dark mode at application level:

Image

Later I guess we should have an additional project setting, so that also the Designer could pick up that color scheme, and then we generate and inject via ApplicationConfiguration.Initialize().

If you pick System, then the System setting is applied wherever possible.
(Challenging are TabControl, MonthCalendar and therefore DateTimePicker - but it seems we have workarounds for that to mitigate this - more to follow soon.)

System as a dark mode setting is a "logical ambient" property, means that any control with that setting looks at its parent - a top-level control without a parent would look at Application.ColorMode and the Application using System would then look at the System.

Versioning Visual Styles for current and future A11Y requirements

Visual Styles Mode for versioning how controls render in different versions of the Framework is essential to ensure backward compatibility. This is especially important when new accessibility laws require changes in the UI, such as increasing the minimum size, padding, or margins of controls.

In some cases, these adjustments can break the pixel-perfect layout of Forms or User Controls. Additionally, controls derived from a base control that changes how it requests space can potentially break other derived controls. Here are a few scenarios where versioning visual styles is required:

  • New Accessibility Laws: To comply with new accessibility standards, controls may need to have larger minimum sizes, padding, or margins. For example, adjusting the padding of a TextBoxBase derived control might not be feasible without breaking existing layouts.

  • Dark Mode: Dark Mode can introduce contrast issues with closely positioned controls, especially when they have non-controllable border sizes or padding. Ensuring proper spacing and rendering adjustments in Dark Mode is crucial.

Image

  • High-DPI Modes: Controls with borders defined by fixed pixel widths rather than DPI-scaled thicknesses can lead to scaling issues. Adjustments may be needed to ensure these controls render correctly in high-DPI environments.

The APIs are added to Application and Control classes, incorporating "VisualStyles" in their names to handle these scenarios. This ensures that both new and existing applications can benefit from enhanced rendering while maintaining compatibility with previous versions of the Framework.

Image

Form (Window) Title Bar control

There will be new APIs to allow customization of the Form's title bar, including setting colors for the window border, caption, and caption text. This is based on new official public Win32 APIs, ensuring consistent and customizable window theming across different applications.

The following is taken from the WinForms Async Feature demo, also showing, how closely related async and A11Y are in terms of styling, fluent design and responsive UIs (Only the first seconds are relevant, as they demo the changing of the color of the Window's title bar.)

Image

These new APIs are basically a wrapper around (now) public Windows DWM API DwmSetWindowAttribute using the respective attributes:

  • DWMWA_BORDER_COLOR
  • DWMWA_CAPTION_COLOR
  • DWMWA_TEXT_COLOR
  • DWMWA_WINDOW_CORNER_PREFERENCE

Current Progress

One thing which is already be important to know (and to have in the docs later, should we get this feature in for .NET 9): We will most likely not achieve dark mode for all controls from the get-go (probably even for some of them ever). Challenging controls are MonthCalendar, DateTimePicker and TabControl at this point. Meanwhile, there seem to be workarounds available, which would mitigate issues with those controls.

DarkModeDemo

  • New API for DarkMode control on Application.
  • New API for DarkMode control on Control.
  • Mapping SystemColors -> Application.SystemColors/patch for DarkMode
  • New API for controlling Form Titelbar Color/BorderColor/TextColor
  • Scrollbars for Form/ScrollableControl/ListView/TreeView/ListBox/ComboBox
  • Radio Button
  • CheckBox
  • ComboBox DarkMode
  • Button DarkMode
  • TreeView DarkMode
  • ListView DarkMode
  • TextBox/RichtextBox
  • Strips - Still have issues, but the basics work fine!
  • Groupbox Caption
  • ColumnHeaders ListView Forground Text
  • ListView Forground Group Caption (might be an issue to get it brighter).
  • DataGridView (still needs some fine-tuning though in edge cases).
  • TabControl (Scrollbars are working in DarkMode alright, but theming this thing would be a very improvised construct. If someone has a good idea or approach, have at it!)
  • MonthCalendar - doesn't style with XP-styling at all, could be styled, if we disabled XP styling.
  • DateTimePicker - uses MonthCalendar, so same applies.

Current list of new APIs

Note: We will for .NET 9 implement the new APIs under the Experimental attribute, so we can collect and react to respective feedback, and then introduce the new APIs finally in .NET 10 based on that feedback.

[Subject to change by API Review]

// ***************************************
// *** Dark Mode
// ***************************************

namespace System.Windows.Forms
{
    // Note: We would call it now SystemColorMode to acknowledge that at one point 
    // Windows might add more modes we need to pick up. So, just "DarkMode" in that case 
    // wouldn't do it. ColorMode would also not work, since it already exists.
    // Note: Nothing to do with theming. We are just taking what Windows offers!
    [Experimental("WFO9000")]
    public enum SystemColorMode
    {
        Classic = 0	// Reason: We might later pick up Color Modes from Windows,
			// which are also Light-based. This implies "original setting".
        System = 1,	// Takes the Mode from the Window OS Environment.
        Dark = 2,	// We are forcing the primary dark mode scheme.
    }

    public static partial class Application
    {
        [Experimental("WFO9000")]
        public static SystemColorMode ColorMode { get; }  // Returns the actual color mode (Classic or Dark - never System).

	// Sets the Color mode for the Application. Every value can be passed. If a mode is not available,
	// nothing happens. You can check the actual result with ColorMode.
        [Experimental("WFO9000")]
        public static bool SetColorMode(SystemColorMode colorMode)

	// Returns the ColorMode of the Windows OS System, either Classic or Dark.
        [Experimental("WFO9000")]
        public static SystemColorMode SystemColorMode { get; }
    }

    public class TextBoxBase : Control
    {
        // For A11Y reasons, we un-shadow Padding for TextBoxBase to make Padding 
        // for TextBox based controls actually work. Will only have any effect, if 
	// VisualStylesMode is set to > Classic. Note, that we've modernized the
	// Adorners in that case, also to visually indicating more default padding 
	// in ANY of the BorderStyle settings, to meet the minimum upcoming 
	// A11Y requirements of 24 pixels (Border-less 96dpi textboxes only have 18/22 Px).
-       public new Padding Padding {get; set; }
    }

    public enum ControlStyles
    { .
      .
      .
      /// <summary>
      ///  For certain UI-related color modes (Dark Mode/Light Mode), controls
      ///  can opt-in to automatically apply the appropriate DWM theming.
      /// </summary>
      [Experimental("WFO9001")]
      ApplyThemingImplicitly = 0b00001000_00000000_00000000, // 0x00080000
    }

   // ***************************************
   // Windows Title Bar Control (based on new official public Win32 APIs)
   // ***************************************

    public unsafe partial class Form
    {
        // Existing partial class members...

        [Experimental("WFO9000")]
        public void SetWindowBorderColor(System.Drawing.Color color)

        [Experimental("WFO9000")]
        public void SetWindowCaptionColor(System.Drawing.Color color)

        [Experimental("WFO9000")]
        public void SetWindowCaptionTextColor(System.Drawing.Color color)

        [Experimental("WFO9000")]
        public void SetWindowCornerPreference(WindowCornerPreference cornerPreference)

        [Experimental("WFO9000")]
        public enum WindowCornerPreference
        {
            Default = 0,
            DoNotRound = 1,
            Round = 2,
            RoundSmall = 3
        }
    }

    // ***************************************
    // Visual Styles Versioning [Updated after API-Review]
    // ***************************************

    [Experimental("WFO9001")]
    public enum VisualStylesMode : Short
    {
        Classic = 0,     // was 1, saves time on initialization.
        Disabled = 1, // was 0, saves time on initialization.
        Net10 = 2     
        Latest = Short.Max
    }

    public static partial class Application
    {
        [Experimental("WFO9001")]
        public static VisualStylesMode DefaultVisualStylesMode { get; }

        [Experimental("WFO9001")]
        public static void SetDefaultVisualStylesMode(VisualStylesMode styleSetting)
    }

    public unsafe partial class Control 
    {
        [Experimental("WFO9001")]
        public VisualStylesMode VisualStylesMode { get; set; }

        [Experimental("WFO9001")]
        public event EventHandler? VisualStylesModeChanged

        [Experimental("WFO9001")]
        protected virtual void OnVisualStylesModeChanged(EventArgs e)

	// Allows a derived Control to set a particular Styles mode for backwards compatibility reasons.
        [Experimental("WFO9001")]
        protected virtual VisualStylesMode DefaultVisualStylesMode { get; }
    }

    /// To address checkbox A11Y min size requirements.
    public enum Appearance
    {
        Normal = 0,
        Button = 1,

        /// <summary>
        ///  The appearance of a Modern UI toggle switch.
        ///  This setting is not taken into account, when <see cref="VisualStylesMode"/> is set
        ///  to <see cref="VisualStylesMode.Disabled"/> or <see cref="VisualStylesMode.Classic"/>.
        /// </summary>
        [Experimental("WFO9001")]
        ToggleSwitch = 2
    }
}

New Visual Basic Framework APIs

Since WinForms Apps, which are based on the Windows Forms Application Framework, do not have the equivalent of the Program.cs, we would suggest the following new APIs in the same style as the existing VB default settings:

Image

Namespace Microsoft.VisualBasic.ApplicationServices

    Public Class ApplyApplicationDefaultsEventArgs
        Public Property ColorMode As System.Windows.Forms.SystemColorMode
        Public Property VisualStylesMode As System.Windows.Forms.VisualStylesMode
    End Class

    Public Class WindowsFormsApplicationBase
        Public Property ColorMode As System.Windows.Forms.SystemColorMode
        Public Property VisualStylesMode As System.Windows.Forms.VisualStylesMode
    End Class

End Namespace
@KlausLoeffelmann KlausLoeffelmann added the api-suggestion (1) Early API idea and discussion, it is NOT ready for implementation label Aug 23, 2022
@KlausLoeffelmann KlausLoeffelmann added this to the 8.0 Preview1 milestone Aug 23, 2022
@RussKie
Copy link
Member

RussKie commented Aug 23, 2022

I suggest avoiding "dark" in the API names. At some point in time Windows could introduce a new mode - say, blue - and that wouldn't fit into the proposed API.

Historically, the colours were part of the theme definition, now it appears Windows is making a distinction between "colours" and "themes" - the former is the selection of "light" vs "dark", and the latter is now accessibility related:

image
image

I think IsDarkModeEnabled should be a part of Application type - as this setting affects the application as a whole. As per the discussion with the Windows team, the only option for the Win32 app to find out the current configuration is vie the registry:

Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize
• SystemUsesLightTheme=0|1
• AppsUseLightTheme=0|1

NB: we still need to confirm this has been officially documented.

@KlausLoeffelmann
Copy link
Member Author

I suggest avoiding "dark" in the API names. At some point in time Windows could introduce a new mode - say, blue - and that wouldn't fit into the proposed API.

I thought about that for a while. I decided to use Dark Mode in the name, because I followed the public Windows API, which the prototype is based on, and which does the same. Another point is that Dark Mode is meanwhile a buzz word, so I'd argue it helps with discoverability. Therefore, I strongly advocate to continue using Dark Mode.

I think IsDarkModeEnabled should be a part of Application type - as this setting affects the application as a whole.

This is what Application.EnvironmentDarkMode does. It returns either Enabled or Disabled ... or ... NotSupported. And this is why a boolean value does not suffice. You want not only to know, if it's on or off, you also want to know, if it's available altogether on that particular machine/platform.

As per the discussion with the Windows team, the only option for the Win32 app to find out the current configuration is ...

Yes. That's how I implemented the prototype already. There is another way which we already know is supported, but that one does use WinRT projected API, and I want to talk with @JeremyKuhne first, if that is an option for us. Also, that way to achieve it seems a bit stilted, but I am still investigating this, so, we'll see what route will be best.

@paul1956
Copy link
Contributor

How is the prototype/feature going to be exposed? Does it depend on specific version of Windows 7/10/11/next...? Do Control vendors custom controls need to support this?

@kirsan31
Copy link
Contributor

kirsan31 commented Aug 24, 2022

Guys I am sorry, but when it comes to themes / modes - #3691 immediately rings in my head (and I can't get away from it) 🙄

@KlausLoeffelmann
Copy link
Member Author

Sadly true. I am only very carefully optimistic that Windows will change anything in that regard. And I know custom NC-Painting is probably not an option. @RussKie, didn't you have something here?

@DualBrain
Copy link

Yep... yep... yep... yep... and yep!

@RussKie
Copy link
Member

RussKie commented Aug 30, 2022

didn't you have something here?

Yes, I tinkered with NC- and C-painting of the form in the VS designer context. I got some partial progress, but that'd require more time (which is a bit scarce). Custom rendering of MDI windows could be a significant challenge.

@kirsan31
Copy link
Contributor

Yes, I tinkered with NC- and C-painting of the form in the VS designer context. I got some partial progress, but that'd require more time (which is a bit scarce). Custom rendering of MDI windows could be a significant challenge.

Is the Windows team so untouchable? 😕

@paul1956
Copy link
Contributor

Are MDI windows used that much that a 99% solution isn't worth releasing as a start?

@KlausLoeffelmann
Copy link
Member Author

KlausLoeffelmann commented Aug 30, 2022

The issue here also is that MDI-Windows aren't really the best idea for user interfaces, and so neither Windows nor we recommend these. We have no plans to really obsolete them alright, but they aren't really a good idea. That said, the rendering of MDI Windows is one thing. But in WinForms, you can also host a Form as a control in another Form. That then isn't technically an MDI window, although the same (rendering) principle applies. Which leads to the outdated look and feel, that cannot be changed easily (so, also the same applies as for MDI windows). Our own Designer is bitten by that: The reason the Designer Form adornments are looking outdated is the reason for that, so there is some internal motivation to change that. But at the same time, you don't really do much with the (NC) window controls of the Form in the Designer in this case. Actually nothing: The adornments of the Form in the Designer are just supposed to look pretty. Nothing else. The window never really moves inside its container, you can only resize it. With MDI windows that's quite different: And I am afraid, that NC painting will just get its own "dynamics" at one point and opens all other sorts of problems in the Real world. That's why I am personally really hesitant to address the Windows-inside-Windows-look-outdated issue with custom NC painting in the runtime and per se.

@DualBrain: Do you have any experiences here in that regard?

@kirsan31
Copy link
Contributor

kirsan31 commented Aug 30, 2022

@KlausLoeffelmann

The issue here also is that MDI-Windows aren't really the best idea for user interfaces, and so neither Windows nor we recommend these. We have no plans to really obsolete them alright, but they aren't really a good idea.

Can I ask - why (except #3691 of course 😁 )? I often hear a similar statement but without any arguments 🤷‍♂️ But I have arguments in defense of MDI:

  1. MDI is the only multi windows layout that WinForms can offer you out of the box.
  2. This is the most stable (error free) noncommercial multi windows layout for WinForms.
  3. So far great if you need to manage multiple windows that are visible at the same time and can be quickly minimized/restored. I mean for such tasks MDI still better fit then V.S. dock layout...
  4. About obsoleting MDI... Pls look at WinForms repo issues sorted by 👍: image
    I think if you say that MDI is outdated and not worth fixing / maintaining, then all this can be safely projected onto the entire WinForms 😥

@DualBrain
Copy link

But in WinForms, you can also host a Form as a control in another Form.

I will confirm that I do (regularly) utilize this technique and can share that it does present some challenges - with that said, sometimes these challenges are less of an issue than attempting alternative methods (code rewrite, inheritance, etc.); a real pros vs. cons situation where accepting the challenges/limitations of embedding a window within a window.

Now with that said, when I do this it is typically done in a way where the embedded window is treated more like a control rather than something that will move about within the confines of the containing window.

@DualBrain
Copy link

I do tend to agree that MDI (in its current form) is a bit dated; however, there many scenarios where this approach is still very valid. While I don't personally design projects around this model, I do stumble across projects every once and a while that do; as such, I don't see MDI going away any time soon - at least not without a very solid/clear replacement available.

In order to handle "dark mode", what I've currently been doing is pretty much working within the very limited feature set that can do it and trying to design my applications to work within these limitations.

Honestly, I really didn't even realize how limited this really was until I attempted to get @paul1956's project switched over... meaning trying to apply dark mode to an already existing project changing the overall design/layout/interface really isn't that much of an option.

Some components are simply "too difficult" to repaint without effectively replacing them because they don't expose any functionality to affect any sort of color changes (comboboxes) while others are what I would refer to as being 90-95% there, but that last little percent is very visibly annoying (toolbar/statusbar). Menus are actually pretty good.

With all that said, the first thing that is needed is exactly what the original topic of this issue raises... need some way to identify that Windows a) supports dark mode and b) is in dark mode. Sure, you can just have a mechanism in place to allow the end-user to select / switch between; but from my point of view I would like to default my application to what the user has already specified within the system.

And, assuming that is possible, have the ability to tell my application that it is in dark mode or not affecting (automagically) standard components accordingly. This does need to be something that I, as the developer, can manually toggle in a tri-state manner - auto (system setting), enabled (dark mode active), disabled (dark mode inactive).

As to whether or not MDI handles this or if MDI delays this getting done... my opinion is that this feature should be something that could be made available incrementally; meaning it needs to be totally "opt-in" allow for use to take advantage of these pieces if we are willing/able. To say this another way, I don't think dark mode capability should be an all or nothing feature... identify the main sore spots and address those incrementally. Default to application.darkmode = disabled allowing for enabled/auto.

I recognize that I'm making this sound easier than it really is; but I really don't favor the approach of waiting 3 years to get a better combobox, status bar, etc. (meaning the more common of the common controls).

As for the menu... please, please, please feel free to "borrow" from my efforts and use as you see fit. Again, I'd love to just be able to set a single property and "it just work".

@KlausLoeffelmann
Copy link
Member Author

@kirsan31, @DualBrain: When it comes to MDI Windows, WinForms just follows the Windows/Office guidelines. It doesn't have to do with my/general personal preference, it's just a very functional reason: When multi-monitor display scenarios became common even in conservative work environments like Banks, Insurance Companies, Government Departments (all typical WinForms target audiences), MDI user interface just became totally unpractical: You simply can't drag a document to a secondary monitor, when you have a constraining MDI parent on the primary monitor. And with that (I say that from a very strong personal experience as a consultant in the area of migrating/modernizing Win32, VB6 and older WinForms/WPF apps in a previous life) End Users started to demand other UI paradigms.

@KlausLoeffelmann
Copy link
Member Author

Still, I recognize that we need to support existing apps with that - so, no, no one said anything about obsoleting it. All that I am saying is, that it'll be very likely that we (WinForms) won't invest in a scenario, that a) doesn't make sense in modern work environments, and b) that isn't very likely supported (in terms of modernizing) by Windows. Should they decide to introduce theming, then we would very likely pick that up and enable it. But I don't think it would make sense for us (WinForms) to start messing with a NC-Custom-rendering approach.

@DualBrain
Copy link

@KlausLoeffelmann Sorry if it didn't come across properly; but I was trying to clarify that MDI is what it is, pretty much isn't (or rather shouldn't be) going anywhere and although not generally recommended, there are scenarios where it is being used and/or may be useful. As I said, I don't particularly utilize it as I would much rather have docking, tabbed, tear-away model than MDI. ;-)

Additionally, I definitely don't want MDI doing one thing or another preventing/delaying the moving forward with dark mode.

@kirsan31
Copy link
Contributor

kirsan31 commented Aug 31, 2022

I think I've been misunderstood here. I didn't offer to slow down this work because of the MDI. I just wanted to remind that we have an unfixed bug on this topic for a very long time, and it's time to kick the Windows team harder...

@KlausLoeffelmann for not to flood, I answered in the relevant topic.

@Balkoth
Copy link
Contributor

Balkoth commented Sep 8, 2022

Dark mode is easily my most wanted feature for winforms. Second would be a working PMv2 mode...

@driver1998
Copy link

Is the dark common control theme documented now? Last time I checked it is still undocumented. Only the dark title bar attribute is documented and public.

@JeremyKuhne
Copy link
Member

I don't understand how SystemColors.DarkMode is meant to be used. Imagine I have Application.DefaultDarkMode = DarkMode.Disabled, but I set one specific control to DarkMode.Enabled. That one control would need to render with dark mode colors, right? How does it access those colors?

I don't think it can in the current shape. I'm looking at this particular aspect with @KlausLoeffelmann currently and we'll update.

@KlausLoeffelmann
Copy link
Member Author

I don't understand how SystemColors.DarkMode is meant to be used. Imagine I have Application.DefaultDarkMode = DarkMode.Disabled, but I set one specific control to DarkMode.Enabled. That one control would need to render with dark mode colors, right? How does it access those colors?

I don't think it can in the current shape. I'm looking at this particular aspect with @KlausLoeffelmann currently and we'll update.

Yes. In the original design, I made both palettes principally accessible. But the challenge then is, that you cannot work with the default values in existing control implementations. That would then only work, if Color itself had the option to return the original defined SystemColor based on a palette index or setting at the time of the querying. But we determined that this would be WAY too risky in the remaining time frame.

So, as a first version, we rather aim now to control dark mode on Application scope, but no longer at Control scope at all.

@JeremyKuhne JeremyKuhne modified the milestones: .NET 9.0, 9.0 RC1 Jul 24, 2024
JeremyKuhne added a commit to JeremyKuhne/runtime that referenced this issue Jul 29, 2024
Allows overriding KnownColor system values with an alternate set, which in the initial iteration is "dark mode" colors. Enables "dark mode" features in Windows Forms.

This is from the approved part of dotnet/winforms#7641 with further naming iteration done offline with API review.
JeremyKuhne added a commit to dotnet/runtime that referenced this issue Jul 29, 2024
Allows overriding KnownColor system values with an alternate set, which in the initial iteration is "dark mode" colors. Enables "dark mode" features in Windows Forms.

This is from the approved part of dotnet/winforms#7641 with further naming iteration done offline with API review.
@terrajobst terrajobst added api-ready-for-review (2) API is ready for formal API review; applied by the issue owner and removed api-needs-work (3) API needs work before it is approved, it is NOT ready for implementation; applied by repo owners labels Jul 30, 2024
@terrajobst
Copy link
Member

terrajobst commented Jul 30, 2024

Video

  • Having two diagnostics (one for dark mode and one for visual styles works)
  • Microsoft.VisualBasic.ApplicationServices
    • The props on WindowsFormsApplicationBase should be marked as [EditorBrowsable(Never)]
    • The props should be marked as experimental as well
    • While the feature is experimental there won't be any UI for changing the dark mode / visual styles, it can only be done in user code, which also ensures that the user gets a diagnostic unless they opt-in
namespace System.Windows.Forms;

[Experimental("WFO9000")]
public enum SystemColorMode
{
    Classic = 0,
    System = 1,	
    Dark = 2,	
}

public static partial class Application
{
    [Experimental("WFO9000")]
    public static SystemColorMode ColorMode { get; }  

    [Experimental("WFO9000")]
    public static bool SetColorMode(SystemColorMode colorMode);

    [Experimental("WFO9000")]
    public static SystemColorMode SystemColorMode { get; }
}

public class TextBoxBase : Control
{
    // Remove:
    // public new Padding Padding {get; set; }
}

public enum ControlStyles
{
    [Experimental("WFO9001")]
    ApplyThemingImplicitly = 0b00001000_00000000_00000000, 
}

public unsafe partial class Form
{
    [Experimental("WFO9000")]
    public void SetWindowBorderColor(Color color);

    [Experimental("WFO9000")]
    public void SetWindowCaptionColor(Color color);

    [Experimental("WFO9000")]
    public void SetWindowCaptionTextColor(Color color);

    [Experimental("WFO9000")]
    public void SetWindowCornerPreference(WindowCornerPreference cornerPreference);

    [Experimental("WFO9000")]
    public enum WindowCornerPreference
    {
        Default = 0,
        DoNotRound = 1,
        Round = 2,
        RoundSmall = 3
    }
}

[Experimental("WFO9001")]
public enum VisualStylesMode : Short
{
    Classic = 0,
    Disabled = 1,
    Net10 = 2,
    Latest = short.MaxValue
}

public static partial class Application
{
    [Experimental("WFO9001")]
    public static VisualStylesMode DefaultVisualStylesMode { get; }

    [Experimental("WFO9001")]
    public static void SetDefaultVisualStylesMode(VisualStylesMode styleSetting);
}

public unsafe partial class Control 
{
    [Experimental("WFO9001")]
    public VisualStylesMode VisualStylesMode { get; set; }

    [Experimental("WFO9001")]
    public event EventHandler? VisualStylesModeChanged;

    [Experimental("WFO9001")]
    protected virtual void OnVisualStylesModeChanged(EventArgs e);

    [Experimental("WFO9001")]
    protected virtual VisualStylesMode DefaultVisualStylesMode { get; }
}

public enum Appearance
{
    Normal = 0,
    Button = 1,
    [Experimental("WFO9001")]
    ToggleSwitch = 2
}
namespace Microsoft.VisualBasic.ApplicationServices;

public partial class ApplyApplicationDefaultsEventArgs : EventArgs
{
    [Experimental("WFO9000")]
    public SystemColorMode ColorMode { get; set; }

    [Experimental("WFO9001")]
    public VisualStylesMode VisualStylesMode { get; set; }
}

public partial class WindowsFormsApplicationBase
{
    [Experimental("WFO9000")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public SystemColorMode ColorMode { get; set; }

    [Experimental("WFO9001")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public VisualStylesMode VisualStylesMode { get; set; }
}

@terrajobst terrajobst added api-approved (4) API was approved in API review, it can be implemented and removed api-ready-for-review (2) API is ready for formal API review; applied by the issue owner labels Jul 30, 2024
@merriemcgaw merriemcgaw modified the milestones: 9.0 RC1, .NET 10.0 Aug 9, 2024
@merriemcgaw
Copy link
Member

Important!

I've made the extremely hard decision to bump this feature's release to 10. This was coming in awfully hot for .NET 9 RC1 and wasn't looking like everything we needed to do internally (t's crossed and i's dotted) could be addressed in time to meet code complete, which is in just a couple of days. Rather than try to force it into RC1 I decided the best course of action will be to move it to 10 and have it ready to go well before the first preview. Given the bake time it will have in the next release, I think we can be confident of a successful release in 10.

Those of you who are invested in the Dark Mode and VisualStyles work - we want all of the feedback that we can get. You will be able to start seeing builds with this feature work in a few weeks at most when .NET repos move Main to target .NET 10. We have every confidence in this feature and are looking forward to having it ready for you with the very first builds of .NET 10 for your evaluation.

As always, if you have any questions, please don't hesitate to ask.

@KlausLoeffelmann KlausLoeffelmann unpinned this issue Aug 9, 2024
@RussKie
Copy link
Member

RussKie commented Aug 11, 2024

I can fully appreciate how uneasy it was to make this decision, but given the timelines I do think you made the right call.

@RussKie
Copy link
Member

RussKie commented Aug 12, 2024

Looks like the dark-mode landed in .NET 9 afterall --> #11857
🎉

@RussKie RussKie linked a pull request Aug 12, 2024 that will close this issue
@paul1956
Copy link
Contributor

Congratulations to everyone that made it happen.

Now that it is/will be here, as a experiential feature, what does one need to do to try it?
Do I need to wait for a preview version of VS where its included?
After you answer above what are the changes, I need to make to my app to try it.

@creizlein
Copy link

Looks like the dark-mode landed in .NET 9 afterall --> #11857
🎉

I don't understand anything now. Anyone care to explain? Hehehe. A day ago it was pp to .NET 10 🙄

@MagicAndre1981
Copy link

great, so we can test our apps with .net9 and ship dark mode with .net 10 . Perfect.

@KlausLoeffelmann
Copy link
Member Author

KlausLoeffelmann commented Aug 12, 2024

Closing comments:

  • A stripped-down version of the originally envisioned Dark Mode/Modern Styles Features has made it into .NET 9 as Experimental Feature, meaning why Application.ColorMode is there; the originally planned...
  • ...modernized VisualStyles API unfortunately did NOT make it into .NET 9 and has been moved to .NET 10.
  • VisualBasic has a new API to control dark mode via the application framework default events.
  • Control.InvokeAsync is part of .NET 9.
  • Form.ShowAsync and Form.ShowDialogAsync are in .NET 9 as Experimental Feature.
  • TaskDialog.ShowDialogAsync are in .NET 9 as Experimental Features.

The new APIs are scheduled to go in into the .NET 9 RC1 SDK.

You should be able to test them earlier in the upcoming days by installing a nightly build from here:
https:/dotnet/sdk/blob/main/documentation/package-table.md

I don't understand anything now. Anyone care to explain? Hehehe. A day ago it was pp to .NET 10 🙄.

Let's put it that way:
Let's all just be glad that it could get in as Experimental in .NET 9, so we can start testing and refining and polish it for .NET 10.

@JeremyKuhne
Copy link
Member

Looks like the dark-mode landed in .NET 9 afterall --> #11857
🎉

I don't understand anything now. Anyone care to explain? Hehehe. A day ago it was pp to .NET 10 🙄

There was close to zero expectation we would be able to meet the .NET code complete deadline (yesterday). @KlausLoeffelmann and I worked a very long weekend to close out async support and made a moon shot to try and get at least dark mode in. A combination of skill, hard work, and luck got us to a much happier place.

("Code complete" means no new features.)

@terrajobst
Copy link
Member

@creizlein

I understand the confusion, but I don't quite understand the negativity. I saw your earlier comment as well (which I think was deleted). Maybe I'm missing something but from what I can tell the WinForms team tried very hard to land Dark Mode, despite the fact that it came in late. Do you feel like there is some transparency in planning missing? Or where does your frustration come from?

@RussKie RussKie removed the 🚧 work in progress Work that is current in progress label Aug 12, 2024
@AraHaan
Copy link
Member

AraHaan commented Aug 20, 2024

Since this is delayed till .NET 10, having an actual theme interface that derives from ProfessionalColorTable I think would be a worthwhile impl of this in winforms and can be refined after that as needed, also it would allow an application to load those values from json config as well if they so desire (I was considering doing my own fork of winforms that does just that btw to see how well that would work out).

@github-actions github-actions bot locked and limited conversation to collaborators Sep 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved (4) API was approved in API review, it can be implemented area-Theming blocking Used by the API Review Board
Projects
None yet