diff --git a/.editorconfig b/.editorconfig index a5b52c7ebe2..47af5a77edf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -70,13 +70,13 @@ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case dotnet_sort_system_directives_first = true # Expression-level preferences -dotnet_style_object_initializer = true:warning +dotnet_style_object_initializer = true:suggestion dotnet_style_collection_initializer = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_null_propagation = true:warning -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion dotnet_style_prefer_auto_properties = true:silent -dotnet_style_prefer_simplified_boolean_expressions = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_compound_assignment = true:suggestion @@ -319,6 +319,9 @@ dotnet_diagnostic.CA5397.severity = warning # RS0041: Public members should not use oblivious types dotnet_diagnostic.RS0041.severity = warning +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent # C# files @@ -497,6 +500,12 @@ dotnet_diagnostic.CA2016.severity = warning # CA2020: Prevent behavioral change caused by built-in operators of IntPtr/UIntPtr dotnet_diagnostic.CA2020.severity = warning +# WFO9000: +dotnet_diagnostic.WFO9000.severity = silent + +# WFO9001: +dotnet_diagnostic.WFO9001.severity = silent + # .NET diagnostic dotnet_diagnostic.RS0041.severity = none dotnet_diagnostic.IDE0005.severity = error @@ -532,9 +541,9 @@ csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = false # Expression-bodied members -csharp_style_expression_bodied_methods = true:silent -csharp_style_expression_bodied_constructors = true:silent -csharp_style_expression_bodied_operators = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent csharp_style_expression_bodied_properties = true:silent csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_accessors = true:silent @@ -572,12 +581,12 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false csharp_using_directive_placement = outside_namespace:silent -csharp_prefer_simple_using_statement = true:warning +csharp_prefer_simple_using_statement = true:suggestion csharp_prefer_braces = true:silent -csharp_style_namespace_declarations = file_scoped:silent +csharp_style_namespace_declarations = block_scoped:silent csharp_style_prefer_method_group_conversion = true:silent csharp_style_prefer_top_level_statements = true:silent -csharp_style_prefer_primary_constructors = true:silent +csharp_style_prefer_primary_constructors = true:suggestion csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_local_functions = false:silent csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent @@ -586,6 +595,7 @@ csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimenta csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion # Visual Basic files diff --git a/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj b/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj index e57cd2e7598..9e4d1bb5764 100644 --- a/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj +++ b/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj @@ -9,7 +9,7 @@ Microsoft.VisualBasic.Forms true - 15.0 + 15.5 None true true diff --git a/src/System.Private.Windows.Core/src/NativeMethods.txt b/src/System.Private.Windows.Core/src/NativeMethods.txt index d8c2ca6a07a..acf4dfdbc76 100644 --- a/src/System.Private.Windows.Core/src/NativeMethods.txt +++ b/src/System.Private.Windows.Core/src/NativeMethods.txt @@ -13,6 +13,7 @@ CreateDIBSection CreateFontIndirect CreateICW CreateRectRgn +CreateRoundRectRgn DeleteDC DeleteObject DestroyIcon @@ -57,6 +58,7 @@ E_OUTOFMEMORY E_POINTER E_UNEXPECTED EncoderParameters +ExtSelectClipRgn fdex* FDEX_PROP_FLAGS FILETIME @@ -94,6 +96,7 @@ GetSystemMetrics GetThreadLocale GetViewportExtEx GetViewportOrgEx +GetWindowDC GlobalAlloc GlobalFree GlobalLock @@ -142,7 +145,9 @@ MonitorFromWindow MONITORINFOEXW MONITORINFOF_* NONCLIENTMETRICSW +NCCALCSIZE_PARAMS OBJ_TYPE +OffsetClipRgn OffsetViewportOrgEx OLE_E_ADVISENOTSUPPORTED OLE_E_INVALIDRECT diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.EditorPropertyLine.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.EditorPropertyLine.cs index d667c15f31e..5ce716ad391 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.EditorPropertyLine.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.EditorPropertyLine.cs @@ -108,7 +108,7 @@ private unsafe void ActivateDropDown() try { - ShowDropDown(listBox, SystemColors.ControlDark); + ShowDropDown(listBox, Drawing.SystemColors.ControlDark); } finally { diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.TextBoxPropertyLine.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.TextBoxPropertyLine.cs index cb9d0644f29..22c0223b1d9 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.TextBoxPropertyLine.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.TextBoxPropertyLine.cs @@ -32,7 +32,7 @@ protected TextBoxPropertyLine(IServiceProvider serviceProvider, DesignerActionPa _readOnlyTextBoxLabel = new EditorLabel { BackColor = Color.Transparent, - ForeColor = SystemColors.WindowText, + ForeColor = Drawing.SystemColors.WindowText, TabStop = true, TextAlign = ContentAlignment.TopLeft, UseMnemonic = false, @@ -159,14 +159,14 @@ protected virtual void OnReadOnlyTextBoxLabelClick(object? sender, MouseEventArg private void OnReadOnlyTextBoxLabelEnter(object? sender, EventArgs e) { - _readOnlyTextBoxLabel.ForeColor = SystemColors.HighlightText; - _readOnlyTextBoxLabel.BackColor = SystemColors.Highlight; + _readOnlyTextBoxLabel.ForeColor = Drawing.SystemColors.HighlightText; + _readOnlyTextBoxLabel.BackColor = Drawing.SystemColors.Highlight; } private void OnReadOnlyTextBoxLabelLeave(object? sender, EventArgs e) { - _readOnlyTextBoxLabel.ForeColor = SystemColors.WindowText; - _readOnlyTextBoxLabel.BackColor = SystemColors.Window; + _readOnlyTextBoxLabel.ForeColor = Drawing.SystemColors.WindowText; + _readOnlyTextBoxLabel.BackColor = Drawing.SystemColors.Window; } protected TypeConverter.StandardValuesCollection? GetStandardValues() diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs index f6e7d691b09..7bcf96fe79b 100644 --- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DesignerActionPanel.cs @@ -117,9 +117,9 @@ public DesignerActionPanel(IServiceProvider serviceProvider) MinimumSize = new Size(150, 0); } - public Color ActiveLinkColor { get; } = SystemColors.HotTrack; + public Color ActiveLinkColor { get; } = Drawing.SystemColors.HotTrack; - public Color BorderColor { get; } = SystemColors.ActiveBorder; + public Color BorderColor { get; } = Drawing.SystemColors.ActiveBorder; /// /// Returns the list of commands that should be filtered by the form that hosts this panel. This is done so that these specific commands will not get passed on to VS, and can instead be handled by the panel itself. @@ -167,23 +167,23 @@ public DesignerActionPanel(IServiceProvider serviceProvider) /// private Line? FocusedLine => ActiveControl?.Tag as Line; - public Color GradientDarkColor { get; } = SystemColors.Control; + public Color GradientDarkColor { get; } = Drawing.SystemColors.Control; - public Color GradientLightColor { get; } = SystemColors.Control; + public Color GradientLightColor { get; } = Drawing.SystemColors.Control; public bool InMethodInvoke { get; internal set; } - public Color LinkColor { get; } = SystemColors.HotTrack; + public Color LinkColor { get; } = Drawing.SystemColors.HotTrack; - public Color SeparatorColor { get; } = SystemColors.ControlDark; + public Color SeparatorColor { get; } = Drawing.SystemColors.ControlDark; - public Color TitleBarColor { get; } = SystemColors.ActiveCaption; + public Color TitleBarColor { get; } = Drawing.SystemColors.ActiveCaption; - public Color TitleBarTextColor { get; } = SystemColors.ActiveCaptionText; + public Color TitleBarTextColor { get; } = Drawing.SystemColors.ActiveCaptionText; - public Color TitleBarUnselectedColor { get; } = SystemColors.InactiveCaption; + public Color TitleBarUnselectedColor { get; } = Drawing.SystemColors.InactiveCaption; - public Color LabelForeColor { get; } = SystemColors.ControlText; + public Color LabelForeColor { get; } = Drawing.SystemColors.ControlText; /// /// Helper event so that Lines can be notified of this event. diff --git a/src/System.Windows.Forms.Design/src/System/Drawing/Design/ColorEditor.ColorUI.cs b/src/System.Windows.Forms.Design/src/System/Drawing/Design/ColorEditor.ColorUI.cs index 5b99ed2a03d..22c8ea152c5 100644 --- a/src/System.Windows.Forms.Design/src/System/Drawing/Design/ColorEditor.ColorUI.cs +++ b/src/System.Windows.Forms.Design/src/System/Drawing/Design/ColorEditor.ColorUI.cs @@ -70,7 +70,7 @@ private Color[] CustomColors /// /// Array of system colors. /// - private Color[] SystemColorValues => _systemColorConstants ??= GetConstants(typeof(SystemColors)); + private Color[] SystemColorValues => _systemColorConstants ??= GetConstants(typeof(ControlSystemColors)); public object? Value => _value; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.SelectionUIItem.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.SelectionUIItem.cs index 2efe3e4195d..88eeec8a568 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.SelectionUIItem.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SelectionUIService.SelectionUIItem.cs @@ -135,7 +135,7 @@ public virtual void DoPaint(Graphics graphics) Rectangle inner = _innerRect; Rectangle outer = _outerRect; Region oldClip = graphics.Clip; - Color borderColor = SystemColors.Control; + Color borderColor = Drawing.SystemColors.Control; if (_control is not null && _control.Parent is not null) { Control parent = _control.Parent; diff --git a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt index 6dc088f67e9..0c70a732181 100644 --- a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt +++ b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt @@ -108,6 +108,8 @@ DSH_FLAGS DTM_* DTN_* DTS_* +DwmSetWindowAttribute +DWM_WINDOW_CORNER_PREFERENCE DuplicateHandle EC_* ECO_* @@ -135,6 +137,7 @@ EXTLOGFONTW ExtTextOut FDAP FillRect +FillRgn FindExecutable FINDREPLACE_FLAGS FindWindow @@ -164,6 +167,7 @@ GetClipboardFormatName GetClipBox GetClipCursor GetClipRgn +GetComboBoxInfo GetCurrentActCtx GetCurrentObject GetCurrentProcess @@ -600,6 +604,7 @@ SetActiveWindow SetBkColor SetBkMode SetCapture +SetCaretPos SetCursor SetCursorPos SetFocus diff --git a/src/System.Windows.Forms.Primitives/src/Windows/Win32/PInvoke.FillRgn.cs b/src/System.Windows.Forms.Primitives/src/Windows/Win32/PInvoke.FillRgn.cs new file mode 100644 index 00000000000..0d5478473c8 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Windows/Win32/PInvoke.FillRgn.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Windows.Win32; + +internal static partial class PInvoke +{ + /// + public static int FillRgn(T hDC, ref HRGN hRgn, HBRUSH hbr) + where T : IHandle + { + int result = FillRgn(hDC.Handle, hRgn, hbr); + GC.KeepAlive(hDC.Wrapper); + + return result; + } +} diff --git a/src/System.Windows.Forms/src/PublicAPI.Shipped.txt b/src/System.Windows.Forms/src/PublicAPI.Shipped.txt index d3244777ad7..51a60bef024 100644 --- a/src/System.Windows.Forms/src/PublicAPI.Shipped.txt +++ b/src/System.Windows.Forms/src/PublicAPI.Shipped.txt @@ -9983,8 +9983,6 @@ System.Windows.Forms.TextBoxBase.Modified.set -> void System.Windows.Forms.TextBoxBase.ModifiedChanged -> System.EventHandler? System.Windows.Forms.TextBoxBase.MouseClick -> System.Windows.Forms.MouseEventHandler? System.Windows.Forms.TextBoxBase.MultilineChanged -> System.EventHandler? -System.Windows.Forms.TextBoxBase.Padding.get -> System.Windows.Forms.Padding -System.Windows.Forms.TextBoxBase.Padding.set -> void System.Windows.Forms.TextBoxBase.PaddingChanged -> System.EventHandler? System.Windows.Forms.TextBoxBase.Paint -> System.Windows.Forms.PaintEventHandler? System.Windows.Forms.TextBoxBase.Paste() -> void diff --git a/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt index f371d6e76df..ce73a9eae1d 100644 --- a/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/src/PublicAPI.Unshipped.txt @@ -1,5 +1,81 @@ +static System.Windows.Forms.Application.DefaultDarkMode.get -> System.Windows.Forms.DarkMode +static System.Windows.Forms.Control.ControlSystemColors.GetAdaptedDarkModeColorFromKnownColor(System.Drawing.KnownColor knownColor, bool darkMode) -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors +System.Windows.Forms.Control.ControlSystemColors.ActiveBorder.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ActiveCaption.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ActiveCaptionText.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.AppWorkspace.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ButtonFace.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ButtonHighlight.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ButtonShadow.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.Control.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ControlDark.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ControlDarkDark.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ControlLight.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ControlLightLight.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ControlText.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.Desktop.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.GradientActiveCaption.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.GradientInactiveCaption.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.GrayText.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.Highlight.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.HighlightText.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.HotTrack.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.InactiveBorder.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.InactiveCaption.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.InactiveCaptionText.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.Info.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.InfoText.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.Menu.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.MenuBar.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.MenuHighlight.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.MenuText.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.ScrollBar.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.Window.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.WindowFrame.get -> System.Drawing.Color +System.Windows.Forms.Control.ControlSystemColors.WindowText.get -> System.Drawing.Color +System.Windows.Forms.Control.DarkModeChanged -> System.EventHandler? +System.Windows.Forms.Control.SystemColors.get -> System.Windows.Forms.Control.ControlSystemColors! +System.Windows.Forms.Control.VisualStylesModeChanged -> System.EventHandler? System.Windows.Forms.ToolStrip.AllowClickThrough.get -> bool System.Windows.Forms.ToolStrip.AllowClickThrough.set -> void +override System.Windows.Forms.CheckBox.OnPaint(System.Windows.Forms.PaintEventArgs! pevent) -> void +override System.Windows.Forms.ProgressBar.OnCreateControl() -> void +static System.Windows.Forms.Application.DefaultVisualStylesMode.get -> System.Windows.Forms.VisualStylesMode +static System.Windows.Forms.Application.EnvironmentDarkMode.get -> System.Windows.Forms.DarkMode +static System.Windows.Forms.Application.IsDarkModeEnabled.get -> bool +static System.Windows.Forms.Application.SetDefaultDarkMode(System.Windows.Forms.DarkMode darkMode) -> bool +static System.Windows.Forms.Application.SetDefaultVisualStylesMode(System.Windows.Forms.VisualStylesMode styleSetting) -> void +System.Windows.Forms.Appearance.ToggleSwitch = 2 -> System.Windows.Forms.Appearance +System.Windows.Forms.Control.DarkMode.get -> System.Windows.Forms.DarkMode +System.Windows.Forms.Control.DarkMode.set -> void +System.Windows.Forms.Control.DebuggerBreakCounters.get -> System.Collections.Generic.Dictionary! +System.Windows.Forms.Control.DebuggerBreakCounters.set -> void +System.Windows.Forms.Control.VisualStylesMode.get -> System.Windows.Forms.VisualStylesMode +System.Windows.Forms.Control.VisualStylesMode.set -> void +System.Windows.Forms.DarkMode +System.Windows.Forms.DarkMode.Disabled = 3 -> System.Windows.Forms.DarkMode +System.Windows.Forms.DarkMode.Enabled = 2 -> System.Windows.Forms.DarkMode +System.Windows.Forms.DarkMode.Inherits = 1 -> System.Windows.Forms.DarkMode +System.Windows.Forms.DarkMode.NotSupported = 0 -> System.Windows.Forms.DarkMode +System.Windows.Forms.Form.SetWindowBorderColor(System.Drawing.Color color) -> void +System.Windows.Forms.Form.SetWindowCaptionColor(System.Drawing.Color color) -> void +System.Windows.Forms.Form.SetWindowCaptionTextColor(System.Drawing.Color color) -> void +System.Windows.Forms.Form.SetWindowCornerPreference(System.Windows.Forms.Form.WindowCornerPreference cornerPreference) -> void +System.Windows.Forms.Form.WindowCornerPreference +System.Windows.Forms.Form.WindowCornerPreference.Default = 0 -> System.Windows.Forms.Form.WindowCornerPreference +System.Windows.Forms.Form.WindowCornerPreference.DoNotRound = 1 -> System.Windows.Forms.Form.WindowCornerPreference +System.Windows.Forms.Form.WindowCornerPreference.Round = 2 -> System.Windows.Forms.Form.WindowCornerPreference +System.Windows.Forms.Form.WindowCornerPreference.RoundSmall = 3 -> System.Windows.Forms.Form.WindowCornerPreference +System.Windows.Forms.VisualStylesMode +System.Windows.Forms.VisualStylesMode.Classic = 1 -> System.Windows.Forms.VisualStylesMode +System.Windows.Forms.VisualStylesMode.Disabled = 0 -> System.Windows.Forms.VisualStylesMode +System.Windows.Forms.VisualStylesMode.Latest = 2 -> System.Windows.Forms.VisualStylesMode +virtual System.Windows.Forms.Control.DarkModeSupported.get -> bool +virtual System.Windows.Forms.Control.DefaultVisualStylesMode.get -> System.Windows.Forms.VisualStylesMode +virtual System.Windows.Forms.Control.IsDarkModeEnabled.get -> bool +virtual System.Windows.Forms.Control.OnDarkModeChanged(System.EventArgs! e) -> void +virtual System.Windows.Forms.Control.OnDebuggerBreak(string! ticket) -> bool virtual System.Drawing.Design.PropertyValueUIHandler.Invoke(System.ComponentModel.ITypeDescriptorContext! context, System.ComponentModel.PropertyDescriptor! propDesc, System.Collections.ArrayList! valueUIItemList) -> void virtual System.Drawing.Design.PropertyValueUIItemInvokeHandler.Invoke(System.ComponentModel.ITypeDescriptorContext! context, System.ComponentModel.PropertyDescriptor! descriptor, System.Drawing.Design.PropertyValueUIItem! invokedItem) -> void virtual System.Windows.Forms.Application.MessageLoopCallback.Invoke() -> bool @@ -12,6 +88,10 @@ virtual System.Windows.Forms.ColumnReorderedEventHandler.Invoke(object? sender, virtual System.Windows.Forms.ColumnWidthChangedEventHandler.Invoke(object? sender, System.Windows.Forms.ColumnWidthChangedEventArgs! e) -> void virtual System.Windows.Forms.ColumnWidthChangingEventHandler.Invoke(object? sender, System.Windows.Forms.ColumnWidthChangingEventArgs! e) -> void virtual System.Windows.Forms.ContentsResizedEventHandler.Invoke(object? sender, System.Windows.Forms.ContentsResizedEventArgs! e) -> void +virtual System.Windows.Forms.Control.OnParentDarkModeChanged(System.EventArgs! e) -> void +virtual System.Windows.Forms.Control.OnParentVisualStylesModeChanged(System.EventArgs! e) -> void +virtual System.Windows.Forms.Control.OnVisualStylesModeChanged(System.EventArgs! e) -> void +virtual System.Windows.Forms.Control.ValidateVisualStylesMode(System.Windows.Forms.VisualStylesMode visualStylesMode) -> bool virtual System.Windows.Forms.ControlEventHandler.Invoke(object? sender, System.Windows.Forms.ControlEventArgs! e) -> void virtual System.Windows.Forms.ConvertEventHandler.Invoke(object? sender, System.Windows.Forms.ConvertEventArgs! e) -> void virtual System.Windows.Forms.DataGridViewAutoSizeColumnModeEventHandler.Invoke(object? sender, System.Windows.Forms.DataGridViewAutoSizeColumnModeEventArgs! e) -> void @@ -128,4 +208,4 @@ virtual System.Windows.Forms.UpDownEventHandler.Invoke(object? source, System.Wi virtual System.Windows.Forms.WebBrowserDocumentCompletedEventHandler.Invoke(object? sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs! e) -> void virtual System.Windows.Forms.WebBrowserNavigatedEventHandler.Invoke(object? sender, System.Windows.Forms.WebBrowserNavigatedEventArgs! e) -> void virtual System.Windows.Forms.WebBrowserNavigatingEventHandler.Invoke(object? sender, System.Windows.Forms.WebBrowserNavigatingEventArgs! e) -> void -virtual System.Windows.Forms.WebBrowserProgressChangedEventHandler.Invoke(object? sender, System.Windows.Forms.WebBrowserProgressChangedEventArgs! e) -> void \ No newline at end of file +virtual System.Windows.Forms.WebBrowserProgressChangedEventHandler.Invoke(object? sender, System.Windows.Forms.WebBrowserProgressChangedEventArgs! e) -> void diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx index 00ed554ae7f..4b363a775fc 100644 --- a/src/System.Windows.Forms/src/Resources/SR.resx +++ b/src/System.Windows.Forms/src/Resources/SR.resx @@ -6979,4 +6979,13 @@ Stack trace where the illegal operation occurred was: Design time features are not supported. - + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + + The visual style {0} is not supported for the {1} control with the name {2}. + + \ No newline at end of file diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf index e9a0c85c661..8a8da703152 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf @@ -1527,6 +1527,11 @@ Ukazatel myši, který se zobrazí, pokud jej přesunete přes ovládací prvek. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Vyvolá se při změně hodnoty vlastnosti DataContext. @@ -2132,6 +2137,11 @@ Určuje, zda je ovládací prvek viditelný nebo skrytý. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. Šířka ovládacího prvku, v souřadnicích kontejneru. @@ -11139,6 +11149,11 @@ Trasování zásobníku, kde došlo k neplatné operaci: Zadaná kombinace Class, Part a State není definována aktuálním vizuálním stylem. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. Ovládací prvek WebBrowser nepodporuje vlastnost AllowDrop. Pomocí vlastnosti AllowWebBrowserDrop povolte zpracování přetažení myší ovládacím prvkem WebBrowser. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf index 95b47cd70cb..8418aca6b80 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf @@ -1527,6 +1527,11 @@ Der angezeigte Cursor, wenn der Zeiger über das Steuerelement bewegt wird. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Tritt auf, wenn sich der Wert der DataSource-Eigenschaft ändert. @@ -2132,6 +2137,11 @@ Bestimmt, ob das Steuerelement sichtbar oder ausgeblendet ist. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. Die Breite des Steuerelements (in Containerkoordinaten). @@ -11139,6 +11149,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Die gegebene Kombination aus "Class", "Part" und "State" wird vom aktuellen visuellen Stil nicht definiert. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. Das WebBrowser-Steuerelement unterstützt AllowDrop nicht. Verwenden Sie AllowWebBrowserDrop, um dem WebBrowser die Verarbeitung von Drag/Drop zu gestatten. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf index 3b57effaa5d..577f35891aa 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf @@ -1527,6 +1527,11 @@ Cursor que aparece al pasar el puntero por el control. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Se produce cuando cambia el valor de la propiedad DataContext. @@ -2132,6 +2137,11 @@ Determina si el control está visible u oculto. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. Ancho del control, en las coordenadas del contenedor. @@ -11139,6 +11149,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: La combinación dada de Class, Part y State no está definida por el estilo visual actual. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. El control WebBrowser no permite AllowDrop. Utilice AllowWebBrowserDrop para que WebBrowser pueda procesar la operación de arrastrar y colocar. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf index 9907e5ebe3d..1bcd27d359f 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf @@ -1527,6 +1527,11 @@ Le curseur qui s'affiche lorsque le pointeur passe sur le contrôle. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Se produit lorsque la valeur de la propriété DataContext change. @@ -2132,6 +2137,11 @@ Détermine si le contrôle est visible ou masqué. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. La largeur du contrôle, en coordonnées conteneur. @@ -11139,6 +11149,11 @@ Cette opération non conforme s'est produite sur la trace de la pile : La combinaison donnée Classe, Partie et État n'est pas définie par le style visuel actuel. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. Le contrôle WebBrowser ne prend pas en charge AllowDrop. Utilisez AllowWebBrowserDrop pour autoriser WebBrowser à utiliser le glisser-déplacer. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf index fbbed81c46f..0f3e04a22f6 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf @@ -1527,6 +1527,11 @@ Il cursore che viene visualizzato quando il puntatore del mouse viene spostato sul controllo. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Generato quando il valore della proprietà DataSource cambia. @@ -2132,6 +2137,11 @@ Determina se il controllo è visibile o nascosto. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. La larghezza del controllo, nelle coordinate del contenitore. @@ -11139,6 +11149,11 @@ Traccia dello stack da cui si è verificata l'operazione non valida: La combinazione specificata di Class, Part e State non è definita dallo stile di visualizzazione corrente. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. Il controllo WebBrowser non supporta AllowDrop. Utilizzare AllowWebBrowserDrop per consentire il trascinamento al controllo WebBrowser. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf index 403622cfa93..7843c5a317d 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf @@ -1527,6 +1527,11 @@ ポインターがコントロールに移動したときに表示されるカーソルです。 + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. DataContext プロパティの値が変更されたときに発生します。 @@ -2132,6 +2137,11 @@ コントロールの表示、非表示を示します。 + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. コンテナー座標で表したコントロールの幅です。 @@ -11139,6 +11149,11 @@ Stack trace where the illegal operation occurred was: クラス、パート、および状態の指定された組み合わせは、現在の Visual スタイルによって定義されていません。 + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. WebBrowser コントロールは AllowDrop をサポートしていません。 WebBrowser でドラッグ アンド ドロップ操作を可能にするには、AllowWebBrowserDrop を使用してください。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf index c21f7b40809..5727885393f 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf @@ -1527,6 +1527,11 @@ 포인터가 컨트롤 위로 이동할 때 나타나는 커서입니다. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. DataContext 속성의 값이 변경되면 발생합니다. @@ -2132,6 +2137,11 @@ 컨트롤을 표시할지 여부를 결정합니다. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. 컨테이너 좌표로 표시한 컨트롤의 너비입니다. @@ -11139,6 +11149,11 @@ Stack trace where the illegal operation occurred was: 클래스, 파트 및 상태의 지정된 조합이 현재 비주얼 스타일로 정의되지 않았습니다. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. WebBrowser 컨트롤에는 AllowDrop을 사용할 수 없습니다. WebBrowser에서 끌기/놓기 작업을 사용하려면 AllowWebBrowserDrop을 사용하십시오. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf index b3cbd046187..1f5fd2a6e83 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf @@ -1527,6 +1527,11 @@ Wygląd kursora, gdy wskaźnik myszy przesuwa się nad formantem. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Występuje, gdy wartość właściwości DataContext zostanie zmieniona. @@ -2132,6 +2137,11 @@ Określa, czy formant jest widoczny, czy ukryty. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. Szerokość formantu we współrzędnych kontenera. @@ -11139,6 +11149,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Podana kombinacja elementów Class, Part i State nie jest zdefiniowana w aktualnym stylu wizualnym. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. Formant WebBrowser nie obsługuje elementu AllowDrop. Użyj elementu AllowWebBrowserDrop, aby zezwolić elementowi WebBrowser na przetworzenie przeciągania/upuszczania. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf index 3df80b724aa..75aa634fde5 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf @@ -1527,6 +1527,11 @@ O cursor exibido quando o ponteiro se move sobre o controle. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Ocorre quando o valor da propriedade DataContext é alterado. @@ -2132,6 +2137,11 @@ Determina se o controle está visível ou oculto. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. A largura do controle, nas coordenadas do recipiente. @@ -11139,6 +11149,11 @@ Rastreamento de pilha em que a operação ilegal ocorreu: A combinação dada de Class, Part e State não é definida pelo estilo visual atual. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. O controle WebBrowser não oferece suporte para AllowDrop. Use AllowWebBrowserDrop para permitir que WebBrowser processe arrastar/soltar. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf index 793a46d03d2..ff4d40f89a7 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf @@ -1527,6 +1527,11 @@ Курсор, отображаемый при наведении указателя мыши на данный элемент управления. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. Возникает при изменении значения свойства DataContext. @@ -2132,6 +2137,11 @@ Определяет, отображается или скрыт данный элемент управления. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. Ширина элемента управления в координатах контейнера. @@ -11140,6 +11150,11 @@ Stack trace where the illegal operation occurred was: Данная комбинация Class, Part и State не определена текущим стилем отображения. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. Элемент управления "Веб-браузер" не поддерживает AllowDrop. Используйте AllowWebBrowserDrop, чтобы разрешить для веб-браузера обработку перетаскивания. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf index 932a97f83ac..e54fe09e077 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf @@ -1527,6 +1527,11 @@ İşaretçi denetimin üzerine geldiğinde görünen imleç. + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. DataContext özelliğinin değeri değiştiğinde gerçekleşir. @@ -2132,6 +2137,11 @@ Denetimin görünür mü yoksa gizli mi olduğunu belirler. + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. Denetimin genişliği (kapsayıcı koordinatlarında). @@ -11139,6 +11149,11 @@ Geçersiz işlemin gerçekleştiği yığın izi: Verilen Class, Part ve State birleşimi geçerli görsel stil tarafından tanımlanmamış. + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. WebBrowser denetimi AllowDrop'u desteklemiyor. WebBrowser'ın sürükle/bırak işlemine izin vermesi için AllowWebBrowserDrop'u kullanın. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf index adf5ca8b183..af13f08ac4e 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf @@ -1527,6 +1527,11 @@ 指针移过该控件时显示的光标。 + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. 当 DataContext 属性的值发生更改时发生。 @@ -2132,6 +2137,11 @@ 确定该控件是可见的还是隐藏的。 + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. 控件的宽度(以容器坐标表示)。 @@ -11139,6 +11149,11 @@ Stack trace where the illegal operation occurred was: 类、部分和状态的给定组合在当前视觉样式中没有定义。 + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. WebBrowser 控件不支持 AllowDrop。 请使用 AllowWebBrowserDrop 以允许 WebBrowser 处理拖/放操作。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf index 0a8e75a0e01..fc80f973f8f 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf @@ -1527,6 +1527,11 @@ 當指標移到控制項時所顯示的游標。 + + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + Determines the dark mode setting for the control. This ambient property inherits its value from its parent control or from the Application class. + + Occurs when the value of the DataContext property changes. DataCoNtext 屬性的值變更時發生。 @@ -2132,6 +2137,11 @@ 決定控制項是可見或隱藏。 + + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + Determines how the control renders itself with visual styles. This ambient property can inherit its value from its parent control or, if it is a top-level control, from the application's default setting. + + The width of the control, in container coordinates. 容器座標中控制項的寬度。 @@ -11139,6 +11149,11 @@ Stack trace where the illegal operation occurred was: 提供的類別、組件和狀態組合不是由目前的視覺化樣式定義的。 + + The visual style {0} is not supported for the {1} control with the name {2}. + The visual style {0} is not supported for the {1} control with the name {2}. + + The WebBrowser control does not support AllowDrop. Use AllowWebBrowserDrop to allow the WebBrowser to process drag/drop. WebBrowser 控制項不支援 AllowDrop。請使用 AllowWebBrowserDrop 以允許 WebBrowser 處理拖放作業。 diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs index bbcb3a3f87a..04ec1484dfc 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs @@ -37,9 +37,14 @@ public sealed partial class Application private static bool s_comCtlSupportsVisualStylesInitialized; private static bool s_comCtlSupportsVisualStyles; private static FormCollection? s_forms; - private static readonly object s_internalSyncObject = new(); + private static readonly Lock s_internalSyncObject = new(); private static bool s_useWaitCursor; + private static DarkMode? s_darkMode; + + private const string DarkModeKeyPath = "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; + private const string DarkModeKey = "AppsUseLightTheme"; + /// /// Events the user can hook into /// @@ -52,7 +57,6 @@ public sealed partial class Application // Used to avoid recursive exit private static bool s_exiting; - private static bool s_parkingWindowCreated; /// @@ -103,6 +107,7 @@ private static unsafe bool InitializeComCtlSupportsVisualStyles() // GetModuleHandle returns a handle to a mapped module without incrementing its // reference count. var hModule = PInvoke.GetModuleHandle(Libraries.Comctl32); + fixed (byte* ptr = "ImageList_WriteEx\0"u8) { if (!hModule.IsNull) @@ -113,6 +118,7 @@ private static unsafe bool InitializeComCtlSupportsVisualStyles() // Load comctl since GetModuleHandle failed to find it nint ninthModule = PInvoke.LoadComctl32(StartupPath); + if (ninthModule == 0) { return false; @@ -234,13 +240,178 @@ public static InputLanguage CurrentInputLanguage internal static bool CustomThreadExceptionHandlerAttached => ThreadContext.FromCurrent().CustomThreadExceptionHandlerAttached; - internal static Font? DefaultFont => s_defaultFontScaled ?? s_defaultFont; + /// + /// Gets the default dark mode for the application. This is the DarkMode which either has been set + /// by or its default value . + /// + [Experimental("WFO9001")] + public static DarkMode DefaultDarkMode + { + get + { + if (!s_darkMode.HasValue) + { + if (EnvironmentDarkMode is DarkMode.NotSupported) + { + return DarkMode.NotSupported; + } + + return DarkMode.Disabled; + } + + return s_darkMode.Value; + } + } + + /// + /// Sets the default dark mode for the application. + /// + /// The default dark mode to set. + /// True if the default dark mode was set successfully; otherwise, false. + [Experimental("WFO9001")] + public static bool SetDefaultDarkMode(DarkMode darkMode) + { + return darkMode switch + { + DarkMode.Enabled or DarkMode.Disabled or DarkMode.Inherits => SetDefaultDarkModeCore(darkMode), + _ => throw new ArgumentException($"Setting to {darkMode} is not supported in this context.") + }; + + static bool SetDefaultDarkModeCore(DarkMode darkMode) + { + if (EnvironmentDarkMode == DarkMode.NotSupported) + { + s_darkMode = DarkMode.NotSupported; + return false; + } + + s_darkMode = darkMode; + return true; + } + } + + /// + /// Gets the default used as the rendering style guideline for the application's controls. + /// + /// + /// + /// Starting from .NET 9, controls must adapt to new requirements in certain situations, such as dark mode and enhanced accessibility (A11Y) features. + /// These changes can potentially alter the layout and appearance of forms. + /// + /// + /// Therefore, it is necessary to provide mechanisms to finely control backward compatibility for existing and upcoming versions. + /// This includes adjusting control rendering, requesting different sizes for new minimum space requirements, and handling adornments or margins/paddings. + /// This property allows developers to ensure that their applications maintain a consistent appearance and behavior across different .NET versions, + /// particularly when backward compatibility to "XP-based VisualStyles" is essential. + /// + /// + [Experimental("WFO9000")] + public static VisualStylesMode DefaultVisualStylesMode { get; private set; } + + /// + /// Sets the default as the rendering style guideline for the Application's controls to use. + /// Default is when visual style rendering is enabled, + /// otherwise . Setting to has the same effect + /// as not setting using . + /// + /// The version of visual styles to set. + [Experimental("WFO9000")] + public static void SetDefaultVisualStylesMode(VisualStylesMode styleSetting) + { + if (styleSetting != DefaultVisualStylesMode) + { + DefaultVisualStylesMode = styleSetting; + + if (styleSetting == VisualStylesMode.Disabled) + { + UseVisualStyles = false; + return; + } + + EnableVisualStyles(); + } + } + + internal static Font DefaultFont => s_defaultFontScaled ?? s_defaultFont!; + + /// + /// Gets the dark mode setting of the OS system environment. + /// + /// + /// + /// The dark mode setting is determined based on the operating system version and its system settings. It returns + /// if the dark mode is enabled in the system settings, + /// if the dark mode is disabled, and if the dark mode is not supported. + /// + /// + /// Dark mode is supported on Windows 11 or later versions. + /// + /// + /// Dark mode is not supported, if High Contrast mode has been enabled in the system settings. + /// + /// + [Experimental("WFO9001")] + public static DarkMode EnvironmentDarkMode + { + get + { + int systemDarkMode = -1; + + if (SystemInformation.HighContrast) + { + return DarkMode.NotSupported; + } + + // Dark mode is supported when we are >= W11/22000 + // Technically, we could go earlier, but then the APIs we're using weren't officially public. + if (OsVersion.IsWindows11_OrGreater()) + { + try + { + systemDarkMode = (int)(Registry.GetValue( + keyName: DarkModeKeyPath, + valueName: DarkModeKey, + defaultValue: -1) ?? 0); + } + catch + { + } + } + + return systemDarkMode switch + { + 0 => DarkMode.Enabled, + 1 => DarkMode.Disabled, + _ => DarkMode.NotSupported + }; + } + } + + /// + /// Gets a value indicating whether the application is running in a dark mode context. + /// Note: In a high contrast mode, this will always return . + /// + [Experimental("WFO9001")] + public static bool IsDarkModeEnabled => !SystemInformation.HighContrast + && DefaultDarkMode switch + { + DarkMode.Enabled => true, + DarkMode.Disabled => false, + _ => EnvironmentDarkMode switch + { + DarkMode.Enabled => true, + DarkMode.Disabled => false, + + // We return false even if DarkMode is not supported so that we ALWAYS have a valid result here. + _ => false + } + }; /// /// Gets the path for the executable file that started the application. /// - public static string ExecutablePath => - s_executablePath ??= PInvoke.GetModuleFileNameLongPath(HINSTANCE.Null); + public static string ExecutablePath + => s_executablePath ??= PInvoke.GetModuleFileNameLongPath(HINSTANCE.Null); /// /// Gets the current mode for the process. @@ -392,8 +563,11 @@ public static void RegisterMessageLoop(MessageLoopCallback? callback) /// visual styles? If you are doing visual styles rendering, use this to be consistent with the rest /// of the controls in your app. /// - public static bool RenderWithVisualStyles - => ComCtlSupportsVisualStyles && VisualStyleRenderer.IsSupported; +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + public static bool RenderWithVisualStyles => + ComCtlSupportsVisualStyles + && DefaultVisualStylesMode != VisualStylesMode.Disabled; +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. /// /// Gets or sets the format string to apply to top level window captions @@ -772,6 +946,7 @@ public static void EnableVisualStyles() // Extract the manifest from managed resources. using Stream? stream = module.Assembly.GetManifestResourceStream( "System.Windows.Forms.XPThemes.manifest"); + if (stream is not null) { UseVisualStyles = ThemingScope.CreateActivationContext(stream); @@ -780,6 +955,15 @@ public static void EnableVisualStyles() Debug.Assert(UseVisualStyles, "Enable Visual Styles failed"); +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + + // We need to check, if SetDefaultVisualStylesMode was called before EnableVisualStyles, so those will always be in sync! + if (UseVisualStyles && DefaultVisualStylesMode == VisualStylesMode.Disabled) + { + DefaultVisualStylesMode = VisualStylesMode.Classic; + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + s_comCtlSupportsVisualStylesInitialized = false; } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.SystemColors.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.SystemColors.cs new file mode 100644 index 00000000000..2369f03b5e3 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.SystemColors.cs @@ -0,0 +1,356 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Runtime.CompilerServices; + +namespace System.Windows.Forms; + +public unsafe partial class Control +{ + [Experimental("WFO9000")] + public class ControlSystemColors + { + private readonly Color[] _mappedSystemColors; + + private static readonly Color[] s_systemColors = + [ + Drawing.SystemColors.ActiveBorder, + Drawing.SystemColors.ActiveCaption, + Drawing.SystemColors.ActiveCaptionText, + Drawing.SystemColors.AppWorkspace, + Drawing.SystemColors.Control, + Drawing.SystemColors.ControlDark, + Drawing.SystemColors.ControlDarkDark, + Drawing.SystemColors.ControlLight, + Drawing.SystemColors.ControlLightLight, + Drawing.SystemColors.ControlText, + Drawing.SystemColors.Desktop, + Drawing.SystemColors.GrayText, + Drawing.SystemColors.Highlight, + Drawing.SystemColors.HighlightText, + Drawing.SystemColors.HotTrack, + Drawing.SystemColors.InactiveBorder, + Drawing.SystemColors.InactiveCaption, + Drawing.SystemColors.InactiveCaptionText, + Drawing.SystemColors.Info, + Drawing.SystemColors.InfoText, + Drawing.SystemColors.Menu, + Drawing.SystemColors.MenuText, + Drawing.SystemColors.ScrollBar, + Drawing.SystemColors.Window, + Drawing.SystemColors.WindowFrame, + Drawing.SystemColors.WindowText, + Drawing.SystemColors.ButtonFace, + Drawing.SystemColors.ButtonHighlight, + Drawing.SystemColors.ButtonShadow, + Drawing.SystemColors.GradientActiveCaption, + Drawing.SystemColors.GradientInactiveCaption, + Drawing.SystemColors.MenuBar, + Drawing.SystemColors.MenuHighlight + ]; + + private static readonly Color[] s_darkSystemColors = + [ + unchecked(Color.FromArgb((int)0xFF2D2D2D)), // ActiveBorder - Dark gray + unchecked(Color.FromArgb((int)0xFF0078D4)), // ActiveCaption - Highlighted Text Background + unchecked(Color.FromArgb((int)0xFFFFFFFF)), // ActiveCaptionText - White + unchecked(Color.FromArgb((int)0xFF252526)), // AppWorkspace - Panel Background + unchecked(Color.FromArgb((int)0xFF202020)), // Control - Normal Panel/Windows Background + unchecked(Color.FromArgb((int)0xFF4A4A4A)), // ControlDark - A lighter gray for dark mode + unchecked(Color.FromArgb((int)0xFF5A5A5A)), // ControlDarkDark - An even lighter gray for dark mode + unchecked(Color.FromArgb((int)0xFF2E2E2E)), // ControlLight - Unfocused Textbox Background + unchecked(Color.FromArgb((int)0xFF1F1F1F)), // ControlLightLight - Focused Textbox Background + unchecked(Color.FromArgb((int)0xFFE0E0E0)), // ControlText - Control Forecolor and Text Color + unchecked(Color.FromArgb((int)0xFF000000)), // Desktop - Black + unchecked(Color.FromArgb((int)0xFF969696)), // GrayText - Prompt Text Focused TextBox + unchecked(Color.FromArgb((int)0xFF2B2B2B)), // Highlight - Highlighted Panel in DarkMode + unchecked(Color.FromArgb((int)0xFFFFFFFF)), // HighlightText - White + unchecked(Color.FromArgb((int)0xFF4CC2FF)), // HotTrack - Background of the ToggleSwitch + unchecked(Color.FromArgb((int)0xFF2D2D2D)), // InactiveBorder - Dark gray + unchecked(Color.FromArgb((int)0xFF2B2B2B)), // InactiveCaption - Highlighted Panel in DarkMode + unchecked(Color.FromArgb((int)0xFF121212)), // InactiveCaptionText - Middle Dark Panel + unchecked(Color.FromArgb((int)0xFF99EBFF)), // Info - Link Label + unchecked(Color.FromArgb((int)0xFFCFCFCF)), // InfoText - Prompt Text Color + unchecked(Color.FromArgb((int)0xFF2E2E2E)), // Menu - Normal Menu Background + unchecked(Color.FromArgb((int)0xFFFFFFFF)), // MenuText - White + unchecked(Color.FromArgb((int)0xFF9A9A9A)), // ScrollBar - Scrollbars and Scrollbar Arrows + unchecked(Color.FromArgb((int)0xFF202020)), // Window - Window Background + unchecked(Color.FromArgb((int)0xFFFFFFFF)), // WindowFrame - White + unchecked(Color.FromArgb((int)0xFFFFFFFF)), // WindowText - White + unchecked(Color.FromArgb((int)0xFF202020)), // ButtonFace - Same as Window Background + unchecked(Color.FromArgb((int)0xFFFFFFFF)), // ButtonHighlight - White + unchecked(Color.FromArgb((int)0xFF9A9A9A)), // ButtonShadow - Same as Scrollbar Elements + unchecked(Color.FromArgb((int)0xFF0078D4)), // GradientActiveCaption - Same as Highlighted Text Background + unchecked(Color.FromArgb((int)0xFF2B2B2B)), // GradientInactiveCaption - Same as Highlighted Panel in DarkMode + unchecked(Color.FromArgb((int)0xFF2E2E2E)), // MenuBar - Same as Normal Menu Background + unchecked(Color.FromArgb((int)0xFF3D3D3D)) // MenuHighlight - Same as Highlighted Menu Background + ]; + + internal ControlSystemColors(bool isDarkMode = false) + { + var sourceColors = isDarkMode ? s_darkSystemColors : s_systemColors; + _mappedSystemColors = new Color[sourceColors.Length]; + + var sourceSpan = sourceColors.AsSpan(); + var targetSpan = _mappedSystemColors.AsSpan(); + + for (int i = 0; i < sourceSpan.Length; i++) + { + targetSpan[i] = sourceSpan[i]; + } + + if (Debugger.IsAttached) + { + Debugger.Break(); + } + + ActiveBorder = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ActiveBorder)]; + ActiveCaption = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ActiveCaption)]; + ActiveCaptionText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ActiveCaptionText)]; + AppWorkspace = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.AppWorkspace)]; + Control = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.Control)]; + ControlDark = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ControlDark)]; + ControlDarkDark = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ControlDarkDark)]; + ControlLight = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ControlLight)]; + ControlLightLight = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ControlLightLight)]; + ControlText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ControlText)]; + Desktop = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.Desktop)]; + GrayText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.GrayText)]; + Highlight = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.Highlight)]; + HighlightText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.HighlightText)]; + HotTrack = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.HotTrack)]; + InactiveBorder = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.InactiveBorder)]; + InactiveCaption = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.InactiveCaption)]; + InactiveCaptionText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.InactiveCaptionText)]; + Info = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.Info)]; + InfoText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.InfoText)]; + Menu = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.Menu)]; + MenuText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.MenuText)]; + ScrollBar = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ScrollBar)]; + Window = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.Window)]; + WindowFrame = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.WindowFrame)]; + WindowText = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.WindowText)]; + ButtonFace = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ButtonFace)]; + ButtonHighlight = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ButtonHighlight)]; + ButtonShadow = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.ButtonShadow)]; + GradientActiveCaption = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.GradientActiveCaption)]; + GradientInactiveCaption = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.GradientInactiveCaption)]; + MenuBar = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.MenuBar)]; + MenuHighlight = _mappedSystemColors[GetSystemColorLookupIndex(KnownColor.MenuHighlight)]; + } + + /// + /// Returns the default system colors. This reflects the actual current system colors. + /// + internal static ControlSystemColors Default { get; } = new(); + + /// + /// Returns the default system colors for dark mode. This is a hard-coded set of colors intended to use for the WinForms dark mode. + /// + internal static ControlSystemColors DefaultDarkMode { get; } = new(true); + + /// + /// Gets the active border color. + /// + public Color ActiveBorder { get; } + + /// + /// Gets the window color. + /// + public Color Window { get; } + + /// + /// Gets the scroll bar color. + /// + public Color ScrollBar { get; } + + /// + /// Gets the menu text color. + /// + public Color MenuText { get; } + + /// + /// Gets the menu highlight color. + /// + public Color MenuHighlight { get; } + + /// + /// Gets the menu bar color. + /// + public Color MenuBar { get; } + + /// + /// Gets the menu color. + /// + public Color Menu { get; } + + /// + /// Gets the info text color. + /// + public Color InfoText { get; } + + /// + /// Gets the info color. + /// + public Color Info { get; } + + /// + /// Gets the inactive caption text color. + /// + public Color InactiveCaptionText { get; } + + /// + /// Gets the inactive caption color. + /// + public Color InactiveCaption { get; } + + /// + /// Gets the inactive border color. + /// + public Color InactiveBorder { get; } + + /// + /// Gets the hot track color. + /// + public Color HotTrack { get; } + + /// + /// Gets the highlight text color. + /// + public Color HighlightText { get; } + + /// + /// Gets the highlight color. + /// + public Color Highlight { get; } + + /// + /// Gets the window frame color. + /// + public Color WindowFrame { get; } + + /// + /// Gets the window frame color. + /// + public Color WindowText { get; } + + /// + /// Gets the gray text color. + /// + public Color GrayText { get; } + + /// + /// Gets the gradient active caption color. + /// + public Color GradientActiveCaption { get; } + + /// + /// Gets the desktop color. + /// + public Color Desktop { get; } + + /// + /// Gets the control text color. + /// + public Color ControlText { get; } + + /// + /// Gets the control light light color. + /// + public Color ControlLightLight { get; } + + /// + /// Gets the control light color. + /// + public Color ControlLight { get; } + + /// + /// Gets the control dark dark color. + /// + public Color ControlDarkDark { get; } + + /// + /// Gets the control dark color. + /// + public Color ControlDark { get; } + + /// + /// Gets the control color. + /// + public Color Control { get; } + + /// + /// Gets the button shadow color. + /// + public Color ButtonShadow { get; } + + /// + /// Gets the button highlight color. + /// + public Color ButtonHighlight { get; } + + /// + /// Gets the button face color. + /// + public Color ButtonFace { get; } + + /// + /// Gets the application workspace color. + /// + public Color AppWorkspace { get; } + + /// + /// Gets the active caption text color. + /// + public Color ActiveCaptionText { get; } + + /// + /// Gets the active caption color. + /// + public Color ActiveCaption { get; } + + /// + /// Gets the gradient inactive caption color. + /// + public Color GradientInactiveCaption { get; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetSystemColorLookupIndex(KnownColor color) + { + int index; + if (color >= KnownColor.YellowGreen) + { + index = (int)color - (int)KnownColor.YellowGreen + (int)KnownColor.WindowText; + } + else + { + index = (int)color; + } + + index--; + return index; + } + + /// + /// Get the system color index for the specified known color. + /// + /// The known color for the given system color. + /// The 0-based index. + /// If the known color does not reflect a system color. + public static Color GetAdaptedDarkModeColorFromKnownColor(KnownColor knownColor, bool darkMode) + { + int index = GetSystemColorLookupIndex(knownColor); + + if (index < 0 || index >= s_systemColors.Length) + { + throw new ArgumentOutOfRangeException( + nameof(knownColor), + knownColor, + $"{knownColor} is not a System Color."); + } + + return darkMode ? s_darkSystemColors[index] : s_systemColors[index]; + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs index d6354e76d7c..c6fc24717d7 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -14,6 +14,7 @@ using Windows.Win32.System.Ole; using Windows.Win32.UI.Accessibility; using Windows.Win32.UI.Input.KeyboardAndMouse; +using Windows.Win32.Graphics.Dwm; using Com = Windows.Win32.System.Com; using ComTypes = System.Runtime.InteropServices.ComTypes; using Encoding = System.Text.Encoding; @@ -144,7 +145,9 @@ public unsafe partial class Control : private static readonly object s_marginChangedEvent = new(); private protected static readonly object s_paddingChangedEvent = new(); private static readonly object s_previewKeyDownEvent = new(); - private static readonly object s_dataContextEvent = new(); + private static readonly object s_dataContextChangedEvent = new(); + private static readonly object s_darkModeChangedEvent = new(); + private static readonly object s_visualStylesModeChangedEvent = new(); private static MessageId s_threadCallbackMessage; private static ContextCallback? s_invokeMarshaledCallbackHelperDelegate; @@ -157,6 +160,9 @@ public unsafe partial class Control : private static FontHandleWrapper? s_defaultFontHandleWrapper; + internal const string DarkModeIdentifier = "DarkMode"; + internal const string ExplorerThemeIdentifier = "Explorer"; + private const short PaintLayerBackground = 1; private const short PaintLayerForeground = 2; @@ -216,6 +222,8 @@ public unsafe partial class Control : private static readonly int s_ambientPropertiesServiceProperty = PropertyStore.CreateKey(); private static readonly int s_dataContextProperty = PropertyStore.CreateKey(); + private static readonly int s_darkModeProperty = PropertyStore.CreateKey(); + private static readonly int s_visualStylesModeProperty = PropertyStore.CreateKey(); private static bool s_needToLoadComCtl = true; @@ -283,6 +291,12 @@ public unsafe partial class Control : // Inform whether the AnchorsInfo needs to be reevaluated, especially when the control's bounds have been altered explicitly. internal bool _forceAnchorCalculations; + // Cache/lookup for SystemPens used in dark mode. + private static readonly Dictionary s_darkModeSystemPenCache = []; + + // Cache/lookup for SystemBrushes used in dark mode. + private static readonly Dictionary s_darkModeSystemBrushCache = []; + internal byte LayoutSuspendCount { get; private set; } /// @@ -709,14 +723,14 @@ internal HBRUSH BackColorBrush Color color = BackColor; HBRUSH backBrush; - if (color.IsSystemColor) + if (color.IsSystemColor && !IsDarkModeEnabled) { backBrush = PInvoke.GetSysColorBrush(color); SetState(States.OwnCtlBrush, false); } else { - backBrush = PInvoke.CreateSolidBrush((COLORREF)(uint)ColorTranslator.ToWin32(color)); + backBrush = PInvoke.CreateSolidBrush((COLORREF)(uint)ColorTranslator.ToWin32(AdaptForDarkMode(color))); SetState(States.OwnCtlBrush, true); } @@ -1522,7 +1536,7 @@ public virtual Cursor Cursor return cursor; } - // We only do ambients for things with "Cursors.Default" + // We only do ambient for things with "Cursors.Default" // as their default. Cursor localDefault = DefaultCursor; if (localDefault != Cursors.Default) @@ -1609,11 +1623,225 @@ public ControlBindingsCollection DataBindings } } + /// + /// Gets or sets the dark mode for the control. + /// + /// + /// The dark mode for the control. The default value is . This property is ambient. + /// Deriving classes should override to implement their own dark-mode detection logic. + /// + [SRCategory(nameof(SR.CatAppearance))] + [EditorBrowsable(EditorBrowsableState.Always)] + [SRDescription(nameof(SR.ControlDarkModeDescr))] + [Experimental("WFO9001")] + public DarkMode DarkMode + { + get => Properties.TryGetObject(s_darkModeProperty, out DarkMode value) + ? value + : ParentInternal?.DarkMode ?? DarkMode.Inherits; + + set => SetDarkMode(value); + } + + private bool ShouldSerializeDarkMode() + => DarkMode != DefaultDarkMode; + + private void ResetDarkMode() + => DarkMode = DefaultDarkMode; + + /// + /// Tests, if the control is currently in dark mode. This property is ambient. Inherited controls can return false, + /// to prevent their base classes to apply their dark-mode logic, and can still test for dark-mode by calling base.IsDarkModeEnabled. + /// + [Experimental("WFO9001")] + protected virtual bool IsDarkModeEnabled + { + get + { + if (Properties.ContainsObject(s_darkModeProperty)) + { + // If we inherit, it's the parent's dark-mode, otherwise it's the value we have. + return (DarkMode == DarkMode.Inherits + && (ParentInternal?.IsDarkModeEnabled ?? Application.IsDarkModeEnabled)) + || DarkMode == DarkMode.Enabled; + } + else + { + // We're ambient: It's either the parent's or the application's dark-mode. + return ParentInternal?.IsDarkModeEnabled ?? Application.IsDarkModeEnabled; + } + } + } + + [Experimental("WFO9001")] + private void SetDarkMode(DarkMode darkMode) + { + if (ParentInternal is not null && Equals(darkMode, DarkMode)) + { + return; + } + + if (darkMode switch + { + DarkMode.Inherits or + DarkMode.Enabled or + DarkMode.Disabled => Application.EnvironmentDarkMode == DarkMode.NotSupported || !DarkModeSupported + ? throw new ArgumentException($"{darkMode} is not supported in this Environment.") + : true, + _ => throw new ArgumentException($"{darkMode} is not supported in this context.") + }) + { + // When DarkModeSetting was different than its parent before, but now it is about to become the same, + // we're removing it altogether, so it can inherit the value from its parent. + if (Properties.ContainsObject(s_darkModeProperty) && Equals(ParentInternal?.DarkMode, darkMode)) + { + Properties.RemoveObject(s_darkModeProperty); + } + else + { + Properties.SetObject(s_darkModeProperty, darkMode); + } + + OnDarkModeChanged(EventArgs.Empty); + + if (IsHandleCreated) + { + RecreateHandle(); + } + else + { + UpdateStyles(); + } + } + } + + [Experimental("WFO9001")] + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnDarkModeChanged(EventArgs e) + { + if (GetAnyDisposingInHierarchy()) + { + return; + } + + if (Events[s_darkModeChangedEvent] is EventHandler eventHandler) + { + eventHandler(this, e); + } + + ControlCollection? controlsCollection = (ControlCollection?)Properties.GetObject(s_controlsCollectionProperty); + if (controlsCollection is not null) + { + for (int i = 0; i < controlsCollection.Count; i++) + { + controlsCollection[i].OnParentDarkModeChanged(e); + } + } + } + + [Experimental("WFO9001")] + public ControlSystemColors SystemColors => + IsDarkModeEnabled + ? ControlSystemColors.DefaultDarkMode + : ControlSystemColors.Default; + + /// + /// Converts the specific system color to dark mode if dark mode is enabled. + /// + /// + /// The system color translated to dark mode. + internal Color AdaptForDarkMode(Color color) + { + if (!IsDarkModeEnabled || !color.IsSystemColor) + { + return color; + } + + // This throws, if the color is not a system color. + return ControlSystemColors.GetAdaptedDarkModeColorFromKnownColor(color.ToKnownColor(), IsDarkModeEnabled); + } + + /// + /// Converts the specific system pen to its dark mode equivalent if dark mode is enabled, and cashes it. + /// + /// The existing SystemPen to convert. + /// + internal Pen AdaptForDarkMode(Pen pen) + { + if (!IsDarkModeEnabled) + { + return pen; + } + + KnownColor penColor = pen.Color.ToKnownColor(); + + // We need to lookup the Pen, should it exist in the cache, or create a new one and cache it: + if (s_darkModeSystemPenCache.TryGetValue(penColor, out Pen? darkModePen)) + { + return darkModePen; + } + + Pen newPen = new Pen(AdaptForDarkMode(pen.Color), pen.Width) + { + Alignment = pen.Alignment, + CompoundArray = pen.CompoundArray, + DashCap = pen.DashCap, + DashOffset = pen.DashOffset, + DashPattern = pen.DashPattern, + DashStyle = pen.DashStyle, + EndCap = pen.EndCap, + LineJoin = pen.LineJoin, + MiterLimit = pen.MiterLimit, + StartCap = pen.StartCap + }; + + s_darkModeSystemPenCache.Add(penColor, newPen); + + return newPen; + } + + /// + /// Converts the specific system pen to its dark mode equivalent if dark mode is enabled, and cashes it. + /// + /// + /// + internal Brush AdaptForDarkMode(Brush brush) + { + if (brush is not SolidBrush solidBrush || !IsDarkModeEnabled) + { + return brush; + } + + KnownColor brushColor = solidBrush.Color.ToKnownColor(); + + // We need to lookup the Pen, should it exist in the cache, or create a new one and cache it: + if (s_darkModeSystemBrushCache.TryGetValue(brushColor, out SolidBrush? darkModeBrush)) + { + return darkModeBrush; + } + + SolidBrush newBrush = new SolidBrush(AdaptForDarkMode(solidBrush.Color)); + + s_darkModeSystemBrushCache.Add(brushColor, newBrush); + return newBrush; + } + + /// + /// Determines whether the control supports dark mode. + /// + /// , if the control supports dark mode; otherwise, . + [Experimental("WFO9001")] + protected virtual bool DarkModeSupported + => Application.EnvironmentDarkMode != DarkMode.NotSupported; + + [Experimental("WFO9001")] + private static DarkMode DefaultDarkMode => DarkMode.Inherits; + /// /// The default BackColor of a generic top-level Control. Subclasses may have /// different defaults. /// - public static Color DefaultBackColor => SystemColors.Control; + public static Color DefaultBackColor => ControlSystemColors.Default.Control; /// /// Deriving classes can override this to configure a default cursor for their control. @@ -1644,7 +1872,7 @@ public static Font DefaultFont /// The default ForeColor of a generic top-level Control. Subclasses may have /// different defaults. /// - public static Color DefaultForeColor => SystemColors.ControlText; + public static Color DefaultForeColor => ControlSystemColors.Default.ControlText; protected virtual Padding DefaultMargin => CommonProperties.DefaultMargin; @@ -1684,16 +1912,17 @@ internal Color DisabledColor Color color = BackColor; if (color.A != 0) { - return color; + return AdaptForDarkMode(color); } Control? control = ParentInternal; + while (color.A == 0) { if (control is null) { // Don't know what to do, this seems good as anything - color = SystemColors.Control; + color = Drawing.SystemColors.Control; break; } @@ -1701,7 +1930,7 @@ internal Color DisabledColor control = control.ParentInternal; } - return color; + return AdaptForDarkMode(color); } } @@ -2910,7 +3139,8 @@ private bool RenderColorTransparent(Color c) /// /// This property is required by certain controls (TabPage) to render its transparency using theming API. - /// We don't want all controls (that are have transparent BackColor) to use theming API to render its background because it has HUGE PERF cost. + /// We don't want all controls (that are have transparent BackColor) to use + /// theming API to render its background because it has HUGE PERF cost. /// internal virtual bool RenderTransparencyWithVisualStyles => false; @@ -3086,7 +3316,8 @@ public override ISite? Site newAmbients = value.GetService(); } - // If the ambients changed, compare each property. + // If the ambient properties have changed, compare each property. + // TODO (RC1): We need to amend DataContext, DarkMode, VisualStylesMode if (oldAmbients != newAmbients) { bool checkFont = !Properties.ContainsObject(s_fontProperty); @@ -3408,7 +3639,7 @@ protected internal virtual bool ShowKeyboardCues // ProcessUICues will check the current state of the control using WM_QUERYUISTATE // If WM_QUERYUISTATE indicates that the accelerators are hidden we will // either call WM_UPDATEUISTATE or WM_CHANGEUISTATE depending on whether we're hosted or not. - // All controls in the heirarchy will be individually called back on WM_UPDATEUISTATE, which will go into WmUpdateUIState. + // All controls in the hierarchy will be individually called back on WM_UPDATEUISTATE, which will go into WmUpdateUIState. // In WmUpdateUIState, we will update our uiCuesState cached value, which // changes the public value of what we return here for ShowKeyboardCues/ShowFocusCues. @@ -3621,6 +3852,116 @@ public event EventHandler? VisibleChanged remove => Events.RemoveHandler(s_visibleEvent, value); } + /// + /// Gets or sets how the control renders itself with visual styles. This ambient property can inherit its value + /// from its parent control or, if it is a top-level control, from the application's default setting. + /// + /// + /// The for the control. A control's VisualStylesMode default value always derives + /// from its parent control, or if there is no parent, from the Application. The Application's + /// is . + /// + /// + /// + /// Starting from .NET 9, controls are required to adapt to new requirements such as dark mode and enhanced + /// accessibility (A11Y) features. These changes can potentially alter the layout and appearance of forms. + /// Therefore, it is necessary to provide mechanisms to finely control backwards compatibility for existing + /// and upcoming versions. This includes adjusting control rendering, requesting different sizes for new + /// minimum space requirements, and handling adornments or margins/paddings. This property allows developers + /// to ensure that their applications maintain a consistent appearance and behavior across different .NET versions, + /// particularly when backwards compatibility is essential. + /// + /// + /// As an ambient property, if the control is a top-level control and its VisualStylesMode is not explicitly set, + /// it will inherit the setting from . Inherited controls can + /// overwrite to ensure backwards compatibility for controls, which + /// rely on a specific version of the visual styles of a base control (see as an example). + /// + /// + [SRCategory(nameof(SR.CatAppearance))] + [EditorBrowsable(EditorBrowsableState.Always)] + [SRDescription(nameof(SR.ControlVisualStylesModeDescr))] + [Experimental("WFO9000")] + public VisualStylesMode VisualStylesMode + { + get => Properties.TryGetObject(s_visualStylesModeProperty, out VisualStylesMode value) + ? value + : ParentInternal?.VisualStylesMode ?? DefaultVisualStylesMode; + + set + { + if (ParentInternal is not null + && Equals(value, VisualStylesMode)) + { + return; + } + + if (!ValidateVisualStylesMode(value)) + { + throw new NotSupportedException( + string.Format( + format: SR.VisualStylesModeNotSupported, + arg0: value, + arg1: GetType().Name, + arg2: string.IsNullOrWhiteSpace(Name) + ? "- - -" + : Name)); + } + + // When VisualStyleMode was different than its parent before, but now it is about to become the same, + // we're removing it altogether, so it can again inherit the value from its parent. + if (Properties.ContainsObject(s_visualStylesModeProperty) + && Equals(ParentInternal?.VisualStylesMode, value)) + { + Properties.RemoveObject(s_visualStylesModeProperty); + } + else + { + Properties.SetObject(s_visualStylesModeProperty, value); + } + + // We need to re-layout, because changing the VisualStylesMode in many cases + // affects the layout of the control. + using (LayoutTransaction.CreateTransactionIf( + condition: AutoSize, + controlToLayout: ParentInternal, + elementCausingLayout: this, + property: nameof(Padding))) + { + if (IsHandleCreated) + { + // This implies updating the styles. + RecreateHandle(); + + if (GetState(States.LayoutIsDirty)) + { + // The above did not cause our layout to be refreshed. We explicitly refresh our + // layout to ensure that any children are repositioned to account for the change + // in whatever could cause a new size (most likely a new BorderStyle related Padding).. + LayoutTransaction.DoLayout(this, this, nameof(Padding)); + } + } + else + { + UpdateStyles(); + } + } + } + } + + /// + /// Gets the default visual styles mode for the control; standard is . + /// + /// The default visual styles mode for the control. + [Experimental("WFO9000")] + protected virtual VisualStylesMode DefaultVisualStylesMode => Application.DefaultVisualStylesMode; + + private bool ShouldSerializeVisualStylesMode() + => Properties.ContainsObject(s_visualStylesModeProperty); + + private void ResetVisualStylesMode() + => Properties.RemoveObject(s_visualStylesModeProperty); + /// /// Wait for the wait handle to receive a signal: throw an exception if the thread is no longer with us. /// @@ -3809,8 +4150,22 @@ public event ControlEventHandler? ControlRemoved [SRDescription(nameof(SR.ControlDataContextChangedDescr))] public event EventHandler? DataContextChanged { - add => Events.AddHandler(s_dataContextEvent, value); - remove => Events.RemoveHandler(s_dataContextEvent, value); + add => Events.AddHandler(s_dataContextChangedEvent, value); + remove => Events.RemoveHandler(s_dataContextChangedEvent, value); + } + + /// + /// Occurs when the value of the property changes. + /// + [SRCategory(nameof(SR.CatAppearance))] + [Browsable(true)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + // [SRDescription(nameof(SR.ControlDarkModeChangedDescr))] + [SRDescription("Darkmode Description.")] + public event EventHandler? DarkModeChanged + { + add => Events.AddHandler(s_darkModeChangedEvent, value); + remove => Events.RemoveHandler(s_darkModeChangedEvent, value); } [SRCategory(nameof(SR.CatDragDrop))] @@ -4284,6 +4639,20 @@ public event EventHandler? Validated remove => Events.RemoveHandler(s_validatedEvent, value); } + /// + /// Occurs when the value of the property changes. + /// + [SRCategory(nameof(SR.CatAppearance))] + [Browsable(true)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + // [SRDescription(nameof(SR.ControlVisualStylesModeChangedDescr))] + [SRDescription("VisualStyleMode Description.")] + public event EventHandler? VisualStylesModeChanged + { + add => Events.AddHandler(s_visualStylesModeChangedEvent, value); + remove => Events.RemoveHandler(s_visualStylesModeChangedEvent, value); + } + [EditorBrowsable(EditorBrowsableState.Advanced)] protected internal void AccessibilityNotifyClients(AccessibleEvents accEvent, int childID) { @@ -5946,8 +6315,9 @@ internal virtual HBRUSH InitializeDCForWmCtlColor(HDC dc, MessageId msg) // NOTE: this message may not have originally been sent to this HWND. if (!GetStyle(ControlStyles.UserPaint)) { - PInvoke.SetTextColor(dc, (COLORREF)(uint)ColorTranslator.ToWin32(ForeColor)); - PInvoke.SetBkColor(dc, (COLORREF)(uint)ColorTranslator.ToWin32(BackColor)); + PInvoke.SetTextColor(dc, (COLORREF)(uint)ColorTranslator.ToWin32(AdaptForDarkMode(ForeColor))); + PInvoke.SetBkColor(dc, (COLORREF)(uint)ColorTranslator.ToWin32(AdaptForDarkMode(BackColor))); + return BackColorBrush; } @@ -6917,7 +7287,7 @@ protected virtual void OnDataContextChanged(EventArgs e) return; } - if (Events[s_dataContextEvent] is EventHandler eventHandler) + if (Events[s_dataContextChangedEvent] is EventHandler eventHandler) { eventHandler(this, e); } @@ -7162,6 +7532,52 @@ protected virtual void OnParentDataContextChanged(EventArgs e) OnDataContextChanged(e); } + [EditorBrowsable(EditorBrowsableState.Advanced)] + [Experimental("WFO9001")] + protected virtual void OnParentDarkModeChanged(EventArgs e) + { + if (Properties.ContainsObject(s_darkModeProperty)) + { + // If this DataContext was the same as the Parent's just became, + if (Equals(Properties.GetObject(s_darkModeProperty), Parent?.DarkMode)) + { + // we need to make it ambient again by removing it. + Properties.RemoveObject(s_darkModeProperty); + + // Even though internally we don't store it any longer, and the + // value we had stored therefore changed, technically the value + // remains the same, so we don't raise the DataContextChanged event. + return; + } + } + + // In every other case we're going to raise the event. + OnDarkModeChanged(e); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + [Experimental("WFO9000")] + protected virtual void OnParentVisualStylesModeChanged(EventArgs e) + { + if (Properties.ContainsObject(s_visualStylesModeProperty)) + { + // If this DataContext was the same as the Parent's just became, + if (Equals(Properties.GetObject(s_visualStylesModeProperty), Parent?.VisualStylesMode)) + { + // we need to make it ambient again by removing it. + Properties.RemoveObject(s_visualStylesModeProperty); + + // Even though internally we don't store it any longer, and the + // value we had stored therefore changed, technically the value + // remains the same, so we don't raise the DataContextChanged event. + return; + } + } + + // In every other case we're going to raise the event. + OnVisualStylesModeChanged(e); + } + [EditorBrowsable(EditorBrowsableState.Advanced)] protected virtual void OnParentEnabledChanged(EventArgs e) { @@ -7522,6 +7938,21 @@ protected virtual void OnHandleCreated(EventArgs e) PInvoke.SetWindowText(this, _text); } + if (IsDarkModeEnabled) + { + // These controls need to be individually themed. + if (this is not ComboBox + and not GroupBox + and not ListView + and not MonthCalendar) + { + _ = PInvoke.SetWindowTheme( + hwnd: HWND, + pszSubAppName: $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", + pszSubIdList: null); + } + } + if (this is not ScrollableControl && !IsMirrored && GetExtendedState(ExtendedStates.SetScrollPosition) @@ -8270,6 +8701,30 @@ protected virtual void OnValidated(EventArgs e) ((EventHandler?)Events[s_validatedEvent])?.Invoke(this, e); } + [Experimental("WFO9000")] + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnVisualStylesModeChanged(EventArgs e) + { + if (GetAnyDisposingInHierarchy()) + { + return; + } + + if (Events[s_visualStylesModeChangedEvent] is EventHandler eventHandler) + { + eventHandler(this, e); + } + + ControlCollection? controlsCollection = (ControlCollection?)Properties.GetObject(s_controlsCollectionProperty); + if (controlsCollection is not null) + { + for (int i = 0; i < controlsCollection.Count; i++) + { + controlsCollection[i].OnParentVisualStylesModeChanged(e); + } + } + } + /// /// This is called in the constructor before calculating the initial . /// This gives a chance to initialize fields that will be used in calls to sizing related virtuals such as @@ -8311,7 +8766,7 @@ internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backC PaintTransparentBackground(e, rectangle); } - // If the form or mdiclient is mirrored then we do not render the background image due to GDI+ issues. + // If the form or MDIClient is mirrored then we do not render the background image due to GDI+ issues. bool formRTL = ((this is Form || this is MdiClient) && IsMirrored); // The rest of this won't do much if BackColor is transparent and there is no BackgroundImage, @@ -8333,7 +8788,7 @@ internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backC if (imageIsTransparent) { - PaintBackColor(e, rectangle, backColor); + PaintBackColor(e, rectangle, AdaptForDarkMode(backColor)); } ControlPaint.DrawBackgroundImage( @@ -8348,7 +8803,7 @@ internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backC } else { - PaintBackColor(e, rectangle, backColor); + PaintBackColor(e, rectangle, AdaptForDarkMode(backColor)); } } @@ -8428,7 +8883,8 @@ internal unsafe void PaintTransparentBackground(PaintEventArgs e, Rectangle rect // We need to use theming painting for certain controls (like TabPage) when they parent other controls. // But we don't want to to this always as this causes serious performance (at Runtime and DesignTime) // so checking for RenderTransparencyWithVisualStyles which is TRUE for TabPage and false by default. - if (Application.RenderWithVisualStyles && parent.RenderTransparencyWithVisualStyles) + if (Application.DefaultVisualStylesMode != VisualStylesMode.Disabled + && parent.RenderTransparencyWithVisualStyles) { // When we are rendering with visual styles, we can use the cool DrawThemeParentBackground function // that UxTheme provides to render the parent's background. This function is control agnostic, so @@ -10563,6 +11019,17 @@ private protected void SetTopLevelInternal(bool value) } } + private static unsafe void PrepareDarkMode(HWND hwnd, bool darkModeEnabled) + { + BOOL value = darkModeEnabled; + + PInvoke.DwmSetWindowAttribute( + hwnd, + DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, + &value, + (uint)sizeof(BOOL)); + } + protected virtual void SetVisibleCore(bool value) { if (value != Visible) @@ -10580,7 +11047,12 @@ protected virtual void SetVisibleCore(bool value) // bit and call CreateControl() if (IsHandleCreated || value) { - PInvoke.ShowWindow(this, value ? ShowParams : SHOW_WINDOW_CMD.SW_HIDE); + if (value) + { + PrepareDarkMode(HWND, IsDarkModeEnabled); + } + + PInvoke.ShowWindow(HWND, value ? ShowParams : SHOW_WINDOW_CMD.SW_HIDE); } } else if (IsHandleCreated || (value && _parent?.Created == true)) @@ -11273,6 +11745,16 @@ internal virtual void OnBoundsUpdate(int x, int y, int width, int height) { } + [Experimental("WFO9000")] + protected virtual bool ValidateVisualStylesMode(VisualStylesMode visualStylesMode) + => visualStylesMode switch + { + VisualStylesMode.Disabled => true, + VisualStylesMode.Classic => true, + VisualStylesMode.Latest => true, + _ => false, + }; + // These Window* methods allow us to keep access to the "window" // property private, which is important for restricting access to the // handle. @@ -11398,9 +11880,11 @@ private void WmCtlColorControl(ref Message m) { // We could simply reflect the message, but it's faster to handle it here if possible. Control? control = FromHandle(m.LParamInternal); + if (control is not null) { m.ResultInternal = (LRESULT)(nint)control.InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal); + if (m.ResultInternal != 0) { return; @@ -13122,4 +13606,86 @@ internal ToolStripControlHost? ToolStripControlHost internal HWND HWND => (HWND)Handle; internal virtual bool AllowsChildrenToShowToolTips() => true; + + /// + /// DEBUG ONLY: Maintains a dictionary of counters for each ticket, which let's you invoke a Debugger.Break() call + /// when a respective counter reaches 0. + /// + /// + /// Usage example: + /// + /// // Create an instance of the class containing the OnDebuggerBreak method. + /// var yourClassInstance = new YourClass(); + /// + /// // Define breakpoints for specific tickets. + /// yourClassInstance.DebuggerBreakCounters.Add("loadData", 3); + /// yourClassInstance.DebuggerBreakCounters.Add("saveData", -1); + /// + /// // Simulate the process where a breakpoint might be necessary. + /// for(int i = 0; i != 5; i++) + /// { + /// if (yourClassInstance.OnDebuggerBreak("loadData")) + /// { + /// Debugger.Break(); // This will trigger a break on the 3rd call. + /// } + /// } + /// + /// // Directly call to simulate another ticket without loop. + /// if (yourClassInstance.OnDebuggerBreak("saveData")) + /// { + /// Debugger.Break(); // This will always break. + /// } + /// + /// + /// This setup helps in debugging specific parts of the code by allowing conditional + /// breakpoints based on dynamic runtime conditions. + /// + /// + [Browsable(true)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + [DefaultValue(0)] + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription("Debug only property: enter a counter value at which a hard-coded Debugger.Break() call will be invoked.")] + public Dictionary DebuggerBreakCounters { get; set; } = []; + + private readonly Dictionary _currentDebuggerBreakCounters = []; + + protected virtual bool OnDebuggerBreak(string ticket) + { + if (!Debugger.IsAttached + || !DebuggerBreakCounters.TryGetValue(ticket, out int counter)) + { + return false; + } + + if (counter == -1) + { + // We always break! + return true; + } + + if (counter == 0) + { + // We never break! + return false; + } + + if (!_currentDebuggerBreakCounters.TryGetValue(ticket, out int currentCounter)) + { + // let's add the ticket to the current counters, and start counting, if it's not 0. + _currentDebuggerBreakCounters.Add( + ticket, + DebuggerBreakCounters[ticket]); + } + + if (--counter > 0) + { + return false; + } + + // Let's reset the counter, but signal breaking! + _currentDebuggerBreakCounters[ticket] = DebuggerBreakCounters[ticket]; + + return true; + } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/Appearance.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/Appearance.cs index fb144cac774..fba84d282ae 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/Appearance.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/Appearance.cs @@ -17,4 +17,12 @@ public enum Appearance /// The appearance of a Windows button. /// Button = 1, + + /// + /// The appearance of a Modern UI toggle switch. + /// This setting is not taken into account, when is set + /// to or . + /// + [Experimental("WFO9000")] + ToggleSwitch = 2 } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/ButtonBase.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/ButtonBase.cs index 6ceb9b5a715..b93aeccd1b6 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/ButtonBase.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/ButtonBase.cs @@ -50,7 +50,6 @@ public abstract partial class ButtonBase : Control, ICommandBindingTargetProvide // Backing fields for the infrastructure to make ToolStripItem bindable and introduce (bindable) ICommand. private Input.ICommand? _command; private object? _commandParameter; - internal static readonly object s_commandChangedEvent = new(); internal static readonly object s_commandParameterChangedEvent = new(); internal static readonly object s_commandCanExecuteChangedEvent = new(); @@ -1256,7 +1255,7 @@ public bool UseCompatibleTextRendering [SRDescription(nameof(SR.ButtonUseVisualStyleBackColorDescr))] public bool UseVisualStyleBackColor { - get => (_isEnableVisualStyleBackgroundSet || (RawBackColor.IsEmpty && (BackColor == SystemColors.Control))) + get => (_isEnableVisualStyleBackgroundSet || (RawBackColor.IsEmpty && (BackColor == Drawing.SystemColors.Control))) && _enableVisualStyleBackground; set { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/CheckBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/CheckBox.cs index 90ef3c81cb4..3b37ecfbb9a 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/CheckBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Buttons/CheckBox.cs @@ -5,6 +5,7 @@ using System.Drawing; using System.Windows.Forms.ButtonInternal; using System.Windows.Forms.Layout; +using System.Windows.Forms.Rendering.CheckBox; using Windows.Win32.System.Variant; using Windows.Win32.UI.Accessibility; @@ -35,6 +36,7 @@ public partial class CheckBox : ButtonBase // A flag indicating if UIA StateChanged event needs to be triggered, // to avoid double-triggering when Checked value changes. private bool _notifyAccessibilityStateChangedNeeded; + private AnimatedToggleSwitchRenderer? _toggleSwitchRenderer; /// /// Initializes a new instance of the class. @@ -80,6 +82,14 @@ public Appearance Appearance else { UpdateStyles(); + +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + if (value == Appearance.ToggleSwitch + && VisualStylesMode == VisualStylesMode.Latest) + { + Refresh(); + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } OnAppearanceChanged(EventArgs.Empty); @@ -224,17 +234,30 @@ protected override CreateParams CreateParams { CreateParams cp = base.CreateParams; cp.ClassName = PInvoke.WC_BUTTON; + if (OwnerDraw) { cp.Style |= PInvoke.BS_OWNERDRAW; } else { - cp.Style |= PInvoke.BS_3STATE; - if (Appearance == Appearance.Button) +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (VisualStylesMode == VisualStylesMode.Latest) + { + SetStyle(ControlStyles.UserPaint, true); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.ResizeRedraw, true); + cp.Style |= PInvoke.BS_OWNERDRAW; + } + else { - cp.Style |= PInvoke.BS_PUSHLIKE; + cp.Style |= PInvoke.BS_3STATE; + if (Appearance == Appearance.Button) + { + cp.Style |= PInvoke.BS_PUSHLIKE; + } } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. // Determine the alignment of the check box ContentAlignment align = RtlTranslateContent(CheckAlign); @@ -269,6 +292,26 @@ private void ScaleConstants() internal override Size GetPreferredSizeCore(Size proposedConstraints) { + Size textSize; + +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (Appearance == Appearance.ToggleSwitch + && VisualStylesMode == VisualStylesMode.Latest) + { + _toggleSwitchRenderer ??= new AnimatedToggleSwitchRenderer(this, ModernCheckBoxStyle.Rounded); + int dpiScale = (int)(DeviceDpi / 96f); + + textSize = TextRenderer.MeasureText(Text, Font); + int switchWidth = 50 * dpiScale; + int switchHeight = 25 * dpiScale; + + int totalWidth = textSize.Width + switchWidth + 20 * dpiScale; // 10 dpi padding on each side + int totalHeight = Math.Max(textSize.Height, switchHeight); + + return new Size(totalWidth, totalHeight); + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (Appearance == Appearance.Button) { ButtonStandardAdapter adapter = new(this); @@ -280,7 +323,7 @@ internal override Size GetPreferredSizeCore(Size proposedConstraints) return base.GetPreferredSizeCore(proposedConstraints); } - Size textSize = TextRenderer.MeasureText(Text, Font); + textSize = TextRenderer.MeasureText(Text, Font); Size size = SizeFromClientSize(textSize); size.Width += _flatSystemStylePaddingWidth; @@ -289,6 +332,20 @@ internal override Size GetPreferredSizeCore(Size proposedConstraints) return size + Padding.Size; } + protected override void OnPaint(PaintEventArgs pevent) + { +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (VisualStylesMode == VisualStylesMode.Latest + && Appearance == Appearance.ToggleSwitch) + { + _toggleSwitchRenderer?.RenderControl(pevent.Graphics); + return; + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + + base.OnPaint(pevent); + } + internal override Rectangle OverChangeRectangle { get @@ -392,6 +449,16 @@ protected virtual void OnCheckedChanged(EventArgs e) /// protected virtual void OnCheckStateChanged(EventArgs e) { +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (VisualStylesMode == VisualStylesMode.Latest + && Appearance == Appearance.ToggleSwitch) + { + _toggleSwitchRenderer?.RestartAnimation(); + + Refresh(); + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (OwnerDraw) { Refresh(); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.FlatComboAdapter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.FlatComboAdapter.cs index 801cbd52d78..d1aea10bdde 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.FlatComboAdapter.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.FlatComboAdapter.cs @@ -168,19 +168,19 @@ protected virtual void DrawFlatComboDropDown(ComboBox comboBox, Graphics g, Rect } protected virtual Color GetOuterBorderColor(ComboBox comboBox) - => comboBox.Enabled ? SystemColors.Window : SystemColors.ControlDark; + => comboBox.Enabled ? ControlSystemColors.Default.Window : ControlSystemColors.Default.ControlDark; protected virtual Color GetPopupOuterBorderColor(ComboBox comboBox, bool focused) { if (!comboBox.Enabled) { - return SystemColors.ControlDark; + return ControlSystemColors.Default.ControlDark; } - return focused ? SystemColors.ControlDark : SystemColors.Window; + return focused ? ControlSystemColors.Default.ControlDark : ControlSystemColors.Default.Window; } protected virtual Color GetInnerBorderColor(ComboBox comboBox) - => comboBox.Enabled ? comboBox.BackColor : SystemColors.Control; + => comboBox.Enabled ? comboBox.BackColor : ControlSystemColors.Default.Control; } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.cs index bcc8f460e10..3471018803d 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ComboBox/ComboBox.cs @@ -263,7 +263,7 @@ public override Color BackColor } else { - return SystemColors.Window; + return Drawing.SystemColors.Window; } } set => base.BackColor = value; @@ -780,7 +780,7 @@ public int MaxLength } /// - /// If the mouse is over the combobox, draw selection rect. + /// If the mouse is over the ComboBox, draw selection rect. /// internal bool MouseIsOver { @@ -793,11 +793,14 @@ internal bool MouseIsOver // Nothing to see here... Just keep on walking... // Turns out that with Theming off, we don't get quite the same messages as with theming on, so // our drawing gets a little messed up. So in case theming is off, force a draw here. - if ((!ContainsFocus || !Application.RenderWithVisualStyles) && FlatStyle == FlatStyle.Popup) +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + if ((!ContainsFocus || Application.DefaultVisualStylesMode != VisualStylesMode.Disabled) + && FlatStyle == FlatStyle.Popup) { Invalidate(); Update(); } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } } } @@ -2377,7 +2380,7 @@ protected override void CreateHandle() /// Inheriting classes should not forget to call /// base.OnHandleCreated() /// - protected override void OnHandleCreated(EventArgs e) + protected override unsafe void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); @@ -2386,10 +2389,9 @@ protected override void OnHandleCreated(EventArgs e) PInvoke.SendMessage(this, PInvoke.CB_LIMITTEXT, (WPARAM)MaxLength); } - // Get the handles and wndprocs of the ComboBox's child windows - // - Debug.Assert(_childEdit is null, "Child edit window already attached"); - Debug.Assert(_childListBox is null, "Child listbox window already attached"); + // Get the handles and WndProcs of the ComboBox's child windows + Debug.Assert(_childEdit is null, "Child Edit window is already attached."); + Debug.Assert(_childListBox is null, "Child ListBox window is already attached."); bool ok = _childEdit is null && _childListBox is null; @@ -2398,7 +2400,7 @@ protected override void OnHandleCreated(EventArgs e) HWND hwnd = PInvoke.GetWindow(this, GET_WINDOW_CMD.GW_CHILD); if (!hwnd.IsNull) { - // If it's a simple dropdown list, the first HWND is the list box. + // If it's a simple dropdown list, the first HWND is the ListBox. if (DropDownStyle == ComboBoxStyle.Simple) { _childListBox = new ComboBoxChildNativeWindow(this, ChildWindowType.ListBox); @@ -2411,7 +2413,7 @@ protected override void OnHandleCreated(EventArgs e) _childEdit = new ComboBoxChildNativeWindow(this, ChildWindowType.Edit); _childEdit.AssignHandle(hwnd); - // Set the initial margin for combobox to be zero (this is also done whenever the font is changed). + // Set the initial margin for ComboBox to be zero (this is also done whenever the font is changed). PInvoke.SendMessage(_childEdit, PInvoke.EM_SETMARGINS, (WPARAM)(PInvoke.EC_LEFTMARGIN | PInvoke.EC_RIGHTMARGIN)); } } @@ -2429,7 +2431,7 @@ protected override void OnHandleCreated(EventArgs e) UpdateItemHeight(); } - // Resize a simple style combobox on handle creation + // Resize a simple style ComboBox on handle creation // to respect the requested height. // if (DropDownStyle == ComboBoxStyle.Simple) @@ -2438,7 +2440,7 @@ protected override void OnHandleCreated(EventArgs e) } // If HandleCreated set the AutoComplete... - // this function checks if the correct properties are set to enable AutoComplete feature on combobox. + // This function checks if the correct properties are set to enable AutoComplete feature on ComboBox. try { _fromHandleCreate = true; @@ -2449,6 +2451,19 @@ protected override void OnHandleCreated(EventArgs e) _fromHandleCreate = false; } + if (IsDarkModeEnabled) + { + // Style the ComboBox Open-Button: + PInvoke.SetWindowTheme(HWND, "DarkMode_CFD", null); + COMBOBOXINFO cInfo = default; + cInfo.cbSize = (uint)sizeof(COMBOBOXINFO); + + // Style the ComboBox drop-down (including its ScrollBar(s)): + _ = PInvoke.GetComboBoxInfo(HWND, ref cInfo); + + PInvoke.SetWindowTheme(cInfo.hwndList, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); + } + if (_itemsCollection is not null) { foreach (object item in _itemsCollection) @@ -2465,7 +2480,7 @@ protected override void OnHandleCreated(EventArgs e) } } - // NOTE: Setting SelectedIndex must be the last thing we do. + // NOTE: Setting SelectedIndex must be the last thing we do! } /// @@ -2722,7 +2737,7 @@ protected override void OnSelectedIndexChanged(EventArgs e) // don't change the position if SelectedIndex is -1 because this indicates a selection not from the list. if (DataManager is not null && DataManager.Position != SelectedIndex) { - // read this as "if everett or (whidbey and selindex is valid)" + // read this as "if Everett or (Whidbey and selIndex is valid)" if (!FormattingEnabled || SelectedIndex != -1) { DataManager.Position = SelectedIndex; @@ -2756,7 +2771,7 @@ protected virtual void OnDropDownStyleChanged(EventArgs e) /// /// This method is called by the parent control when any property - /// changes on the parent. This can be overriden by inheriting + /// changes on the parent. This can be overridden by inheriting /// classes, however they must call base.OnParentPropertyChanged. /// protected override void OnParentBackColorChanged(EventArgs e) @@ -3572,7 +3587,7 @@ private void WmEraseBkgnd(ref Message m) { PInvokeCore.GetClientRect(this, out RECT rect); HDC hdc = (HDC)m.WParamInternal; - using var hbrush = new CreateBrushScope(ParentInternal?.BackColor ?? SystemColors.Control); + using var hbrush = new CreateBrushScope(AdaptForDarkMode(ParentInternal?.BackColor ?? SystemColors.Control)); hdc.FillRectangle(rect, hbrush); m.ResultInternal = (LRESULT)1; return; @@ -3779,12 +3794,15 @@ protected override unsafe void WndProc(ref Message m) // WM_MOUSELEAVE to ourselves, since that also sets up the right state. Or... at least the state is the same // as with Theming on. - if (!Application.RenderWithVisualStyles && !GetStyle(ControlStyles.UserPaint) +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + if (Application.DefaultVisualStylesMode != VisualStylesMode.Disabled + && !GetStyle(ControlStyles.UserPaint) && DropDownStyle == ComboBoxStyle.DropDownList && (FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup)) { PInvoke.PostMessage(this, PInvoke.WM_MOUSELEAVE); } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } finally { @@ -3851,7 +3869,7 @@ protected override unsafe void WndProc(ref Message m) case PInvoke.WM_PAINT: if (!GetStyle(ControlStyles.UserPaint) && (FlatStyle == FlatStyle.Flat || FlatStyle == FlatStyle.Popup) - && !(SystemInformation.HighContrast && BackColor == SystemColors.Window)) + && !(SystemInformation.HighContrast && BackColor == Drawing.SystemColors.Window)) { using RegionScope dropDownRegion = new(FlatComboBoxAdapter._dropDownRect); using RegionScope windowRegion = new(Bounds); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs index 071be810fa1..1de11907471 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs @@ -5826,7 +5826,7 @@ private void DrawColHeaderShadow(Graphics g, int mouseX) if (ApplyVisualStylesToHeaderCells) { - using var brush = SystemColors.HotTrack.GetCachedSolidBrushScope(); + using var brush = Drawing.SystemColors.HotTrack.GetCachedSolidBrushScope(); g.FillRectangle(brush, rectInsertionBar); } else @@ -17103,7 +17103,7 @@ protected override void OnPaint(PaintEventArgs e) { if (SystemInformation.HighContrast) { - ControlPaint.DrawHighContrastFocusRectangle(g, GetGridFocusRectangle(), SystemColors.ActiveCaptionText); + ControlPaint.DrawHighContrastFocusRectangle(g, GetGridFocusRectangle(), Drawing.SystemColors.ActiveCaptionText); } else { @@ -19744,7 +19744,7 @@ private void PaintBorder(Graphics g, Rectangle clipRect, Rectangle bounds) } else if (BorderStyle == BorderStyle.FixedSingle) { - using var pen = SystemColors.ControlText.GetCachedPenScope(); + using var pen = Drawing.SystemColors.ControlText.GetCachedPenScope(); g.DrawRectangle(pen, new Rectangle(0, 0, bounds.Width - 1, bounds.Height - 1)); } else diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.cs index 1bd46beaa36..1a6d6231c42 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridView.cs @@ -353,8 +353,8 @@ private const DataGridViewAutoSizeRowCriteriaInternal InvalidDataGridViewAutoSiz private Timer? _horizScrollTimer; private readonly Dictionary _converters; - private static readonly Color s_defaultBackColor = SystemColors.Window; - private static readonly Color s_defaultBackgroundColor = SystemColors.ControlDark; + private static readonly Color s_defaultBackColor = Drawing.SystemColors.Window; + private static readonly Color s_defaultBackgroundColor = Drawing.SystemColors.ControlDark; private Color _backgroundColor = s_defaultBackgroundColor; private RECT[]? _cachedScrollableRegion; @@ -2073,7 +2073,7 @@ public event EventHandler? DefaultCellStyleChanged private static SolidBrush DefaultForeBrush => (SolidBrush)SystemBrushes.WindowText; - private static Color DefaultGridColor => SystemColors.WindowFrame; + private static Color DefaultGridColor => Drawing.SystemColors.WindowFrame; private static SolidBrush DefaultHeadersBackBrush => (SolidBrush)SystemBrushes.Control; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs index 62587b284fd..47957e25f44 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DataGridView/DataGridViewLinkCell.cs @@ -406,8 +406,8 @@ private Color HighContrastLinkColor [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - // Selected cells have SystemColors.Highlight as a background. - // SystemColors.HighlightText is supposed to be in contrast with SystemColors.Highlight. + // Selected cells have Application.SystemColors.Highlight as a background. + // Application.SystemColors.HighlightText is supposed to be in contrast with Application.SystemColors.Highlight. return Selected ? SystemColors.HighlightText : SystemColors.HotTrack; } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DateTimePicker/DateTimePicker.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DateTimePicker/DateTimePicker.cs index 61f1acb7e13..804bf03d66e 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DateTimePicker/DateTimePicker.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/DateTimePicker/DateTimePicker.cs @@ -25,22 +25,22 @@ public partial class DateTimePicker : Control /// /// Specifies the default title back color. This field is read-only. /// - protected static readonly Color DefaultTitleBackColor = SystemColors.ActiveCaption; + protected static readonly Color DefaultTitleBackColor = ControlSystemColors.Default.ActiveCaption; /// /// Specifies the default foreground color. This field is read-only. /// - protected static readonly Color DefaultTitleForeColor = SystemColors.ActiveCaptionText; + protected static readonly Color DefaultTitleForeColor = ControlSystemColors.Default.ActiveCaptionText; /// /// Specifies the default month background color. This field is read-only. /// - protected static readonly Color DefaultMonthBackColor = SystemColors.Window; + protected static readonly Color DefaultMonthBackColor = ControlSystemColors.Default.Window; /// /// Specifies the default trailing foreground color. This field is read-only. /// - protected static readonly Color DefaultTrailingForeColor = SystemColors.GrayText; + protected static readonly Color DefaultTrailingForeColor = ControlSystemColors.Default.GrayText; private static readonly object s_formatChangedEvent = new(); @@ -80,7 +80,7 @@ public partial class DateTimePicker : Control private bool _validTime = true; // DateTime changeover: DateTime is a value class, not an object, so we need to keep track - // of whether or not its values have been initialised in a separate boolean. + // of whether or not its values have been initialized in a separate boolean. private bool _userHasSetValue; private DateTime _value = DateTime.Now; private DateTime _creationTime = DateTime.Now; @@ -127,7 +127,7 @@ public DateTimePicker() : base() [EditorBrowsable(EditorBrowsableState.Never)] public override Color BackColor { - get => ShouldSerializeBackColor() ? base.BackColor : SystemColors.Window; + get => ShouldSerializeBackColor() ? base.BackColor : Drawing.SystemColors.Window; set => base.BackColor = value; } @@ -488,7 +488,7 @@ public LeftRightAlignment DropDownAlign [EditorBrowsable(EditorBrowsableState.Never)] public override Color ForeColor { - get => ShouldSerializeForeColor() ? base.ForeColor : SystemColors.WindowText; + get => ShouldSerializeForeColor() ? base.ForeColor : Drawing.SystemColors.WindowText; set => base.ForeColor = value; } @@ -1083,7 +1083,7 @@ protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); - // Raise automation event to annouce the control. + // Raise automation event to announce the control. if (IsAccessibilityObjectCreated) { _expandCollapseState = ExpandCollapseState.ExpandCollapseState_Collapsed; @@ -1133,7 +1133,7 @@ protected virtual void OnValueChanged(EventArgs eventargs) { _onValueChanged?.Invoke(this, eventargs); - // Raise automation event to annouce changed value. + // Raise automation event to announce changed value. if (IsAccessibilityObjectCreated) { // If date is changed so dtp value is changed too. diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/GroupBox/GroupBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/GroupBox/GroupBox.cs index e571f240124..4845c0af7ed 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/GroupBox/GroupBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/GroupBox/GroupBox.cs @@ -397,8 +397,8 @@ protected override void OnPaint(PaintEventArgs e) { // BACKCOMPAT requirement: // - // Why the Height/Width < 10 check? This is because uxtheme doesn't seem to handle those cases similar to - // what we do for the non-themed case, so if someone is using the groupbox as a separator, their app will + // Why the Height/Width < 10 check? This is because Ux-theme doesn't seem to handle those cases similar to + // what we do for the non-themed case, so if someone is using the GroupBox as a separator, their app will // look weird in .NET Framework 2.0. We render the old way in these cases. if (!Application.RenderWithVisualStyles || Width < 10 || Height < 10) @@ -424,15 +424,16 @@ protected override void OnPaint(PaintEventArgs e) // We only pass in the text color if it is explicitly set, else we let the renderer use the color // specified by the theme. This is a temporary workaround till we find a good solution for the // "default theme color" issue. - if (ShouldSerializeForeColor() || !Enabled) + if (ShouldSerializeForeColor() || IsDarkModeEnabled || !Enabled) { - Color textcolor = Enabled ? ForeColor : TextRenderer.DisabledTextColor(BackColor); + Color textColor = AdaptForDarkMode(Enabled ? ForeColor : TextRenderer.DisabledTextColor(BackColor)); + GroupBoxRenderer.DrawGroupBox( e, new Rectangle(0, 0, Width, Height), Text, Font, - textcolor, + textColor, textFlags, gbState); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/Label.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/Label.cs index ab4c404459f..b80d57629e3 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/Label.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/Label.cs @@ -1000,7 +1000,7 @@ private void DrawImageInternal(Graphics g, Image image, Rectangle r, ContentAlig if (!Enabled) { - ControlPaint.DrawImageDisabled(g, image, loc.X, loc.Y, BackColor); + ControlPaint.DrawImageDisabled(g, image, loc.X, loc.Y, AdaptForDarkMode(BackColor)); } else { @@ -1015,7 +1015,7 @@ private Size GetBordersAndPadding() // COMPAT: Everett added random numbers to the height of the label if (UseCompatibleTextRendering) { - // Always return the Fontheight + some buffer else the Text gets clipped for Autosize = true.. + // Always return the FontHeight + some buffer else the Text gets clipped for Autosize = true.. if (BorderStyle != BorderStyle.None) { bordersAndPadding.Height += 6; // taken from Everett.PreferredHeight @@ -1273,7 +1273,7 @@ protected override void OnPaint(PaintEventArgs e) Color color; using (DeviceContextHdcScope hdc = new(e)) { - color = hdc.FindNearestColor(Enabled ? ForeColor : DisabledColor); + color = hdc.FindNearestColor(Enabled ? AdaptForDarkMode(ForeColor) : DisabledColor); } // Do actual drawing @@ -1312,8 +1312,8 @@ protected override void OnPaint(PaintEventArgs e) } else { - // Theme specs -- if the backcolor is darker than Control, we use - // ControlPaint.Dark(backcolor). Otherwise we use ControlDark. + // Theme specs -- if the BackColor is darker than Control, we use + // ControlPaint.Dark(BackColor). Otherwise we use ControlDark. Color disabledTextForeColor = TextRenderer.DisabledTextColor(BackColor); TextRenderer.DrawTextInternal(e, Text, Font, face, disabledTextForeColor, flags: flags); @@ -1324,7 +1324,7 @@ protected override void OnPaint(PaintEventArgs e) } /// - /// Overriden by LinkLabel. + /// Overridden by LinkLabel. /// internal virtual void OnAutoEllipsisChanged() { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/LinkLabel.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/LinkLabel.cs index f87d68bc506..0b86457d909 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/LinkLabel.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/Labels/LinkLabel.cs @@ -235,7 +235,7 @@ public LinkBehavior LinkBehavior public Color LinkColor { get => _linkColor.IsEmpty - ? SystemInformation.HighContrast ? SystemColors.HotTrack : IELinkColor + ? SystemInformation.HighContrast ? Drawing.SystemColors.HotTrack : IELinkColor : _linkColor; set { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/ListBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/ListBox.cs index 45c8743a214..ff6d14e6ca4 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/ListBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/ListBox.cs @@ -156,7 +156,7 @@ public override Color BackColor } else { - return SystemColors.Window; + return Drawing.SystemColors.Window; } } set => base.BackColor = value; @@ -449,7 +449,7 @@ public override Color ForeColor } else { - return SystemColors.WindowText; + return Drawing.SystemColors.WindowText; } } set => base.ForeColor = value; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs index 444d60a1917..b4230ba8327 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListView/ListView.cs @@ -103,8 +103,8 @@ public partial class ListView : Control // Ownerdraw data caches... Only valid inside WM_PAINT. - private Color _odCacheForeColor = SystemColors.WindowText; - private Color _odCacheBackColor = SystemColors.Window; + private Color _odCacheForeColor = ControlSystemColors.Default.WindowText; + private Color _odCacheBackColor = ControlSystemColors.Default.Window; private Font _odCacheFont; private HFONT _odCacheFontHandle; private FontHandleWrapper? _odCacheFontHandleWrapper; @@ -353,7 +353,7 @@ public override Color BackColor } else { - return SystemColors.Window; + return Drawing.SystemColors.Window; } } set @@ -853,7 +853,7 @@ public override Color ForeColor } else { - return SystemColors.WindowText; + return Drawing.SystemColors.WindowText; } } set @@ -2882,7 +2882,7 @@ private unsafe void CustomDraw(ref Message m) } // Work-around for a comctl quirk where, - // if clrText is the same as SystemColors.HotTrack, + // if clrText is the same as Application.SystemColors.HotTrack, // the subitem's color is not changed to nmcd->clrText. // // Try to tweak the blue component of clrText first, then green, then red. @@ -2920,7 +2920,7 @@ private unsafe void CustomDraw(ref Message m) mask >>= 8; // Try the next color. // We try adjusting Blue, Green, Red in that order, // since 0x0000FF is the most likely value of - // SystemColors.HotTrack + // Application.SystemColors.HotTrack totalshift += 8; } } @@ -4613,8 +4613,18 @@ protected override void OnHandleCreated(EventArgs e) PInvoke.SendMessage(this, PInvoke.CCM_SETVERSION, (WPARAM)5); } + if (IsDarkModeEnabled) + { + _ = PInvoke.SetWindowTheme(HWND, "DarkMode_Explorer", null); + + // Get the ListView's ColumnHeader handle: + var columnHeaderHandle = (HWND)PInvoke.SendMessage(this, PInvoke.LVM_GETHEADER, (WPARAM)0, (LPARAM)0); + PInvoke.SetWindowTheme(columnHeaderHandle, "DarkMode_ItemsView", null); + } + UpdateExtendedStyles(); RealizeProperties(); + PInvoke.SendMessage(this, PInvoke.LVM_SETBKCOLOR, (WPARAM)0, (LPARAM)BackColor); PInvoke.SendMessage(this, PInvoke.LVM_SETTEXTCOLOR, (WPARAM)0, (LPARAM)ForeColor); @@ -5008,13 +5018,14 @@ protected void RealizeProperties() Color c; c = BackColor; - if (c != SystemColors.Window) + if (c != Drawing.SystemColors.Window || IsDarkModeEnabled) { PInvoke.SendMessage(this, PInvoke.LVM_SETBKCOLOR, (WPARAM)0, (LPARAM)c); } c = ForeColor; - if (c != SystemColors.WindowText) + + if (c != Drawing.SystemColors.WindowText || IsDarkModeEnabled) { PInvoke.SendMessage(this, PInvoke.LVM_SETTEXTCOLOR, (WPARAM)0, (LPARAM)c); } @@ -5924,7 +5935,7 @@ private void WmNmClick() private void WmNmDblClick() { - // If we're checked, hittest to see if we're + // If we're checked, hit-test to see if we're // on the item if (!CheckBoxes || VirtualMode) @@ -6007,6 +6018,29 @@ private unsafe bool WmNotify(ref Message m) { NMHDR* nmhdr = (NMHDR*)(nint)m.LParamInternal; + // We need to set the text color when we are in dark mode, + // so that the themed headers are actually readable. + if (IsDarkModeEnabled && !OwnerDraw && nmhdr->code == PInvoke.NM_CUSTOMDRAW) + { + NMLVCUSTOMDRAW* nmlvcd = (NMLVCUSTOMDRAW*)(nint)m.LParamInternal; + + if (nmlvcd->nmcd.dwDrawStage == NMCUSTOMDRAW_DRAW_STAGE.CDDS_PREPAINT) + { + // Request item draw notifications. + m.ResultInternal = (LRESULT)(nint)PInvoke.CDRF_NOTIFYITEMDRAW; + return true; // And done. + } + + else if (nmlvcd->nmcd.dwDrawStage == NMCUSTOMDRAW_DRAW_STAGE.CDDS_ITEMPREPAINT) + { + PInvoke.SetTextColor(nmlvcd->nmcd.hdc, ForeColor); + + // ...and then just let the default handling play out. + m.ResultInternal = (LRESULT)(nint)PInvoke.CDRF_DODEFAULT; + return false; + } + } + if (nmhdr->code == PInvoke.NM_CUSTOMDRAW && PInvoke.UiaClientsAreListening()) { // Checking that mouse buttons are not pressed is necessary to avoid diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/MonthCalendar/MonthCalendar.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/MonthCalendar/MonthCalendar.cs index 57e02247556..fd35bf8aa84 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/MonthCalendar/MonthCalendar.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/MonthCalendar/MonthCalendar.cs @@ -59,9 +59,9 @@ namespace System.Windows.Forms; [SRDescription(nameof(SR.DescriptionMonthCalendar))] public partial class MonthCalendar : Control { - private static readonly Color s_defaultTitleBackColor = SystemColors.ActiveCaption; - private static readonly Color s_defaultTitleForeColor = SystemColors.ActiveCaptionText; - private static readonly Color s_trailingForeColor = SystemColors.GrayText; + private static readonly Color s_defaultTitleBackColor = ControlSystemColors.Default.ActiveCaption; + private static readonly Color s_defaultTitleForeColor = ControlSystemColors.Default.ActiveCaptionText; + private static readonly Color s_trailingForeColor = ControlSystemColors.Default.GrayText; private const int MonthsInYear = 12; /// @@ -73,7 +73,7 @@ public partial class MonthCalendar : Control /// /// This is the arbitrary number of pixels that the Win32 control /// inserts between calendars vertically, regardless of font. - /// From comctl32 MonthCalendar sources CALBORDER. + /// From ComCtl32 MonthCalendar sources CALBORDER. /// private const int InsertHeightSize = 6; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ProgressBar/ProgressBar.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ProgressBar/ProgressBar.cs index 747186dc107..cc1294b1b26 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ProgressBar/ProgressBar.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ProgressBar/ProgressBar.cs @@ -25,7 +25,7 @@ public partial class ProgressBar : Control private int _marqueeAnimationSpeed = 100; - private static readonly Color s_defaultForeColor = SystemColors.Highlight; + private static readonly Color s_defaultForeColor = ControlSystemColors.Default.Highlight; private ProgressBarStyle _style = ProgressBarStyle.Blocks; @@ -60,6 +60,7 @@ protected override CreateParams CreateParams { // We want to turn on mirroring for Form explicitly. cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_LAYOUTRTL; + // Don't need these styles when mirroring is turned on. cp.ExStyle &= ~(int)(WINDOW_EX_STYLE.WS_EX_RTLREADING | WINDOW_EX_STYLE.WS_EX_RIGHT | WINDOW_EX_STYLE.WS_EX_LEFTSCROLLBAR); } @@ -68,6 +69,20 @@ protected override CreateParams CreateParams } } + protected override void OnCreateControl() + { + base.OnCreateControl(); + + // If DarkMode is enabled, we need to disable the Visual Styles + // so Windows allows setting Fore- and Background color. + // There are more ideal ways imaginable, but this does the trick for now. + if (IsDarkModeEnabled) + { + // Disables Visual Styles for the ProgressBar. + PInvoke.SetWindowTheme(HWND, " ", " "); + } + } + [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] public override bool AllowDrop diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.SnappableControl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.SnappableControl.cs index 932e84aff75..1cf412fb7c6 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.SnappableControl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.SnappableControl.cs @@ -32,7 +32,7 @@ protected override void OnControlAdded(ControlEventArgs ce) { } - public Color BorderColor { get; set; } = SystemColors.ControlDark; + public Color BorderColor { get; set; } = ControlSystemColors.Default.ControlDark; protected override void OnPaint(PaintEventArgs e) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.cs index e80bee69620..ed24e7568b4 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGrid.cs @@ -70,12 +70,16 @@ public partial class PropertyGrid : ContainerControl, IComPropertyBrowser, IProp private object[]? _selectedObjects; private int _paintFrozen; - private Color _lineColor = SystemInformation.HighContrast ? SystemColors.ControlDarkDark : SystemColors.InactiveBorder; - private Color _categoryForegroundColor = SystemColors.ControlText; - private Color _categorySplitterColor = SystemColors.Control; - private Color _viewBorderColor = SystemColors.ControlDark; - private Color _selectedItemWithFocusForeColor = SystemColors.HighlightText; - private Color _selectedItemWithFocusBackColor = SystemColors.Highlight; + + private Color _lineColor = SystemInformation.HighContrast + ? ControlSystemColors.Default.ControlDarkDark + : ControlSystemColors.Default.InactiveBorder; + + private Color _categoryForegroundColor = ControlSystemColors.Default.ControlText; + private Color _categorySplitterColor = ControlSystemColors.Default.Control; + private Color _viewBorderColor = ControlSystemColors.Default.ControlDark; + private Color _selectedItemWithFocusForeColor = ControlSystemColors.Default.HighlightText; + private Color _selectedItemWithFocusBackColor = ControlSystemColors.Default.Highlight; private bool _canShowVisualStyleGlyphs = true; private AttributeCollection? _browsableAttributes; @@ -211,8 +215,8 @@ public PropertyGrid() _helpPane.TabStop = false; _helpPane.Dock = DockStyle.None; - _helpPane.BackColor = SystemColors.Control; - _helpPane.ForeColor = SystemColors.ControlText; + _helpPane.BackColor = Drawing.SystemColors.Control; + _helpPane.ForeColor = Drawing.SystemColors.ControlText; _helpPane.MouseMove += OnChildMouseMove; _helpPane.MouseDown += OnChildMouseDown; @@ -1617,9 +1621,7 @@ private PropertyGridView CreateGridView(IServiceProvider? serviceProvider) } [Conditional("DEBUG")] -#pragma warning disable CA1822 // Mark members as static internal void CheckInCreate() -#pragma warning restore CA1822 // Mark members as static { #if DEBUG if (_inGridViewCreate) @@ -2537,6 +2539,13 @@ protected override void OnFontChanged(EventArgs e) protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); + + // Making sure, the _toolBar BackColor gets updated when the + // default BackColor is not the typical light-theme one. +#pragma warning disable CA2245 // Do not assign a property to itself + BackColor = BackColor; +#pragma warning restore CA2245 // Do not assign a property to itself + OnLayoutInternal(dividerOnly: false); TypeDescriptor.Refreshed += OnTypeDescriptorRefreshed; if (_selectedObjects is not null && _selectedObjects.Length > 0) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/CategoryGridEntry.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/CategoryGridEntry.cs index 34fc56149cf..dc052f7c477 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/CategoryGridEntry.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/CategoryGridEntry.cs @@ -136,7 +136,7 @@ public override void PaintLabel( Rectangle focusRect = new(indent, rect.Y, labelWidth + 3, rect.Height - 1); if (SystemInformation.HighContrast && !OwnerGrid.HasCustomLineColor) { - // Line color is SystemColors.ControlDarkDark in high contrast mode. + // Line color is Application.SystemColors.ControlDarkDark in high contrast mode. ControlPaint.DrawFocusRectangle(g, focusRect, SystemColors.ControlText, OwnerGrid.LineColor); } else diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.DropDownButtonAdapter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.DropDownButtonAdapter.cs index fa535aa3ff1..3794f1f3139 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.DropDownButtonAdapter.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.DropDownButtonAdapter.cs @@ -15,7 +15,7 @@ internal DropDownButtonAdapter(ButtonBase control) : base(control) { } private void DDB_Draw3DBorder(PaintEventArgs e, Rectangle r, bool raised) { - if (Control.BackColor != SystemColors.Control && SystemInformation.HighContrast) + if (Control.BackColor != ControlSystemColors.Default.Control && SystemInformation.HighContrast) { if (raised) { @@ -72,7 +72,7 @@ internal override void PaintUp(PaintEventArgs pevent, CheckState state) } else { - Color c = (ARGB)SystemColors.Window; + Color c = (ARGB)ControlSystemColors.Default.Window; Rectangle rect = Control.ClientRectangle; rect.Inflate(0, -1); ControlPaint.DrawBorder( @@ -87,7 +87,7 @@ internal override void PaintUp(PaintEventArgs pevent, CheckState state) internal override void DrawImageCore(Graphics graphics, Image image, Rectangle imageBounds, Point imageStart, LayoutData layout) { bool isHighContrastHighlighted = !Control.MouseIsDown && IsHighContrastHighlighted(); - Color backgroundColor = isHighContrastHighlighted ? SystemColors.Highlight : Control.BackColor; + Color backgroundColor = isHighContrastHighlighted ? ControlSystemColors.Default.Highlight : Control.BackColor; if (ControlPaint.IsDark(backgroundColor) && image is Bitmap bitmap) { using Image invertedImage = ControlPaint.CreateBitmapWithInvertedForeColor(bitmap, Control.BackColor); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.cs index 814bd3b5e31..5da6d6c861a 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.cs @@ -84,7 +84,7 @@ protected override void OnPaint(PaintEventArgs pevent) Rectangle dropDownButtonRect = new(0, 0, Width, Height); if (state == ComboBoxState.Normal) { - pevent.Graphics.FillRectangle(SystemBrushes.Window, dropDownButtonRect); + pevent.Graphics.FillRectangle(AdaptForDarkMode(SystemBrushes.Window), dropDownButtonRect); } using (DeviceContextHdcScope hdc = new(pevent)) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/PropertyGridView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/PropertyGridView.cs index 1fc3e2bebca..a0cccd38668 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/PropertyGridView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/PropertyGridView.cs @@ -135,9 +135,9 @@ public PropertyGridView(IServiceProvider? serviceProvider, PropertyGrid property SetStyle(ControlStyles.ResizeRedraw, false); SetStyle(ControlStyles.UserMouse, true); - BackColor = SystemColors.Window; - ForeColor = SystemColors.WindowText; - _grayTextColor = SystemColors.GrayText; + BackColor = Drawing.SystemColors.Window; + ForeColor = Drawing.SystemColors.WindowText; + _grayTextColor = Drawing.SystemColors.GrayText; TabStop = true; Text = "PropertyGridView"; @@ -357,7 +357,7 @@ internal Color GrayTextColor return _grayTextColor; } - if (ForeColor.ToArgb() == SystemColors.WindowText.ToArgb()) + if (AdaptForDarkMode(ForeColor).ToArgb() == SystemColors.WindowText.ToArgb()) { return SystemColors.GrayText; } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs index d02511cad3b..1b100a174cb 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabControl.cs @@ -169,9 +169,9 @@ public override Color BackColor get { // The tab control can only be rendered in 1 color: System's Control color. - // So, always return this value... otherwise, we're inheriting the forms backcolor + // So, always return this value... otherwise, we're inheriting the forms BackColor // and passing it on to the pab pages. - return SystemColors.Control; + return Drawing.SystemColors.Control; } set { @@ -1225,7 +1225,7 @@ protected override void OnHandleCreated(EventArgs e) return; } - // Add the handle to hashtable for Ids .. + // Add the handle to HashTable for Ids .. _windowId = NativeWindow.CreateWindowId(this); _handleInTable = true; @@ -1240,6 +1240,7 @@ protected override void OnHandleCreated(EventArgs e) base.OnHandleCreated(e); _cachedDisplayRect = Rectangle.Empty; ApplyItemSize(); + if (_imageList is not null) { PInvoke.SendMessage(this, PInvoke.TCM_SETIMAGELIST, 0, _imageList.Handle); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabPage.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabPage.cs index 0374f8fd402..bc5a42f5c6b 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabPage.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TabControl/TabPage.cs @@ -101,7 +101,10 @@ public override Color BackColor { return color; } - else if (Application.RenderWithVisualStyles && UseVisualStyleBackColor && (ParentInternal is TabControl parent && parent.Appearance == TabAppearance.Normal)) + else if (!IsDarkModeEnabled + && Application.RenderWithVisualStyles + && UseVisualStyleBackColor + && (ParentInternal is TabControl parent && parent.Appearance == TabAppearance.Normal)) { return Color.Transparent; } @@ -590,15 +593,24 @@ protected override void OnPaintBackground(PaintEventArgs e) // Utilize the UseVisualStyleBackColor property to determine whether or not the themed // background should be utilized. - if (Application.RenderWithVisualStyles && UseVisualStyleBackColor && (ParentInternal is TabControl parent && parent.Appearance == TabAppearance.Normal)) + if (Application.RenderWithVisualStyles + && UseVisualStyleBackColor + && (ParentInternal is TabControl parent && parent.Appearance == TabAppearance.Normal)) { - Color bkcolor = UseVisualStyleBackColor ? Color.Transparent : BackColor; + Color bkColor = (UseVisualStyleBackColor && !IsDarkModeEnabled) + ? Color.Transparent + : BackColor; + Rectangle inflateRect = LayoutUtils.InflateRect(DisplayRectangle, Padding); // To ensure that the TabPage draws correctly (the border will get clipped and - // and gradient fill will match correctly with the tabcontrol). Unfortunately, + // and gradient fill will match correctly with the TabControl). Unfortunately, // there is no good way to determine the padding used on the TabPage. - Rectangle rectWithBorder = new(inflateRect.X - 4, inflateRect.Y - 2, inflateRect.Width + 8, inflateRect.Height + 6); + Rectangle rectWithBorder = new( + inflateRect.X - 4, + inflateRect.Y - 2, + inflateRect.Width + 8, + inflateRect.Height + 6); TabRenderer.DrawTabPage(e, rectWithBorder); @@ -606,7 +618,14 @@ protected override void OnPaintBackground(PaintEventArgs e) // draw it ourselves. if (BackgroundImage is not null) { - ControlPaint.DrawBackgroundImage(e.Graphics, BackgroundImage, bkcolor, BackgroundImageLayout, inflateRect, inflateRect, DisplayRectangle.Location); + ControlPaint.DrawBackgroundImage( + e.Graphics, + BackgroundImage, + bkColor, + BackgroundImageLayout, + inflateRect, + inflateRect, + DisplayRectangle.Location); } } else diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBox.cs index 447a9dfbe77..c6b4826cf6b 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBox.cs @@ -907,7 +907,14 @@ private void DrawPlaceholderText(HDC hdc) } } - TextRenderer.DrawTextInternal(hdc, PlaceholderText, Font, rectangle, SystemColors.GrayText, TextRenderer.DefaultQuality, flags); + TextRenderer.DrawTextInternal( + hdc: hdc, + text: PlaceholderText, + font: Font, + bounds: rectangle, + foreColor: SystemColors.GrayText, + fontQuality: TextRenderer.DefaultQuality, + flags: flags); } protected override unsafe void WndProc(ref Message m) @@ -918,7 +925,9 @@ protected override unsafe void WndProc(ref Message m) case PInvoke.WM_LBUTTONDOWN: MouseButtons realState = MouseButtons; bool wasValidationCancelled = ValidationCancelled; + Focus(); + if (realState == MouseButtons && (!ValidationCancelled || wasValidationCancelled)) { @@ -928,40 +937,7 @@ protected override unsafe void WndProc(ref Message m) break; case PInvoke.WM_PAINT: - { - // The native control tracks its own state, so it is get into a state Where either the native control invalidates - // itself, and thus blastsover the placeholder text - // - or - - // The placeholder text is written multiple times without being cleared first. - // - // To avoid either of the above we need the following operations. - // - // NOTE: there is still an observable flicker with this implementations. We're getting a second WM_PAINT after we - // do our begin/end paint. Something is apparently invalidating the control again. Explicitly calling ValidateRect - // should, in theory, prevent this second call but it clearly isn't happening. - - // Invalidate the whole control to make sure the native control doesn't make any assumptions over what it has to paint - if (ShouldRenderPlaceHolderText()) - { - PInvoke.InvalidateRect(this, lpRect: null, bErase: true); - } - - // Let the native implementation draw the background and animate the frame - base.WndProc(ref m); - - if (ShouldRenderPlaceHolderText()) - { - // Invalidate again because the native WM_PAINT already validated everything by calling BeginPaint itself. - PInvoke.InvalidateRect(this, lpRect: null, bErase: true); - - // Use BeginPaint instead of GetDC to prevent flicker and support print-to-image scenarios. - using BeginPaintScope paintScope = new(HWND); - DrawPlaceholderText(paintScope); - - PInvoke.ValidateRect(this, lpRect: null); - } - } - + WmPaintInternal(ref m); break; case PInvoke.WM_PRINT: @@ -972,6 +948,43 @@ protected override unsafe void WndProc(ref Message m) base.WndProc(ref m); break; } + + // Handles the WM_PAINT message to render the placeholder text in the TextBox control. + unsafe void WmPaintInternal(ref Message m) + { + // The native control tracks its own state, which can lead to two issues: + // + // 1. The native control invalidates itself and overwrites the placeholder text. + // 2. The placeholder text is drawn multiple times without being cleared first. + // + // To avoid these issues, we need the following operations. + // + // NOTE: There is still observable flicker with this implementation. A second WM_PAINT is triggered + // after our BeginPaint/EndPaint calls. Something seems to be invalidating the control again. + // Explicitly calling ValidateRect should, in theory, prevent this second call, but it doesn't seem to work. + + // Invalidate the whole control to ensure the native control doesn't make any assumptions about what to paint. + if (ShouldRenderPlaceHolderText()) + { + PInvoke.InvalidateRect(this, lpRect: null, bErase: true); + } + + // Let the native implementation draw the background and animate the frame. + base.WndProc(ref m); + + if (ShouldRenderPlaceHolderText()) + { + // Invalidate again because the native WM_PAINT has already validated everything by calling BeginPaint itself. + PInvoke.InvalidateRect(this, lpRect: null, bErase: true); + + // Use BeginPaint instead of GetDC to prevent flicker and support print-to-image scenarios. + using BeginPaintScope paintScope = new(HWND); + DrawPlaceholderText(paintScope); + + // Validate the rectangle to prevent further invalidation and flicker. + PInvoke.ValidateRect(this, lpRect: null); + } + } } private bool ShouldRenderPlaceHolderText() => diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBoxBase.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBoxBase.cs index 36b068ac121..eba67c653fa 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBoxBase.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TextBox/TextBoxBase.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Design; +using System.Drawing.Drawing2D; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms.Layout; @@ -47,6 +48,10 @@ public abstract partial class TextBoxBase : Control private static readonly object s_multilineChangedEvent = new(); private static readonly object s_readOnlyChangedEvent = new(); + private const int VisualStylesFixed3DBorderPadding = 3; + private const int VisualStylesFixedSingleBorderPadding = 2; + private const int VisualStylesNoBorderPadding = 1; + /// /// The current border for this edit control. /// @@ -81,6 +86,7 @@ public abstract partial class TextBoxBase : Control // We store all boolean properties in here. private BitVector32 _textBoxFlags; + private bool _triggerNewClientSizeRequest; /// /// Creates a new TextBox control. Uses the parent's current font and color @@ -98,7 +104,7 @@ internal TextBoxBase() : base() | ControlStyles.UseTextForAccessibility | ControlStyles.UserPaint, false); - // cache requestedHeight. Note: Control calls DefaultSize (overridable) in the constructor + // cache requested height. Note: Control calls DefaultSize (overridable) in the constructor // to set the control's cached height that is returned when calling Height, so we just // need to get the cached height here. _requestedHeight = Height; @@ -242,10 +248,8 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) [EditorBrowsable(EditorBrowsableState.Never)] public override bool AutoSize { - get - { - return _textBoxFlags[s_autoSize]; - } + get => _textBoxFlags[s_autoSize]; + set { // Note that we intentionally do not call base. TextBoxes size themselves by @@ -285,11 +289,11 @@ public override Color BackColor } else if (ReadOnly) { - return SystemColors.Control; + return Drawing.SystemColors.Control; } else { - return SystemColors.Window; + return Drawing.SystemColors.Window; } } set => base.BackColor = value; @@ -385,7 +389,9 @@ public event EventHandler? BorderStyleChanged [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [SRDescription(nameof(SR.TextBoxCanUndoDescr))] - public bool CanUndo => IsHandleCreated && (int)PInvoke.SendMessage(this, PInvoke.EM_CANUNDO) != 0; + public bool CanUndo => + IsHandleCreated + && (int)PInvoke.SendMessage(this, PInvoke.EM_CANUNDO) != 0; /// /// Returns the parameters needed to create the handle. Inheriting classes @@ -400,6 +406,7 @@ protected override CreateParams CreateParams CreateParams cp = base.CreateParams; cp.ClassName = PInvoke.WC_EDIT; cp.Style |= PInvoke.ES_AUTOHSCROLL | PInvoke.ES_AUTOVSCROLL; + if (!_textBoxFlags[s_hideSelection]) { cp.Style |= PInvoke.ES_NOHIDESEL; @@ -413,24 +420,44 @@ protected override CreateParams CreateParams cp.Style &= ~(int)WINDOW_STYLE.WS_BORDER; cp.ExStyle &= ~(int)WINDOW_EX_STYLE.WS_EX_CLIENTEDGE; - switch (_borderStyle) +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (VisualStylesMode == VisualStylesMode.Latest) { - case BorderStyle.Fixed3D: - cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_CLIENTEDGE; - break; - case BorderStyle.FixedSingle: - cp.Style |= (int)WINDOW_STYLE.WS_BORDER; - break; - } + // We draw the borders ourselves for the visual styles for .NET 9/10 onwards. + if (_textBoxFlags[s_multiline]) + { + cp.Style |= PInvoke.ES_MULTILINE; - if (_textBoxFlags[s_multiline]) + if (_textBoxFlags[s_wordWrap]) + { + cp.Style &= ~PInvoke.ES_AUTOHSCROLL; + } + } + } + else { - cp.Style |= PInvoke.ES_MULTILINE; - if (_textBoxFlags[s_wordWrap]) + switch (_borderStyle) + { + case BorderStyle.Fixed3D: + cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_CLIENTEDGE; + break; + + case BorderStyle.FixedSingle: + cp.Style |= (int)WINDOW_STYLE.WS_BORDER; + break; + } + + if (_textBoxFlags[s_multiline]) { - cp.Style &= ~PInvoke.ES_AUTOHSCROLL; + cp.Style |= PInvoke.ES_MULTILINE; + + if (_textBoxFlags[s_wordWrap]) + { + cp.Style &= ~PInvoke.ES_AUTOHSCROLL; + } } } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. return cp; } @@ -476,12 +503,7 @@ protected override Cursor DefaultCursor /// This is more efficient than setting the size in the control's constructor. /// protected override Size DefaultSize - { - get - { - return new Size(100, PreferredHeight); - } - } + => new Size(100, PreferredHeight); /// /// Gets or sets the foreground color of the control. @@ -750,15 +772,6 @@ public event EventHandler? MultilineChanged remove => Events.RemoveHandler(s_multilineChangedEvent, value); } - [Browsable(false)] - [EditorBrowsable(EditorBrowsableState.Never)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public new Padding Padding - { - get => base.Padding; - set => base.Padding = value; - } - [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] @@ -786,14 +799,45 @@ public event EventHandler? MultilineChanged [EditorBrowsable(EditorBrowsableState.Advanced)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [SRDescription(nameof(SR.TextBoxPreferredHeightDescr))] - public int PreferredHeight +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + public int PreferredHeight => + VisualStylesMode switch + { + VisualStylesMode.Disabled => PreferredHeightClassic, + VisualStylesMode.Classic => PreferredHeightClassic, + VisualStylesMode.Latest => PreferredHeightCore, + + // We'll should never be here. + _ => throw new InvalidEnumArgumentException( + argumentName: nameof(VisualStylesMode), + invalidValue: (int)VisualStylesMode, + enumClass: typeof(VisualStylesMode)) + }; + + [Experimental("WFO9000")] + protected virtual int PreferredHeightCore + { + get + { + // For modern Visual Styles we take the Padding and the adorner padding into account when calculating the preferred height. + int height = FontHeight + + Padding.Vertical + + GetVisualStylesSystemPadding().Vertical; + + return height; + } + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + + private int PreferredHeightClassic { get { // COMPAT we must return the same busted height we did in Everett, even - // if it doesnt take multiline and word wrap into account. For better accuracy and/or wrapping use - // GetPreferredSize instead. + // if it does not take multiline and word wrap into account. + // For better accuracy and/or wrapping use GetPreferredSize instead. int height = FontHeight; + if (_borderStyle != BorderStyle.None) { height += SystemInformation.GetBorderSizeForDpi(_deviceDpi).Height * 4 + 3; @@ -803,37 +847,57 @@ public int PreferredHeight } } - // GetPreferredSizeCore - // This method can return a different value than PreferredHeight! It properly handles - // border style + multiline and wordwrap. - - internal override Size GetPreferredSizeCore(Size proposedConstraints) + /// + /// Defines the visible part of the padding. + /// This is calculated and not adjustable by properties. + /// The visible part of the padding is the part by what we extend the real-estate + /// of the control with its back color. The invisible part of the Padding is the + /// part, by which we extend the real-estate of the control with the back color of the parent control. + /// + /// The visible padding dimensions. + protected virtual Padding GetVisualStylesSystemPadding() { - // 3px vertical space is required between the text and the border to keep the last - // line from being clipped. - // This 3 pixel size was added in everett and we do this to maintain compat. - // old everett behavior was FontHeight + [SystemInformation.BorderSize.Height * 4 + 3] - // however the [ ] was only added if borderstyle was not none. - Size bordersAndPadding = SizeFromClientSize(Size.Empty) + Padding.Size; + int offset = GetBorderThicknessDpiFactor(); - if (BorderStyle != BorderStyle.None) + return BorderStyle switch { - bordersAndPadding += new Size(0, 3); - } + BorderStyle.Fixed3D => new Padding( + left: VisualStylesFixed3DBorderPadding + offset, + top: VisualStylesFixed3DBorderPadding + offset, + right: VisualStylesFixed3DBorderPadding + offset, + bottom: VisualStylesFixed3DBorderPadding + offset), - if (BorderStyle == BorderStyle.FixedSingle) - { - // Bump these by 2px to match BorderStyle.Fixed3D - they'll be omitted from the SizeFromClientSize call. - bordersAndPadding.Width += 2; - bordersAndPadding.Height += 2; - } + BorderStyle.FixedSingle => new Padding( + left: VisualStylesFixedSingleBorderPadding + offset, + top: VisualStylesFixedSingleBorderPadding + offset, + right: VisualStylesFixedSingleBorderPadding + offset, + bottom: VisualStylesFixedSingleBorderPadding + offset), + + BorderStyle.None => new Padding( + left: VisualStylesNoBorderPadding, + top: VisualStylesNoBorderPadding, + right: VisualStylesNoBorderPadding + offset, + // We still need some extra space for the Focus indication. + bottom: VisualStylesNoBorderPadding + offset), - // Reduce constraints by border/padding size - proposedConstraints -= bordersAndPadding; + _ => Padding.Empty, + }; + } - // Fit the text to the remaining space. - // Fixed for .NET Framework 4.0 + /// + /// Calculates the preferred size of the text box for multi-line text scenarios. + /// Note that this calculation can be returning different values compared to . + /// + /// The already existing proposed constraints. + /// The preferred size. + internal override Size GetPreferredSizeCore(Size proposedConstraints) + { + Padding padding = default; + + // Fit the text to whatever remaining space there is. + // (Fixed for .NET Framework 4.0) TextFormatFlags format = TextFormatFlags.NoPrefix; + if (!Multiline) { format |= TextFormatFlags.SingleLine; @@ -843,11 +907,60 @@ internal override Size GetPreferredSizeCore(Size proposedConstraints) format |= TextFormatFlags.WordBreak; } +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + if (VisualStylesMode == VisualStylesMode.Latest) + { + // For Versions >=10, we take our modern Style adorners into account + // when we're calculating the preferred height. + padding = GetVisualStylesSystemPadding(); + + // And also we take the actual Padding property into account + // when calculating the preferred height. + padding = Padding.Add(padding, Padding); + proposedConstraints -= padding.Size; + } + else + { + // We need this only for measuring the text. + Size bordersAndPadding = SizeFromClientSize(Size.Empty) + Padding.Size; + + if (BorderStyle != BorderStyle.None) + { + // 3px vertical space is required between the text and the border to keep the last + // line from being clipped. + // This 3 pixel size was added in Everett and we do this to maintain compat. + // old Everett behavior was FontHeight + [SystemInformation.BorderSize.Height * 4 + 3] + // however the [ ] was only added if BorderStyle was not none. + bordersAndPadding += new Size(0, 3); + } + + if (BorderStyle == BorderStyle.FixedSingle) + { + // Bump these by 2px to match BorderStyle.Fixed3D - they'll be omitted from the SizeFromClientSize call. + // TODO: I am not sure, if this calculation still fits since we got the underlined border style. + bordersAndPadding.Width += 2; + bordersAndPadding.Height += 2; + } + + // We used to add the borders and padding to the returned size - this remains effectively the same: + padding.Left = 0; + padding.Top = 0; + padding.Right = bordersAndPadding.Width; + padding.Bottom = bordersAndPadding.Height; + + // Reduce constraints by border/padding size + proposedConstraints -= bordersAndPadding; + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. + Size textSize = TextRenderer.MeasureText(Text, Font, proposedConstraints, format); // We use this old computation as a lower bound to ensure backwards compatibility. textSize.Height = Math.Max(textSize.Height, FontHeight); - Size preferredSize = textSize + bordersAndPadding; + + Size preferredSize = textSize + + new Size(padding.Horizontal, padding.Vertical); + return preferredSize; } @@ -1140,8 +1253,15 @@ internal void ForceWindowText(string? value) } /// - /// Gets or sets a value indicating whether a multiline text box control automatically wraps words to the - /// beginning of the next line when necessary. + /// Defines as default for this control, so we're not breaking existing implementations. + /// + [Experimental("WFO9000")] + protected override VisualStylesMode DefaultVisualStylesMode => + VisualStylesMode.Classic; + + /// + /// Gets or sets a value indicating whether a multiline text box control + /// automatically wraps words to the beginning of the next line when necessary. /// [SRCategory(nameof(SR.CatBehavior))] [Localizable(true)] @@ -1180,6 +1300,7 @@ private void AdjustHeight(bool returnIfAnchored) } int saveHeight = _requestedHeight; + try { if (_textBoxFlags[s_autoSize] && !_textBoxFlags[s_multiline]) @@ -1283,6 +1404,11 @@ public void ClearUndo() protected override void CreateHandle() { + // Should the Handle be created at this point, + // we need to reset the flag to ensure that the + // new client size is requested. + _triggerNewClientSizeRequest = false; + // This "creatingHandle" stuff is to avoid property change events // when we set the Text property. _textBoxFlags[s_creatingHandle] = true; @@ -1411,7 +1537,7 @@ protected override bool ProcessDialogKey(Keys keyData) } /// - /// TextBox / RichTextBox Onpaint. + /// TextBox / RichTextBox OnPaint. /// /// [Browsable(false)] @@ -1438,6 +1564,30 @@ protected virtual void OnBorderStyleChanged(EventArgs e) } } + protected override unsafe void OnGotFocus(EventArgs e) + { + // We need to invalidate for NcPaint, so we draw focused adorners. + PInvoke.RedrawWindow( + hWnd: this, + lprcUpdate: null, + hrgnUpdate: HRGN.Null, + flags: REDRAW_WINDOW_FLAGS.RDW_FRAME | REDRAW_WINDOW_FLAGS.RDW_INVALIDATE); + + base.OnGotFocus(e); + } + + protected override unsafe void OnLostFocus(EventArgs e) + { + // We need to invalidate for NcPaint, so we draw un-focused adorners. + PInvoke.RedrawWindow( + hWnd: this, + lprcUpdate: null, + hrgnUpdate: HRGN.Null, + flags: REDRAW_WINDOW_FLAGS.RDW_FRAME | REDRAW_WINDOW_FLAGS.RDW_INVALIDATE); + + base.OnLostFocus(e); + } + protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); @@ -1514,7 +1664,7 @@ protected virtual void OnReadOnlyChanged(EventArgs e) protected override void OnTextChanged(EventArgs e) { // since AutoSize existed in Everett, (and is the default) we can't - // relayout the parent when the "PreferredSize" of the control changes. + // re-layout the parent when the "PreferredSize" of the control changes. // this means a multiline = true textbox won't naturally grow in height when // the text changes. CommonProperties.xClearPreferredSizeCache(this); @@ -1979,6 +2129,8 @@ internal virtual void UpdateMaxLength() internal override HBRUSH InitializeDCForWmCtlColor(HDC dc, MessageId msg) { + InitializeClientArea(dc, (HWND)Handle); + if (msg == PInvoke.WM_CTLCOLORSTATIC && !ShouldSerializeBackColor()) { // Let the Win32 Edit control handle background colors itself. @@ -1992,16 +2144,248 @@ internal override HBRUSH InitializeDCForWmCtlColor(HDC dc, MessageId msg) } } +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + private bool WmNcPaint(ref Message m) + { + if (VisualStylesMode < VisualStylesMode.Latest) + { + return false; + } + + HWND hwnd = (HWND)m.HWnd; + HDC hdc = PInvokeCore.GetWindowDC((HWND)m.HWnd); + + // Get the Graphics Object from the DC: + + Graphics graphics = Graphics.FromHdc(hdc); + + try + { + OnNcPaint(graphics); + } + finally + { + graphics.Dispose(); + int result = PInvokeCore.ReleaseDC(hwnd, hdc); + Debug.Assert(result != 0); + } + + return true; + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + private int GetBorderThicknessDpiFactor() + => _deviceDpi switch + { + <= 96 => 1, + <= 120 => 2, + <= 192 => 3, + _ => 4, + }; + + [Experimental("WFO9000")] + private protected virtual void OnNcPaint(Graphics graphics) + { + const int cornerRadius = 15; + + Padding systemPadding = GetVisualStylesSystemPadding(); + int borderThickness = GetBorderThicknessDpiFactor(); + + Color adornerColor = AdaptForDarkMode(ForeColor); + Color parentBackColor = AdaptForDarkMode(Parent?.BackColor ?? BackColor); + Color clientBackColor = AdaptForDarkMode(BackColor); + + using Brush parentBackgroundBrush = new SolidBrush(parentBackColor); + using Brush clientBackgroundBrush = new SolidBrush(clientBackColor); + using Brush adornerBrush = new SolidBrush(adornerColor); + using Pen adornerPen = new(adornerColor, borderThickness); + using Pen focusPen = new(SystemColors.Highlight, borderThickness); + + Rectangle bounds = new Rectangle( + x: 0, + y: 0, + width: Bounds.Width, + height: Bounds.Height); + + Padding clientPadding = ClientPadding; + + // This is the client area without the padding: + Rectangle clientBounds = new( + clientPadding.Left, + clientPadding.Top, + bounds.Width - clientPadding.Horizontal, + bounds.Height - clientPadding.Vertical); + + // This is the client area of the actual original edit control. + Rectangle deflatedBounds = Add(bounds, Padding); + + // Making sure we never color outside the lines: + deflatedBounds.Width -= 1; + deflatedBounds.Height -= 1; + + // We need Anti-Aliasing: + graphics.SmoothingMode = SmoothingMode.AntiAlias; + + graphics.Clip = new Region(bounds); + graphics.ExcludeClip(clientBounds); + + bounds.Inflate(1, 1); + + // Fill the background with the specified brush: + graphics.FillRectangle(parentBackgroundBrush, bounds); + + switch (BorderStyle) + { + case BorderStyle.None: + + // Draw a rounded Rectangle with the border thickness + graphics.FillRectangle( + clientBackgroundBrush, + deflatedBounds); + + break; + + case BorderStyle.FixedSingle: + + // Draw a rounded Rectangle with the border thickness + graphics.FillRectangle( + clientBackgroundBrush, + deflatedBounds); + + // Draw a rounded Rectangle with the border thickness + graphics.DrawRectangle( + adornerPen, + deflatedBounds); + + break; + + case BorderStyle.Fixed3D: + + // fill a rounded Rectangle + graphics.FillRoundedRectangle( + clientBackgroundBrush, + deflatedBounds, + new Size(cornerRadius, cornerRadius)); + + // Draw a rounded Rectangle with the border thickness + graphics.DrawRoundedRectangle( + adornerPen, + deflatedBounds, + new Size(cornerRadius, cornerRadius)); + + break; + } + + // Draw the focus just as one line over the bottom border: + if (Focused) + { + int left = 0, right = 0; + + switch (BorderStyle) + { + case BorderStyle.None: + case BorderStyle.FixedSingle: + left = deflatedBounds.Left; + right = deflatedBounds.Right; + break; + + case BorderStyle.Fixed3D: + // We must shorten the line on both side to not draw into the curve: + left = deflatedBounds.Left + (cornerRadius + 1) / 2; + right = deflatedBounds.Right - (cornerRadius + 1) / 2; + break; + } + + graphics.DrawLine( + pen: focusPen, + x1: left, + y1: deflatedBounds.Bottom, + x2: right, + y2: deflatedBounds.Bottom); + } + + static Rectangle Add(Rectangle r, Padding p) => new( + r.X + p.Left, + r.Y + p.Top, + r.Width - p.Horizontal, + r.Height - p.Vertical); + } + + private protected virtual unsafe void InitializeClientArea(HDC hDC, HWND hwnd) + { + // We only want to do this for VisualStylesMode >= Version10. + // Also, we do that only one time per instance, + // but we need to reset this, when the handle is recreated. +#pragma warning disable WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + if (VisualStylesMode < VisualStylesMode.Latest + || _triggerNewClientSizeRequest) + { + return; + } +#pragma warning restore WFO9000 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + _triggerNewClientSizeRequest = true; + + // Get the window bounds + Rectangle bounds = Bounds; + + // Call SetWindowPos with the current Bounds with SWP_FRAMECHANGED flag. + // Only then we do not change the Window pos/size, but we'll get that WmNcCalcSize message version + // that we need to adjust the client area. + PInvoke.SetWindowPos( + hWnd: this, + hWndInsertAfter: HWND.HWND_TOP, + X: 0, + Y: 0, + cx: 0, + cy: 0, + uFlags: SET_WINDOW_POS_FLAGS.SWP_FRAMECHANGED + | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE + | SET_WINDOW_POS_FLAGS.SWP_NOMOVE + | SET_WINDOW_POS_FLAGS.SWP_NOSIZE + | SET_WINDOW_POS_FLAGS.SWP_NOZORDER); + + Invalidate(true); + } + + private unsafe void WmNcCalcSize(ref Message m) + { + // Make sure _we_ actually kicked this off. + if (_triggerNewClientSizeRequest) + { + bool wParam = m.WParamInternal != 0; + + NCCALCSIZE_PARAMS* ncCalcSizeParams = (NCCALCSIZE_PARAMS*)(void*)m.LParamInternal; + + if (ncCalcSizeParams is not null) + { + Padding padding = ClientPadding; + + ncCalcSizeParams->rgrc._0.top += padding.Top; + ncCalcSizeParams->rgrc._0.bottom -= padding.Bottom; + ncCalcSizeParams->rgrc._0.left += padding.Left; + ncCalcSizeParams->rgrc._0.right -= padding.Right; + + m.ResultInternal = (LRESULT)0; + return; + } + } + + base.WndProc(ref m); + } + + private Padding ClientPadding => Padding + GetVisualStylesSystemPadding(); + private void WmReflectCommand(ref Message m) { if (!_textBoxFlags[s_codeUpdateText] && !_textBoxFlags[s_creatingHandle]) { - uint hiword = m.WParamInternal.HIWORD; - if (hiword == PInvoke.EN_CHANGE && CanRaiseTextChangedEvent) + uint hiWord = m.WParamInternal.HIWORD; + if (hiWord == PInvoke.EN_CHANGE && CanRaiseTextChangedEvent) { OnTextChanged(EventArgs.Empty); } - else if (hiword == PInvoke.EN_UPDATE) + else if (hiWord == PInvoke.EN_UPDATE) { // Force update to the Modified property, which will trigger ModifiedChanged event handlers _ = Modified; @@ -2066,19 +2450,37 @@ protected override void WndProc(ref Message m) { switch (m.MsgInternal) { + case PInvoke.WM_NCCALCSIZE: + WmNcCalcSize(ref m); + break; + + case PInvoke.WM_NCPAINT: + bool handled = WmNcPaint(ref m); + + if (!handled) + { + base.WndProc(ref m); + } + + break; + case PInvoke.WM_LBUTTONDBLCLK: _doubleClickFired = true; base.WndProc(ref m); break; + case MessageId.WM_REFLECT_COMMAND: WmReflectCommand(ref m); break; + case PInvoke.WM_GETDLGCODE: WmGetDlgCode(ref m); break; + case PInvoke.WM_SETFONT: WmSetFont(ref m); break; + case PInvoke.WM_CONTEXTMENU: if (ShortcutsEnabled) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ProfessionalColorTable.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ProfessionalColorTable.cs index 4137a789907..6eebfd89d20 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ProfessionalColorTable.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ProfessionalColorTable.cs @@ -67,7 +67,7 @@ private Dictionary ColorTable /// /// When this is specified, professional colors picks from SystemColors /// rather than colors that match the current theme. If theming is not - /// turned on, we'll fall back to SystemColors. + /// turned on, we'll fall back to Application.SystemColors. /// public bool UseSystemColors { @@ -405,18 +405,18 @@ private void InitSystemColors(ref Dictionary rgbTable) if (lowResolution || highContrast) { - rgbTable[KnownColors.msocbvcrCBBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = SystemColors.ControlLight; - rgbTable[KnownColors.msocbvcrCBDragHandle] = controlText; - rgbTable[KnownColors.msocbvcrCBGradMainMenuHorzEnd] = buttonFace; - rgbTable[KnownColors.msocbvcrCBGradOptionsBegin] = buttonShadow; - rgbTable[KnownColors.msocbvcrCBGradOptionsMiddle] = buttonShadow; - rgbTable[KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = buttonShadow; - rgbTable[KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = buttonShadow; - rgbTable[KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = buttonShadow; - rgbTable[KnownColors.msocbvcrCBMenuBdrOuter] = controlText; - rgbTable[KnownColors.msocbvcrCBMenuBkgd] = window; - rgbTable[KnownColors.msocbvcrCBSplitterLine] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelectedMouseOver] = SystemColors.ControlLight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBDragHandle] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMainMenuHorzEnd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsBegin] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradOptionsMiddle] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedBegin] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedMiddle] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBGradMenuIconBkgdDroppedEnd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBdrOuter] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBMenuBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLine] = buttonShadow; } else { @@ -434,7 +434,7 @@ private void InitSystemColors(ref Dictionary rgbTable) rgbTable[KnownColors.msocbvcrCBSplitterLine] = GetAlphaBlendedColorHighRes(null, buttonShadow, window, 70); } - rgbTable[KnownColors.msocbvcrCBCtlBkgdSelected] = (lowResolution) ? SystemColors.ControlLight : highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBCtlBkgdSelected] = (lowResolution) ? SystemColors.ControlLight : highlight; rgbTable[KnownColors.msocbvcrCBBdrOuterDocked] = buttonFace; rgbTable[KnownColors.msocbvcrCBBdrOuterDocked] = buttonShadow; @@ -502,167 +502,167 @@ private void InitSystemColors(ref Dictionary rgbTable) rgbTable[KnownColors.msocbvcrCBShadow] = rgbTable[KnownColors.msocbvcrCBBkgd]; - rgbTable[KnownColors.msocbvcrCBSplitterLineLight] = buttonHighlight; - rgbTable[KnownColors.msocbvcrCBTearOffHandle] = empty; - rgbTable[KnownColors.msocbvcrCBTearOffHandleMouseOver] = empty; - rgbTable[KnownColors.msocbvcrCBTitleBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrCBTitleText] = buttonHighlight; - rgbTable[KnownColors.msocbvcrDisabledFocuslessHighlightedText] = grayText; - rgbTable[KnownColors.msocbvcrDisabledHighlightedText] = grayText; - rgbTable[KnownColors.msocbvcrDlgGroupBoxText] = controlText; - rgbTable[KnownColors.msocbvcrDocTabBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrDocTabBdrDark] = buttonFace; - rgbTable[KnownColors.msocbvcrDocTabBdrDarkMouseDown] = highlight; - rgbTable[KnownColors.msocbvcrDocTabBdrDarkMouseOver] = SystemColors.MenuText; - rgbTable[KnownColors.msocbvcrDocTabBdrLight] = buttonFace; - rgbTable[KnownColors.msocbvcrDocTabBdrLightMouseDown] = highlight; - rgbTable[KnownColors.msocbvcrDocTabBdrLightMouseOver] = SystemColors.MenuText; - rgbTable[KnownColors.msocbvcrDocTabBdrMouseDown] = highlight; - rgbTable[KnownColors.msocbvcrDocTabBdrMouseOver] = SystemColors.MenuText; - rgbTable[KnownColors.msocbvcrDocTabBdrSelected] = buttonShadow; - rgbTable[KnownColors.msocbvcrDocTabBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrDocTabBkgdMouseDown] = highlight; - rgbTable[KnownColors.msocbvcrDocTabBkgdMouseOver] = highlight; - rgbTable[KnownColors.msocbvcrDocTabBkgdSelected] = window; - rgbTable[KnownColors.msocbvcrDocTabText] = controlText; - rgbTable[KnownColors.msocbvcrDocTabTextMouseDown] = highlightText; - rgbTable[KnownColors.msocbvcrDocTabTextMouseOver] = highlight; - rgbTable[KnownColors.msocbvcrDocTabTextSelected] = windowText; - rgbTable[KnownColors.msocbvcrDWActiveTabBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrDWActiveTabBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrDWActiveTabText] = buttonFace; - rgbTable[KnownColors.msocbvcrDWActiveTabText] = controlText; - rgbTable[KnownColors.msocbvcrDWActiveTabTextDisabled] = buttonShadow; - rgbTable[KnownColors.msocbvcrDWActiveTabTextDisabled] = controlText; - rgbTable[KnownColors.msocbvcrDWInactiveTabBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrDWInactiveTabBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrDWInactiveTabText] = buttonHighlight; - rgbTable[KnownColors.msocbvcrDWInactiveTabText] = controlText; - rgbTable[KnownColors.msocbvcrDWTabBkgdMouseDown] = buttonFace; - rgbTable[KnownColors.msocbvcrDWTabBkgdMouseOver] = buttonFace; - rgbTable[KnownColors.msocbvcrDWTabTextMouseDown] = controlText; - rgbTable[KnownColors.msocbvcrDWTabTextMouseOver] = controlText; - rgbTable[KnownColors.msocbvcrFocuslessHighlightedBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrFocuslessHighlightedBkgd] = SystemColors.InactiveCaption; - rgbTable[KnownColors.msocbvcrFocuslessHighlightedText] = controlText; - rgbTable[KnownColors.msocbvcrFocuslessHighlightedText] = SystemColors.InactiveCaptionText; - rgbTable[KnownColors.msocbvcrGDHeaderBdr] = highlight; - rgbTable[KnownColors.msocbvcrGDHeaderBkgd] = window; - rgbTable[KnownColors.msocbvcrGDHeaderCellBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrGDHeaderCellBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrGDHeaderCellBkgdSelected] = empty; - rgbTable[KnownColors.msocbvcrGDHeaderSeeThroughSelection] = highlight; - rgbTable[KnownColors.msocbvcrGSPDarkBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrGSPDarkBkgd] = window; - rgbTable[KnownColors.msocbvcrGSPGroupContentDarkBkgd] = window; - rgbTable[KnownColors.msocbvcrGSPGroupContentLightBkgd] = window; - rgbTable[KnownColors.msocbvcrGSPGroupContentText] = windowText; - rgbTable[KnownColors.msocbvcrGSPGroupContentTextDisabled] = grayText; - rgbTable[KnownColors.msocbvcrGSPGroupHeaderDarkBkgd] = window; - rgbTable[KnownColors.msocbvcrGSPGroupHeaderLightBkgd] = window; - rgbTable[KnownColors.msocbvcrGSPGroupHeaderText] = controlText; - rgbTable[KnownColors.msocbvcrGSPGroupHeaderText] = windowText; - rgbTable[KnownColors.msocbvcrGSPGroupline] = buttonShadow; - rgbTable[KnownColors.msocbvcrGSPGroupline] = window; - rgbTable[KnownColors.msocbvcrGSPHyperlink] = empty; - rgbTable[KnownColors.msocbvcrGSPLightBkgd] = window; - rgbTable[KnownColors.msocbvcrHyperlink] = empty; - rgbTable[KnownColors.msocbvcrHyperlinkFollowed] = empty; - rgbTable[KnownColors.msocbvcrJotNavUIBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrJotNavUIBdr] = windowText; - rgbTable[KnownColors.msocbvcrJotNavUIGradBegin] = buttonFace; - rgbTable[KnownColors.msocbvcrJotNavUIGradBegin] = window; - rgbTable[KnownColors.msocbvcrJotNavUIGradEnd] = window; - rgbTable[KnownColors.msocbvcrJotNavUIGradMiddle] = buttonFace; - rgbTable[KnownColors.msocbvcrJotNavUIGradMiddle] = window; - rgbTable[KnownColors.msocbvcrJotNavUIText] = windowText; - rgbTable[KnownColors.msocbvcrListHeaderArrow] = controlText; - rgbTable[KnownColors.msocbvcrNetLookBkgnd] = empty; - rgbTable[KnownColors.msocbvcrOABBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrOBBkgdBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrOBBkgdBdrContrast] = window; - rgbTable[KnownColors.msocbvcrOGMDIParentWorkspaceBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrOGRulerActiveBkgd] = window; - rgbTable[KnownColors.msocbvcrOGRulerBdr] = controlText; - rgbTable[KnownColors.msocbvcrOGRulerBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrOGRulerInactiveBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrOGRulerTabBoxBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrOGRulerTabBoxBdrHighlight] = buttonHighlight; - rgbTable[KnownColors.msocbvcrOGRulerTabStopTicks] = buttonShadow; - rgbTable[KnownColors.msocbvcrOGRulerText] = windowText; - rgbTable[KnownColors.msocbvcrOGTaskPaneGroupBoxHeaderBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrOGWorkspaceBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKFlagNone] = buttonHighlight; - rgbTable[KnownColors.msocbvcrOLKFolderbarDark] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKFolderbarLight] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKFolderbarText] = window; - rgbTable[KnownColors.msocbvcrOLKGridlines] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKGroupLine] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKGroupNested] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKGroupShaded] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKGroupText] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKIconBar] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKInfoBarBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKInfoBarText] = controlText; - rgbTable[KnownColors.msocbvcrOLKPreviewPaneLabelText] = windowText; - rgbTable[KnownColors.msocbvcrOLKTodayIndicatorDark] = highlight; - rgbTable[KnownColors.msocbvcrOLKTodayIndicatorLight] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKWBActionDividerLine] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKWBButtonDark] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKWBButtonLight] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKWBButtonLight] = buttonHighlight; - rgbTable[KnownColors.msocbvcrOLKWBDarkOutline] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKWBFoldersBackground] = window; - rgbTable[KnownColors.msocbvcrOLKWBHoverButtonDark] = empty; - rgbTable[KnownColors.msocbvcrOLKWBHoverButtonLight] = empty; - rgbTable[KnownColors.msocbvcrOLKWBLabelText] = windowText; - rgbTable[KnownColors.msocbvcrOLKWBPressedButtonDark] = empty; - rgbTable[KnownColors.msocbvcrOLKWBPressedButtonLight] = empty; - rgbTable[KnownColors.msocbvcrOLKWBSelectedButtonDark] = empty; - rgbTable[KnownColors.msocbvcrOLKWBSelectedButtonLight] = empty; - rgbTable[KnownColors.msocbvcrOLKWBSplitterDark] = buttonShadow; - rgbTable[KnownColors.msocbvcrOLKWBSplitterLight] = buttonFace; - rgbTable[KnownColors.msocbvcrOLKWBSplitterLight] = buttonShadow; - rgbTable[KnownColors.msocbvcrPlacesBarBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrPPOutlineThumbnailsPaneTabBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrPPOutlineThumbnailsPaneTabText] = windowText; - rgbTable[KnownColors.msocbvcrPPSlideBdrActiveSelected] = highlight; - rgbTable[KnownColors.msocbvcrPPSlideBdrActiveSelectedMouseOver] = highlight; - rgbTable[KnownColors.msocbvcrPPSlideBdrInactiveSelected] = grayText; - rgbTable[KnownColors.msocbvcrPPSlideBdrMouseOver] = highlight; - rgbTable[KnownColors.msocbvcrPubPrintDocScratchPageBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrPubWebDocScratchPageBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrSBBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrScrollbarBkgd] = buttonShadow; - rgbTable[KnownColors.msocbvcrToastGradBegin] = buttonFace; - rgbTable[KnownColors.msocbvcrToastGradEnd] = buttonFace; - rgbTable[KnownColors.msocbvcrWPBdrInnerDocked] = empty; - rgbTable[KnownColors.msocbvcrWPBdrOuterDocked] = buttonFace; - rgbTable[KnownColors.msocbvcrWPBdrOuterFloating] = buttonShadow; - rgbTable[KnownColors.msocbvcrWPBkgd] = window; - rgbTable[KnownColors.msocbvcrWPCtlBdr] = buttonShadow; - rgbTable[KnownColors.msocbvcrWPCtlBdrDefault] = buttonShadow; - rgbTable[KnownColors.msocbvcrWPCtlBdrDefault] = controlText; - rgbTable[KnownColors.msocbvcrWPCtlBdrDisabled] = buttonShadow; - rgbTable[KnownColors.msocbvcrWPCtlBkgd] = buttonFace; - rgbTable[KnownColors.msocbvcrWPCtlBkgdDisabled] = buttonFace; - rgbTable[KnownColors.msocbvcrWPCtlText] = controlText; - rgbTable[KnownColors.msocbvcrWPCtlTextDisabled] = buttonShadow; - rgbTable[KnownColors.msocbvcrWPCtlTextMouseDown] = highlightText; - rgbTable[KnownColors.msocbvcrWPGroupline] = buttonShadow; - rgbTable[KnownColors.msocbvcrWPInfoTipBkgd] = SystemColors.Info; - rgbTable[KnownColors.msocbvcrWPInfoTipText] = SystemColors.InfoText; - rgbTable[KnownColors.msocbvcrWPNavBarBkgnd] = buttonFace; - rgbTable[KnownColors.msocbvcrWPText] = controlText; - rgbTable[KnownColors.msocbvcrWPText] = windowText; - rgbTable[KnownColors.msocbvcrWPTextDisabled] = grayText; - rgbTable[KnownColors.msocbvcrWPTitleBkgdActive] = highlight; - rgbTable[KnownColors.msocbvcrWPTitleBkgdInactive] = buttonFace; - rgbTable[KnownColors.msocbvcrWPTitleTextActive] = highlightText; - rgbTable[KnownColors.msocbvcrWPTitleTextInactive] = controlText; - rgbTable[KnownColors.msocbvcrXLFormulaBarBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBSplitterLineLight] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandle] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTearOffHandleMouseOver] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBTitleText] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledFocuslessHighlightedText] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDisabledHighlightedText] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDlgGroupBoxText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDark] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrDarkMouseOver] = SystemColors.MenuText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrLightMouseOver] = SystemColors.MenuText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrMouseOver] = SystemColors.MenuText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBdrSelected] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseDown] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabBkgdSelected] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseDown] = highlightText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDocTabTextSelected] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWActiveTabTextDisabled] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWInactiveTabText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseDown] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabBkgdMouseOver] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseDown] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrDWTabTextMouseOver] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedBkgd] = SystemColors.InactiveCaption; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrFocuslessHighlightedText] = SystemColors.InactiveCaptionText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBdr] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderCellBkgdSelected] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGDHeaderSeeThroughSelection] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPDarkBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentDarkBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentLightBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupContentTextDisabled] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderDarkBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderLightBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupHeaderText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPGroupline] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPHyperlink] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrGSPLightBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlink] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrHyperlinkFollowed] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIBdr] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradBegin] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradEnd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIGradMiddle] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrJotNavUIText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrListHeaderArrow] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrNetLookBkgnd] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOABBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOBBkgdBdrContrast] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGMDIParentWorkspaceBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerActiveBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBdr] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerInactiveBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabBoxBdrHighlight] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerTabStopTicks] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGRulerText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGTaskPaneGroupBoxHeaderBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOGWorkspaceBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFlagNone] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarDark] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarLight] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKFolderbarText] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGridlines] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupLine] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupNested] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupShaded] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKGroupText] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKIconBar] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKInfoBarText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKPreviewPaneLabelText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorDark] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKTodayIndicatorLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBActionDividerLine] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonDark] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBButtonLight] = buttonHighlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBDarkOutline] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBFoldersBackground] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonDark] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBHoverButtonLight] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBLabelText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonDark] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBPressedButtonLight] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonDark] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSelectedButtonLight] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterDark] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrOLKWBSplitterLight] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPlacesBarBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabAreaBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabInactiveBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPOutlineThumbnailsPaneTabText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelected] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrActiveSelectedMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrInactiveSelected] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPPSlideBdrMouseOver] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubPrintDocScratchPageBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrPubWebDocScratchPageBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrSBBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrScrollbarBkgd] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradBegin] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrToastGradEnd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrInnerDocked] = empty; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterDocked] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBdrOuterFloating] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPBkgd] = window; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdr] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDefault] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBdrDisabled] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlBkgdDisabled] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextDisabled] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPCtlTextMouseDown] = highlightText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPGroupline] = buttonShadow; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipBkgd] = SystemColors.Info; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPInfoTipText] = SystemColors.InfoText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPNavBarBkgnd] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPText] = windowText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTextDisabled] = grayText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdActive] = highlight; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleBkgdInactive] = buttonFace; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextActive] = highlightText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrWPTitleTextInactive] = controlText; + rgbTable[ProfessionalColorTable.KnownColors.msocbvcrXLFormulaBarBkgd] = buttonFace; } private static void InitOliveLunaColors(ref Dictionary rgbTable) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs index 4af6c352791..057535e3aab 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs @@ -29,7 +29,11 @@ public StatusStrip() SuspendLayout(); CanOverflow = false; LayoutStyle = ToolStripLayoutStyle.Table; - RenderMode = ToolStripRenderMode.System; + + // Default changed for DarkMode from System to ManagerRenderMode. + // Also to be consistent to the MenuStrip. + // TODO: We'd need to quirk that! + RenderMode = ToolStripRenderMode.ManagerRenderMode; GripStyle = ToolStripGripStyle.Hidden; SetStyle(ControlStyles.ResizeRedraw, true); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripComboBox.ToolStripComboBoxControl.ToolStripComboBoxFlatComboAdapter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripComboBox.ToolStripComboBoxControl.ToolStripComboBoxFlatComboAdapter.cs index 155717838db..fec54d42980 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripComboBox.ToolStripComboBoxControl.ToolStripComboBoxFlatComboAdapter.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripComboBox.ToolStripComboBoxControl.ToolStripComboBoxFlatComboAdapter.cs @@ -45,7 +45,7 @@ protected override Color GetOuterBorderColor(ComboBox comboBox) return base.GetOuterBorderColor(comboBox); } - return (comboBox.Enabled) ? SystemColors.Window : GetColorTable(comboBox as ToolStripComboBoxControl).ComboBoxBorder; + return (comboBox.Enabled) ? ControlSystemColors.Default.Window : GetColorTable(comboBox as ToolStripComboBoxControl).ComboBoxBorder; } protected override Color GetPopupOuterBorderColor(ComboBox comboBox, bool focused) @@ -57,12 +57,12 @@ protected override Color GetPopupOuterBorderColor(ComboBox comboBox, bool focuse if (!comboBox.Enabled) { - return SystemColors.ControlDark; + return ControlSystemColors.Default.ControlDark; } return focused ? GetColorTable(comboBox as ToolStripComboBoxControl).ComboBoxBorder - : SystemColors.Window; + : ControlSystemColors.Default.Window; } protected override void DrawFlatComboDropDown(ComboBox comboBox, Graphics g, Rectangle dropDownRect) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.ModalMenuFilter.HostedWindowsFormsMessageHook.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.ModalMenuFilter.HostedWindowsFormsMessageHook.cs index ff10309778d..0e7bd35e295 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.ModalMenuFilter.HostedWindowsFormsMessageHook.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.ModalMenuFilter.HostedWindowsFormsMessageHook.cs @@ -25,14 +25,13 @@ public HostedWindowsFormsMessageHook() #if DEBUG private readonly string _callingStack; -#pragma warning disable CA1821 // Remove empty Finalizers + [SuppressMessage("Performance", "CA1821:Remove empty Finalizers", Justification = "")] ~HostedWindowsFormsMessageHook() { Debug.Assert( _messageHookHandle == IntPtr.Zero, $"Finalizing an active mouse hook. This will crash the process. Calling stack: {_callingStack}"); } -#pragma warning restore CA1821 #endif public bool HookMessages diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.cs index f8b37e9aa01..da1d3553be2 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripManager.cs @@ -32,7 +32,7 @@ public static partial class ToolStripManager private const int StaticEventDefaultRendererChanged = 0; private const int StaticEventCount = 1; - private static readonly object s_internalSyncObject = new(); + private static readonly Lock s_internalSyncObject = new(); private static void InitializeThread() { @@ -550,12 +550,22 @@ public static bool VisualStylesEnabled internal static ToolStripRenderer CreateRenderer(ToolStripManagerRenderMode renderMode) { - return renderMode switch + switch (renderMode) { - ToolStripManagerRenderMode.System => new ToolStripSystemRenderer(isDefault: true), - ToolStripManagerRenderMode.Professional => new ToolStripProfessionalRenderer(isDefault: true), - _ => new ToolStripSystemRenderer(isDefault: true), - }; + case ToolStripManagerRenderMode.System: + return new ToolStripSystemRenderer(isDefault: true); + case ToolStripManagerRenderMode.Professional: + if (Application.IsDarkModeEnabled) + { + return new ToolStripProfessionalRenderer(new DarkProfessionalColors()); + } + + return new ToolStripProfessionalRenderer(isDefault: true); + + case ToolStripManagerRenderMode.Custom: + default: + return new ToolStripSystemRenderer(isDefault: true); + } } internal static ToolStripRenderer CreateRenderer(ToolStripRenderMode renderMode) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripTextBox.ToolStripTextBoxControl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripTextBox.ToolStripTextBoxControl.cs index fb281026d80..90ff965947b 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripTextBox.ToolStripTextBoxControl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ToolStrips/ToolStripTextBox.ToolStripTextBoxControl.cs @@ -252,7 +252,7 @@ private void WmNCPaint(ref Message m) using Graphics g = hdc.CreateGraphics(); Rectangle clientRect = AbsoluteClientRectangle; - // Could have set up a clip and fill-rectangled, thought this would be faster. + // Could have set up a clip and rectangle-filled, thought this would be faster. using var brush = innerBorderColor.GetCachedSolidBrushScope(); g.FillRectangle(brush, 0, 0, Width, clientRect.Top); // top border g.FillRectangle(brush, 0, 0, clientRect.Left, Height); // left border diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs index ed155b2e55f..a814db3ca3d 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/TreeView/TreeView.cs @@ -192,7 +192,7 @@ internal override void ReleaseUiaProvider(HWND handle) /// public override Color BackColor { - get => ShouldSerializeBackColor() ? base.BackColor : SystemColors.Window; + get => ShouldSerializeBackColor() ? base.BackColor : Drawing.SystemColors.Window; set { base.BackColor = value; @@ -436,7 +436,7 @@ protected override bool DoubleBuffered /// public override Color ForeColor { - get => ShouldSerializeForeColor() ? base.ForeColor : SystemColors.WindowText; + get => ShouldSerializeForeColor() ? base.ForeColor : Drawing.SystemColors.WindowText; set { base.ForeColor = value; @@ -867,7 +867,7 @@ public Color LineColor _lineColor = value; if (IsHandleCreated) { - PInvoke.SendMessage(this, PInvoke.TVM_SETLINECOLOR, 0, _lineColor.ToWin32()); + PInvoke.SendMessage(this, PInvoke.TVM_SETLINECOLOR, 0, AdaptForDarkMode(_lineColor).ToWin32()); } } } @@ -1859,7 +1859,7 @@ protected override void OnHandleCreated(EventArgs e) } // Workaround for problem in TreeView where it doesn't recognize the TVS_CHECKBOXES - // style if it is set before the window is created. To get around the problem, + // style if it is set before the window is created. To get around the problem, // we set it here after the window is created, and we make sure we don't set it // in getCreateParams so that this will actually change the value of the bit. // This seems to make the TreeView happy. @@ -1878,22 +1878,22 @@ protected override void OnHandleCreated(EventArgs e) } Color c = BackColor; - if (c != SystemColors.Window) + if (c != Drawing.SystemColors.Window || IsDarkModeEnabled) { - PInvoke.SendMessage(this, PInvoke.TVM_SETBKCOLOR, 0, c.ToWin32()); + PInvoke.SendMessage(this, PInvoke.TVM_SETBKCOLOR, 0, AdaptForDarkMode(c).ToWin32()); } c = ForeColor; - if (c != SystemColors.WindowText) + if (c != Drawing.SystemColors.WindowText || IsDarkModeEnabled) { - PInvoke.SendMessage(this, PInvoke.TVM_SETTEXTCOLOR, 0, c.ToWin32()); + PInvoke.SendMessage(this, PInvoke.TVM_SETTEXTCOLOR, 0, AdaptForDarkMode(c).ToWin32()); } - // Put the linecolor into the native control only if set. + // Put the LineColor into the native control only if set. if (_lineColor != Color.Empty) { - PInvoke.SendMessage(this, PInvoke.TVM_SETLINECOLOR, 0, _lineColor.ToWin32()); + PInvoke.SendMessage(this, PInvoke.TVM_SETLINECOLOR, 0, AdaptForDarkMode(_lineColor).ToWin32()); } if (_imageList is not null) @@ -2641,7 +2641,10 @@ private void UpdateTreeViewExtendedStyles() // This stops the style from being removed for any derived classes that set it using P/Invoke. if (_treeViewState[TREEVIEWSTATE_doubleBufferedPropertySet]) { - PInvoke.SendMessage(this, PInvoke.TVM_SETEXTENDEDSTYLE, (WPARAM)(nint)PInvoke.TVS_EX_DOUBLEBUFFER, (LPARAM)(nint)(DoubleBuffered ? PInvoke.TVS_EX_DOUBLEBUFFER : 0)); + PInvoke.SendMessage(this, + PInvoke.TVM_SETEXTENDEDSTYLE, + (WPARAM)(nint)PInvoke.TVS_EX_DOUBLEBUFFER, + (LPARAM)(nint)(DoubleBuffered ? PInvoke.TVS_EX_DOUBLEBUFFER : 0)); } } @@ -2830,12 +2833,17 @@ private unsafe void CustomDraw(ref Message m) TreeNodeStates curState = e.State; Font font = node.NodeFont ?? node.TreeView.Font; - Color color = (((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) && node.TreeView.Focused) ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor; + + Color color = (((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) && node.TreeView.Focused) + ? SystemColors.HighlightText + : AdaptForDarkMode((node.ForeColor != Color.Empty) + ? node.ForeColor + : node.TreeView.ForeColor); // Draw the actual node. if ((curState & TreeNodeStates.Selected) == TreeNodeStates.Selected) { - g.FillRectangle(SystemBrushes.Highlight, bounds); + g.FillRectangle(AdaptForDarkMode(SystemBrushes.Highlight), bounds); ControlPaint.DrawFocusRectangle(g, bounds, color, SystemColors.Highlight); TextRenderer.DrawText(g, node.Text, font, bounds, color, TextFormatFlags.Default); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/UpDown/UpDownBase.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/UpDown/UpDownBase.cs index add06a6ff4c..b9dcc5abd0a 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/UpDown/UpDownBase.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/UpDown/UpDownBase.cs @@ -576,7 +576,7 @@ protected override void OnPaint(PaintEventArgs e) // Draws a grayed rectangle around the upDownEdit, since otherwise we will have a white // border around the upDownEdit, which is inconsistent with Windows' behavior // we only want to do this when BackColor is not serialized, since otherwise - // we should display the backcolor instead of the usual grayed textbox. + // we should display the back color instead of the usual grayed textbox. editBounds.Inflate(1, 1); ControlPaint.DrawBorderSimple(e, editBounds, SystemColors.Control); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/DarkMode.cs b/src/System.Windows.Forms/src/System/Windows/Forms/DarkMode.cs new file mode 100644 index 00000000000..04d83159b72 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/DarkMode.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms +{ + [Experimental("WFO9001")] + public enum DarkMode + { + /// + /// Dark mode in the current context is not supported. This value cannot be set as a default value; it is rather a potential return value + /// for the property. + /// + NotSupported = 0, + + /// + /// The setting for the current dark mode context is inherited from the parent context. Note, that you + /// can even pass this value to , in which case + /// the actual dark mode setting will be inherited from the Windows OS system setting. + /// + Inherits = 1, + + /// + /// Dark mode for the current context is or should be enabled. + /// + Enabled = 2, + + /// + /// Dark mode the current context is or should be disabled. + /// + Disabled = 3 + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/DarkProfessionalColors.cs b/src/System.Windows.Forms/src/System/Windows/Forms/DarkProfessionalColors.cs new file mode 100644 index 00000000000..89e0cb8e81c --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/DarkProfessionalColors.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using static System.Windows.Forms.Control; + +namespace System.Windows.Forms +{ + internal class DarkProfessionalColors : ProfessionalColorTable + { + public override Color MenuItemPressedGradientBegin + => Color.FromArgb(0xFF, 0x60, 0x60, 0x60); + + public override Color MenuItemPressedGradientMiddle + => Color.FromArgb(0xFF, 0x60, 0x60, 0x60); + + public override Color MenuItemPressedGradientEnd + => Color.FromArgb(0xFF, 0x60, 0x60, 0x60); + + public override Color MenuItemSelected + => ControlSystemColors.DefaultDarkMode.ControlText; + + public override Color MenuItemSelectedGradientBegin + => Color.FromArgb(0xFF, 0x40, 0x40, 0x40); + + public override Color MenuItemSelectedGradientEnd + => Color.FromArgb(0xFF, 0x40, 0x40, 0x40); + + public override Color MenuStripGradientBegin + => ControlSystemColors.DefaultDarkMode.Control; + + public override Color MenuStripGradientEnd + => ControlSystemColors.DefaultDarkMode.Control; + + public override Color StatusStripGradientBegin + => ControlSystemColors.DefaultDarkMode.Control; + + public override Color StatusStripGradientEnd + => ControlSystemColors.DefaultDarkMode.Control; + + public override Color ToolStripDropDownBackground + => ControlSystemColors.DefaultDarkMode.Control; + + public override Color ImageMarginGradientBegin + => ControlSystemColors.DefaultDarkMode.Control; + + public override Color ImageMarginGradientMiddle + => ControlSystemColors.DefaultDarkMode.Control; + + public override Color ImageMarginGradientEnd + => ControlSystemColors.DefaultDarkMode.Control; + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Dialogs/CommonDialogs/FontDialog.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Dialogs/CommonDialogs/FontDialog.cs index 082b7d26fcc..16002c752b5 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Dialogs/CommonDialogs/FontDialog.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Dialogs/CommonDialogs/FontDialog.cs @@ -96,7 +96,7 @@ public Color Color { get { - // Convert to RGB and back to resolve indirect colors like SystemColors.ControlText + // Convert to RGB and back to resolve indirect colors like Application.SystemColors.ControlText // to real color values like Color.Lime return !_usingDefaultIndirectColor ? _color : ColorTranslator.FromWin32(ColorTranslator.ToWin32(_color)); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs index 95b389b1771..96aed133114 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs @@ -11,6 +11,7 @@ using System.Windows.Forms.VisualStyles; using Windows.Win32.System.Threading; using Windows.Win32.UI.Accessibility; +using Windows.Win32.Graphics.Dwm; namespace System.Windows.Forms; @@ -97,7 +98,7 @@ public partial class Form : ContainerControl private const int SizeGripSize = 16; private static Icon? s_defaultIcon; - private static readonly object s_internalSyncObject = new(); + private static readonly Lock s_internalSyncObject = new(); // Property store keys for properties. The property store allocates most efficiently // in groups of four, so we try to lump properties in groups of four based on how @@ -160,6 +161,14 @@ public partial class Form : ContainerControl private bool _processingDpiChanged; private bool _inRecreateHandle; + public enum WindowCornerPreference + { + Default = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DEFAULT, + DoNotRound = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND, + Round = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND, + RoundSmall = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL + } + /// /// Initializes a new instance of the class. /// @@ -2328,6 +2337,116 @@ protected override void SetVisibleCore(bool value) } } + /// + /// Sets the rounding style of the corners of this Form. + /// + /// + /// A value of the enum + /// which determines the corner rounding style. + /// + public void SetWindowCornerPreference(WindowCornerPreference cornerPreference) + { + SetWindowCornerPreferenceInternal(cornerPreference); + } + + private unsafe void SetWindowCornerPreferenceInternal(WindowCornerPreference cornerPreference) + { + if (!IsHandleCreated) + { + return; + } + + DWM_WINDOW_CORNER_PREFERENCE dwmCornerPreference = cornerPreference switch + { + WindowCornerPreference.Default => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DEFAULT, + WindowCornerPreference.DoNotRound => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND, + WindowCornerPreference.Round => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND, + WindowCornerPreference.RoundSmall => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL, + _ => throw new ArgumentOutOfRangeException(nameof(cornerPreference)) + }; + + PInvoke.DwmSetWindowAttribute( + HWND, + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &dwmCornerPreference, + sizeof(DWM_WINDOW_CORNER_PREFERENCE)); + } + + /// + /// Sets the color of the border of this Form. + /// + /// The border color. + public void SetWindowBorderColor(Color color) + { + SetWindowBorderColorInternal(color); + } + + private unsafe void SetWindowBorderColorInternal(Color color) + { + if (!IsHandleCreated) + { + return; + } + + COLORREF colorRef = color; + + PInvoke.DwmSetWindowAttribute( + HWND, + DWMWINDOWATTRIBUTE.DWMWA_BORDER_COLOR, + &colorRef, + (uint)sizeof(COLORREF)); + } + + /// + /// Sets the color of the title bar of this Form containing the caption and control buttons. + /// + /// The title bar color. + public void SetWindowCaptionColor(Color color) + { + SetWindowCaptionColorInternal(color); + } + + private unsafe void SetWindowCaptionColorInternal(Color color) + { + if (!IsHandleCreated) + { + return; + } + + COLORREF colorRef = color; + + PInvoke.DwmSetWindowAttribute( + HWND, + DWMWINDOWATTRIBUTE.DWMWA_CAPTION_COLOR, + &colorRef, + (uint)sizeof(COLORREF)); + } + + /// + /// Sets the color of the text in the title bar of this Form. + /// + /// The text color of the title bar. + public void SetWindowCaptionTextColor(Color color) + { + SetWindowCaptionTextColorInternal(color); + } + + private unsafe void SetWindowCaptionTextColorInternal(Color color) + { + if (!IsHandleCreated) + { + return; + } + + COLORREF colorRef = color; + + PInvoke.DwmSetWindowAttribute( + HWND, + DWMWINDOWATTRIBUTE.DWMWA_TEXT_COLOR, + &colorRef, + (uint)sizeof(COLORREF)); + } + /// /// Gets or sets the form's window state. /// @@ -2719,7 +2838,8 @@ private void AdjustSystemMenu(HMENU hmenu) UpdateWindowState(); FormWindowState winState = WindowState; FormBorderStyle borderStyle = FormBorderStyle; - bool sizableBorder = borderStyle is FormBorderStyle.SizableToolWindow or FormBorderStyle.Sizable; + bool sizableBorder = (borderStyle is FormBorderStyle.SizableToolWindow + or FormBorderStyle.Sizable); bool showMin = MinimizeBox && winState != FormWindowState.Minimized; bool showMax = MaximizeBox && winState != FormWindowState.Maximized; @@ -4059,6 +4179,11 @@ protected virtual void OnLoad(EventArgs e) // Finally fire the new OnShown(unless the form has already been closed). if (IsHandleCreated) { + if (IsDarkModeEnabled) + { + PInvoke.SetWindowTheme(HWND, $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}", null); + } + BeginInvoke(new MethodInvoker(CallShownEvent)); } } @@ -4202,7 +4327,7 @@ protected override void OnPaint(PaintEventArgs e) if (IsMdiContainer) { - e.GraphicsInternal.FillRectangle(SystemBrushes.AppWorkspace, ClientRectangle); + e.GraphicsInternal.FillRectangle(AdaptForDarkMode(SystemBrushes.AppWorkspace), ClientRectangle); } } @@ -5524,9 +5649,9 @@ protected override void UpdateDefaultButton() } } - if (containerControl.ActiveControl is IButtonControl buttonControl) + if (containerControl.ActiveControl is IButtonControl control) { - SetDefaultButton(buttonControl); + SetDefaultButton(control); } else { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MDI/MDIClient.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MDI/MDIClient.cs index 044baf3cc0d..1894007ddea 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/MDI/MDIClient.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MDI/MDIClient.cs @@ -309,7 +309,10 @@ private void SetWindowRgn() } } - internal override bool ShouldSerializeBackColor() => BackColor != SystemColors.AppWorkspace; + internal override bool ShouldSerializeBackColor() + { + return BackColor != SystemColors.AppWorkspace; + } private static bool ShouldSerializeLocation() => false; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Printing/PrintPreviewControl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Printing/PrintPreviewControl.cs index 5f29bc722df..7ef60e9477c 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Printing/PrintPreviewControl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Printing/PrintPreviewControl.cs @@ -290,9 +290,9 @@ public override string Text } [EditorBrowsable(EditorBrowsableState.Never)] - public override void ResetBackColor() => BackColor = SystemColors.AppWorkspace; + public override void ResetBackColor() => BackColor = Drawing.SystemColors.AppWorkspace; - internal override bool ShouldSerializeBackColor() => !BackColor.Equals(SystemColors.AppWorkspace); + internal override bool ShouldSerializeBackColor() => !BackColor.Equals(Drawing.SystemColors.AppWorkspace); [EditorBrowsable(EditorBrowsableState.Never)] public override void ResetForeColor() => ForeColor = Color.White; @@ -703,7 +703,7 @@ private void PaintResizeBox(PaintEventArgs e) return; } - e.Graphics.FillRectangle(SystemBrushes.Control, ResizeBoxRectangle); + e.Graphics.FillRectangle(AdaptForDarkMode(SystemBrushes.Control), ResizeBoxRectangle); } private void PaintFocus(PaintEventArgs e, bool isHighContrast) @@ -737,7 +737,7 @@ private void PaintFocus(PaintEventArgs e, bool isHighContrast) /// private Color GetBackColor(bool isHighContract) { - return (isHighContract && !ShouldSerializeBackColor()) ? SystemColors.ControlDark : BackColor; + return (isHighContract && !ShouldSerializeBackColor()) ? Drawing.SystemColors.ControlDark : BackColor; } private static int PixelsToPhysical(int pixels, int dpi) => (int)(pixels * 100.0 / dpi); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimatedControlRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimatedControlRenderer.cs new file mode 100644 index 00000000000..c8cb48a2454 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimatedControlRenderer.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; + +namespace System.Windows.Forms.Rendering.Animation; + +/// +/// Represents an abstract base class for animated control renderers. +/// +internal abstract class AnimatedControlRenderer +{ + private bool _disposedValue; + private bool _isRunning; + private readonly Control _control; + + /// + /// Initializes a new instance of the class with the specified control. + /// + /// The control associated with the renderer. + protected AnimatedControlRenderer(Control control) + { + _control = control; + } + + /// + /// Callback for the animation progress. This method is called by the animation manager roughly every + /// 25ms which results in about 40 frames per second. + /// + /// A fraction between 0 and 1 representing the animation progress. + public abstract void AnimationProc(float animationProgress); + + /// + /// Called when the control needs to be painted. + /// + /// The to paint the control. + public abstract void RenderControl(Graphics graphics); + + /// + /// Invalidates the control, causing it to be redrawn, which in turns triggers + /// . + /// + public void Invalidate() + { + _control.Invalidate(); + } + + /// + /// Starts the animation and gets the animation parameters. + /// + public void StartAnimation() + { + if (_isRunning) + { + return; + } + + // Get the animation parameters + (int animationDuration, AnimationCycle animationCycle) = OnStartAnimation(); + + // Register the renderer with the animation manager + AnimationManager.RegisterOrUpdateAnimationRenderer( + this, + animationDuration, + animationCycle); + + _isRunning = true; + } + + internal void RestartAnimation() + { + if (_isRunning) + { + StopAnimation(); + } + + StartAnimation(); + } + + /// + /// Called in a derived class when the animation starts. The derived class returns the animation duration and cycle type. + /// + /// + /// Tuple containing the animation duration and cycle type. + /// + protected abstract (int animationDuration, AnimationCycle animationCycle) OnStartAnimation(); + + /// + /// Stops the animation. + /// + public void StopAnimation() + { + _isRunning = false; + OnStoppedAnimation(); + } + + /// + /// Called when the animation stops. + /// + protected abstract void OnStoppedAnimation(); + + /// + /// Gets the DPI scale of the control. + /// + protected int DpiScale => (int)(_control.DeviceDpi / 96f); + + public bool IsRunning => _isRunning; + + /// + /// Gets the control associated with the renderer. + /// + protected Control Control => _control; + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // Remove the renderer from the animation manager + AnimationManager.UnregisterAnimationRenderer(this); + } + + _disposedValue = true; + } + } + + /// + /// Releases all resources used by the . + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationCycle.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationCycle.cs new file mode 100644 index 00000000000..9529fbc86a4 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationCycle.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms.Rendering.Animation; + +internal enum AnimationCycle +{ + Once, + Loop, + Bounce +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationManager.AnimationRendererItem.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationManager.AnimationRendererItem.cs new file mode 100644 index 00000000000..e57230f0ebf --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationManager.AnimationRendererItem.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms.Rendering.Animation; + +internal partial class AnimationManager +{ + private class AnimationRendererItem + { + public long StopwatchTarget; + + public AnimationRendererItem(AnimatedControlRenderer renderer, int animationDuration, AnimationCycle animationCycle) + { + Renderer = renderer; + AnimationDuration = animationDuration; + AnimationCycle = animationCycle; + } + + public AnimatedControlRenderer Renderer { get; } + public int TriggerFrequency { get; } + public int AnimationDuration { get; set; } + public int FrameCount { get; set; } + public AnimationCycle AnimationCycle { get; set; } + public int FrameOffset { get; set; } = 1; + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationManager.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationManager.cs new file mode 100644 index 00000000000..592f989d4a1 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/Animation/AnimationManager.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ThreadingTimer = System.Threading.Timer; + +namespace System.Windows.Forms.Rendering.Animation; + +internal partial class AnimationManager +{ + private readonly ThreadingTimer _timer; + private readonly Stopwatch _stopwatch; + + private readonly Dictionary _renderer = []; + + private readonly WindowsFormsSynchronizationContext? _syncContext = (WindowsFormsSynchronizationContext?)SynchronizationContext.Current; + private static AnimationManager? s_instance; + private readonly Lock _lock = new(); + + private static AnimationManager Instance + => s_instance ??= new AnimationManager(); + + /// + /// The frame rate of every animation. + /// + public const int FrameRate = 40; + + /// + /// Initializes a new instance of the class. + /// + private AnimationManager() + { + _timer = new ThreadingTimer( + callback: OnTick, + state: null, + dueTime: 0, + // This is the smallest interval we can safely achieve. + // If we want to go faster, we would need to use a Multimedia Timer. + period: 1000 / FrameRate); + + _stopwatch = Stopwatch.StartNew(); + + Application.ApplicationExit += (sender, e) => DisposeRenderer(); + } + + /// + /// Disposes the animation renderers. + /// + private void DisposeRenderer() + { + // stop the timer: + _timer.Dispose(); + + lock (_lock) + { + foreach (AnimatedControlRenderer renderer in _renderer.Keys) + { + renderer.Dispose(); + } + } + } + + /// + /// Registers an animation renderer. + /// + /// The animation renderer to register. + /// The duration of the animation. + /// The animation cycle. + public static void RegisterOrUpdateAnimationRenderer( + AnimatedControlRenderer animationRenderer, + int animationDuration, + AnimationCycle animationCycle) + { + lock (Instance._lock) + { + // If the renderer is already registered, update the animation parameters. + if (Instance._renderer.TryGetValue(animationRenderer, out AnimationRendererItem? renderItem)) + { + renderItem.StopwatchTarget = Instance._stopwatch.ElapsedMilliseconds + animationDuration; + renderItem.AnimationDuration = animationDuration; + renderItem.AnimationCycle = animationCycle; + + return; + } + + renderItem = new AnimationRendererItem(animationRenderer, animationDuration, animationCycle) + { + StopwatchTarget = Instance._stopwatch.ElapsedMilliseconds + animationDuration, + }; + + Instance._renderer.Add(animationRenderer, renderItem); + } + } + + /// + /// Unregisters an animation renderer. + /// + /// The animation renderer to unregister. + internal static void UnregisterAnimationRenderer(AnimatedControlRenderer animationRenderer) + { + lock (Instance._lock) + { + Instance._renderer.Remove(animationRenderer); + } + } + + /// + /// Handles the tick event of the timer. + /// + /// The state object. + private void OnTick(object? state) + { + lock (_lock) + { + long elapsedStopwatchMilliseconds = Instance._stopwatch.ElapsedMilliseconds; + + foreach (AnimationRendererItem item in _renderer.Values) + { + if (!item.Renderer.IsRunning) + { + continue; + } + + long remainingAnimationMilliseconds = item.StopwatchTarget - elapsedStopwatchMilliseconds; + + item.FrameCount += item.FrameOffset; + + if (elapsedStopwatchMilliseconds >= item.StopwatchTarget) + { + switch (item.AnimationCycle) + { + case AnimationCycle.Once: + item.Renderer.StopAnimation(); + break; + + case AnimationCycle.Loop: + item.FrameCount = 0; + item.StopwatchTarget = elapsedStopwatchMilliseconds + item.AnimationDuration; + item.Renderer.RestartAnimation(); + break; + + case AnimationCycle.Bounce: + item.FrameOffset = -item.FrameOffset; + item.StopwatchTarget = elapsedStopwatchMilliseconds + item.AnimationDuration; + item.Renderer.RestartAnimation(); + break; + } + + _syncContext?.Post( + d: _ => item.Renderer.AnimationProc(1), + state: null); + + continue; + } + + float progress = 1 - (remainingAnimationMilliseconds / (float)item.AnimationDuration); + + _syncContext?.Post( + d: _ => item.Renderer.AnimationProc(progress), + state: null); + } + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/CheckBox/AnimatedToggleSwitchRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/CheckBox/AnimatedToggleSwitchRenderer.cs new file mode 100644 index 00000000000..e9af947ece2 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/CheckBox/AnimatedToggleSwitchRenderer.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms.Rendering.Animation; + +namespace System.Windows.Forms.Rendering.CheckBox; + +internal class AnimatedToggleSwitchRenderer : AnimatedControlRenderer +{ + private const int AnimationDuration = 300; // milliseconds + + private readonly ModernCheckBoxStyle _switchStyle; + private float? _animationProgress; + + public AnimatedToggleSwitchRenderer(Control control, ModernCheckBoxStyle switchStyle) : base(control) + { + _switchStyle = switchStyle; + } + + private Forms.CheckBox CheckBox => (Forms.CheckBox)Control; + + protected override (int animationDuration, AnimationCycle animationCycle) OnStartAnimation() + => (AnimationDuration, AnimationCycle.Once); + + public override void AnimationProc(float animationProgress) + { + _animationProgress = animationProgress; + Invalidate(); + } + + /// + /// Called from OnPaint of the control. If we only want the render animations, we need to make sure, + /// that this was triggered by AnimationProc and we know the relative progress. + /// + /// The graphics objects to render in. + public override void RenderControl(Graphics graphics) + { + int dpiScale = DpiScale; + + int switchWidth = 50 * dpiScale; + int switchHeight = 25 * dpiScale; + int circleDiameter = 20 * dpiScale; + + Size textSize = TextRenderer.MeasureText(Control.Text, Control.Font); + + int totalHeight = Math.Max(textSize.Height, switchHeight); + int switchY = (totalHeight - switchHeight) / 2; + int textY = (totalHeight - textSize.Height) / 2; + + graphics.Clear(Control.BackColor); + + switch (CheckBox.TextAlign) + { + case ContentAlignment.MiddleLeft: + case ContentAlignment.TopLeft: + case ContentAlignment.BottomLeft: + RenderSwitch(graphics, new Rectangle(textSize.Width + 10 * DpiScale, switchY, switchWidth, switchHeight), circleDiameter); + RenderText(graphics, new Point(0, textY)); + break; + + default: + RenderSwitch(graphics, new Rectangle(0, switchY, switchWidth, switchHeight), circleDiameter); + RenderText(graphics, new Point(switchWidth + 10 * DpiScale, textY)); + break; + } + } + + private void RenderText(Graphics g, Point position) => + TextRenderer.DrawText(g, CheckBox.Text, CheckBox.Font, position, CheckBox.ForeColor); + + private void RenderSwitch(Graphics g, Rectangle rect, int circleDiameter) + { + float animationProgress = 1; + + if (_animationProgress is not null) + { + // Let's make sure, we don't draw anything if the animation is not running. + if (_animationProgress == 0) + { + return; + } + + animationProgress = _animationProgress.Value; + } + + Color backgroundColor = CheckBox.Checked ^ (animationProgress < 0.8f) + ? SystemColors.Highlight + : SystemColors.ControlDark; + + Color circleColor = SystemColors.ControlLightLight; + + // This works both for when the Animation is running, and when it's not running. + // In the latter case we set the animationProgress to 1, so the circle is drawn + // at the correct position. + float circlePosition = CheckBox.Checked + ? (rect.Width - circleDiameter) * (1 - EaseOut(animationProgress)) + : (rect.Width - circleDiameter) * EaseOut(animationProgress); + + using var backgroundBrush = new SolidBrush(backgroundColor); + using var circleBrush = new SolidBrush(circleColor); + using var backgroundPen = new Pen(SystemColors.WindowFrame, 2 * DpiScale); + + g.SmoothingMode = SmoothingMode.AntiAlias; + + if (_switchStyle == ModernCheckBoxStyle.Rounded) + { + float radius = rect.Height / 2f; + + using var path = new GraphicsPath(); + path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90); + path.AddArc(rect.Right - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90); + path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90); + path.AddArc(rect.X, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90); + path.CloseFigure(); + + g.FillPath(backgroundBrush, path); + g.DrawPath(backgroundPen, path); + } + else + { + g.FillRectangle(backgroundBrush, rect); + g.DrawRectangle(backgroundPen, rect); + } + + g.FillEllipse(circleBrush, rect.X + circlePosition, rect.Y + 2.5f * DpiScale, circleDiameter, circleDiameter); + + static float EaseOut(float t) => (1 - t) * (1 - t); + } + + protected override void OnStoppedAnimation() + { + _animationProgress = null; + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/CheckBox/ModernCheckBoxStyle.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/CheckBox/ModernCheckBoxStyle.cs new file mode 100644 index 00000000000..60c95902f31 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/CheckBox/ModernCheckBoxStyle.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms.Rendering.CheckBox; + +internal enum ModernCheckBoxStyle +{ + Rectangular, + Rounded +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/DrawItemEventArgs.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/DrawItemEventArgs.cs index 5812ded87b3..e8643316e25 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/DrawItemEventArgs.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/DrawItemEventArgs.cs @@ -108,7 +108,7 @@ internal DrawItemEventArgs( public DrawItemState State { get; } /// - /// A suggested color drawing: either SystemColors.WindowText or SystemColors.HighlightText, + /// A suggested color drawing: either Application.SystemColors.WindowText or Application.SystemColors.HighlightText, /// depending on whether this item is selected. /// public Color ForeColor diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/IAnimationRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/IAnimationRenderer.cs new file mode 100644 index 00000000000..5b2a147a535 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/IAnimationRenderer.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms.Rendering; + +/// +/// Represents an animation renderer. Currently fixed set to around 40 frames per second. +/// +internal interface IAnimationRenderer : IDisposable +{ + /// + /// Triggers the animation for the specified number of frames. + /// + /// The frame number of the animation. + void TriggerAnimation(int frameCount); + + /// + /// Starts the animation with the specified parameters. + /// + void StartAnimation(); + + /// + /// Stops the animation. + /// + void StopAnimation(); +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/TextBox/AnimatedBorderStyleRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/TextBox/AnimatedBorderStyleRenderer.cs new file mode 100644 index 00000000000..cf52edbdec6 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/TextBox/AnimatedBorderStyleRenderer.cs @@ -0,0 +1,160 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms.Rendering.Animation; + +namespace System.Windows.Forms.Rendering.TextBox; + +internal class AnimatedBorderStyleRenderer : AnimatedControlRenderer +{ + private const int AnimationDuration = 200; // milliseconds + private readonly TextBoxBase _textBox; + private readonly HDC _hdc; + private Graphics? _graphics; + private float? _animationProgress; + + public AnimatedBorderStyleRenderer(Control control, HDC hdc) : base(control) + { + _textBox = (TextBoxBase)control; + _hdc = hdc; + } + + protected override (int animationDuration, AnimationCycle animationCycle) OnStartAnimation() + => (AnimationDuration, AnimationCycle.Once); + + public override void AnimationProc(float animationProgress) + { + _animationProgress = animationProgress; + + if (_graphics is null) + { + _graphics = Graphics.FromHdc(_hdc); + } + + RenderControl(_graphics, animationProgress); + } + + public void RenderControl(Graphics graphics, float animationProgress) + { + if (_animationProgress is null) + { + int borderThickness = 2; + int deflateOffset = borderThickness / 2; + int cornerRadius = 15; + + Color adornerColor = _textBox.ForeColor; + Color parentBackColor = _textBox.Parent?.BackColor ?? _textBox.BackColor; + Color clientBackColor = _textBox.BackColor; + + using Brush parentBackgroundBrush = new SolidBrush(parentBackColor); + using Brush clientBackgroundBrush = new SolidBrush(clientBackColor); + using Brush adornerBrush = new SolidBrush(adornerColor); + using Pen adornerPen = new(adornerColor, borderThickness); + using Pen focusPen = new(SystemColors.Highlight, borderThickness); + + Rectangle bounds = _textBox.Bounds; + + Rectangle clientBounds = new( + (bounds.Width - _textBox.ClientRectangle.Width) / 2, + (bounds.Height - _textBox.ClientRectangle.Height) / 2, + _textBox.ClientRectangle.Width, + _textBox.ClientRectangle.Height); + + Rectangle deflatedBounds = new( + x: bounds.Left + _textBox.Padding.Left + deflateOffset, + y: bounds.Top + _textBox.Padding.Top + deflateOffset, + width: bounds.Width - (_textBox.Padding.Horizontal + deflateOffset + deflateOffset), + height: bounds.Height - (_textBox.Padding.Vertical + deflateOffset + deflateOffset)); + + // We need Anti-Aliasing: + graphics.SmoothingMode = SmoothingMode.AntiAlias; + + // Translate the origin to the top-left corner of the control: + graphics.TranslateTransform(-bounds.Left, -bounds.Top); + + bounds.Inflate(1, 1); + + Rectangle translatedClientBounds = new( + clientBounds.Left, + clientBounds.Top, + clientBounds.Width, + clientBounds.Height); + + // Contrast to Copilot's suggestions, we need to first exclude + // the clip, then translate the origin. + graphics.ExcludeClip(translatedClientBounds); + + // Fill the background with the specified brush: + graphics.FillRectangle(parentBackgroundBrush, bounds); + + switch (_textBox.BorderStyle) + { + case BorderStyle.None: + + // Draw a rounded Rectangle with the border thickness + graphics.FillRectangle( + clientBackgroundBrush, + deflatedBounds); + + break; + + case BorderStyle.FixedSingle: + + // Draw a rounded Rectangle with the border thickness + graphics.FillRectangle( + clientBackgroundBrush, + deflatedBounds); + + // Draw a rounded Rectangle with the border thickness + graphics.DrawRectangle( + adornerPen, + deflatedBounds); + + break; + + default: + + // fill a rounded Rectangle + graphics.FillRoundedRectangle( + clientBackgroundBrush, + deflatedBounds, + new Size(cornerRadius, cornerRadius)); + + // Draw a rounded Rectangle with the border thickness + graphics.DrawRoundedRectangle( + adornerPen, + deflatedBounds, + new Size(cornerRadius, cornerRadius)); + + break; + } + + // static float EaseOut(float t) => (1 - t) * (1 - t); + + _animationProgress = animationProgress; + + // We draw an animated line at the bottom of the TextBox. That line starts of in the middle of the TextBox, + // directly on the border in an 8th of the TextBox's width. It then moves simultaneously to the left and right + // until it reaches the border on both sides. + + int lineLength = _textBox.Width / 8; + int lineStartX = (_textBox.Width - lineLength) / 2; + int lineStartY = _textBox.Height - borderThickness; + + int lineOffset = (int)(lineLength * animationProgress); + + int lineLeftX = lineStartX - lineOffset; + int lineRightX = lineStartX + lineLength + lineOffset; + + graphics.DrawLine(focusPen, lineLeftX, lineStartY, lineRightX, lineStartY); + } + } + + protected override void OnStoppedAnimation() + { + } + + public override void RenderControl(Graphics graphics) => throw new NotImplementedException(); +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/TextRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/TextRenderer.cs index 0f5f47c1a64..f85b99997e0 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/TextRenderer.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Rendering/TextRenderer.cs @@ -552,7 +552,7 @@ internal static Color DisabledTextColor(Color backColor) return SystemColors.GrayText; } - // If the color is darker than SystemColors.Control make it slightly darker, + // If the color is darker than Application.SystemColors.Control make it slightly darker, // otherwise use the standard control dark color. return ControlPaint.IsDarker(backColor, SystemColors.Control) @@ -632,15 +632,17 @@ internal static ApplyGraphicsProperties GetApplyStateFlags(IDeviceContext device // translation (rotation for example), and this likely impacted the decision to only have a translation // flag when this was originally written. - Debug.Assert(apply.HasFlag(ApplyGraphicsProperties.Clipping) - || graphics.Clip is null - || graphics.Clip.GetHrgn(graphics) == IntPtr.Zero, - "Must preserve Graphics clipping region!"); + // Disabled. We are getting this pretty much whenever we call a DrawText. - Debug.Assert(apply.HasFlag(ApplyGraphicsProperties.TranslateTransform) - || graphics.Transform is null - || graphics.Transform.IsIdentity, - "Must preserve Graphics transformation!"); + // Debug.Assert(apply.HasFlag(ApplyGraphicsProperties.Clipping) + // || graphics.Clip is null + // || graphics.Clip.GetHrgn(graphics) == IntPtr.Zero, + // "Must preserve Graphics clipping region!"); + + // Debug.Assert(apply.HasFlag(ApplyGraphicsProperties.TranslateTransform) + // || graphics.Transform is null + // || graphics.Transform.IsIdentity, + // "Must preserve Graphics transformation!"); } #endif diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/VisualStyles/VisualStyleRenderer.ThemeHandle.cs b/src/System.Windows.Forms/src/System/Windows/Forms/VisualStyles/VisualStyleRenderer.ThemeHandle.cs new file mode 100644 index 00000000000..cbe4e6e2b0c --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/VisualStyles/VisualStyleRenderer.ThemeHandle.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms.VisualStyles; + +public sealed partial class VisualStyleRenderer +{ + // This wrapper class is needed for safely cleaning up TLS cache of handles. + private class ThemeHandle : IDisposable, IHandle + { + private ThemeHandle(HTHEME hTheme) + { + Handle = hTheme; + } + + public HTHEME Handle { get; private set; } + + public static ThemeHandle? Create(string className, bool throwExceptionOnFail) + { + return Create(className, throwExceptionOnFail, HWND.Null); + } + + internal static ThemeHandle? Create(string className, bool throwExceptionOnFail, HWND hWndRef) + { + // HThemes require an HWND when display scaling is different between monitors. + HTHEME hTheme = PInvoke.OpenThemeData(hWndRef, className); + + return hTheme.IsNull + ? throwExceptionOnFail ? throw new InvalidOperationException(SR.VisualStyleHandleCreationFailed) : null + : new ThemeHandle(hTheme); + } + + public void Dispose() + { + if (!Handle.IsNull) + { + PInvoke.CloseThemeData(Handle); + Handle = HTHEME.Null; + } + + GC.SuppressFinalize(this); + } + + ~ThemeHandle() => Dispose(); + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/VisualStyles/VisualStyleRenderer.cs b/src/System.Windows.Forms/src/System/Windows/Forms/VisualStyles/VisualStyleRenderer.cs index ccd8b5146df..206b92fe34d 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/VisualStyles/VisualStyleRenderer.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/VisualStyles/VisualStyleRenderer.cs @@ -10,7 +10,7 @@ namespace System.Windows.Forms.VisualStyles; /// /// This class provides full feature parity with UxTheme API. /// -public sealed class VisualStyleRenderer : IHandle +public sealed partial class VisualStyleRenderer : IHandle { private HRESULT _lastHResult; private const int NumberOfPossibleClasses = VisualStyleElement.Count; // used as size for themeHandles @@ -867,43 +867,4 @@ private static PInvoke.OpenThemeDataScope OpenThemeData(HWND hwnd, string classL PInvoke.OpenThemeDataScope htheme = new(hwnd, classList); return htheme.IsNull ? throw new InvalidOperationException(SR.VisualStyleHandleCreationFailed) : htheme; } - - // This wrapper class is needed for safely cleaning up TLS cache of handles. - private class ThemeHandle : IDisposable, IHandle - { - private ThemeHandle(HTHEME hTheme) - { - Handle = hTheme; - } - - public HTHEME Handle { get; private set; } - - public static ThemeHandle? Create(string className, bool throwExceptionOnFail) - { - return Create(className, throwExceptionOnFail, HWND.Null); - } - - internal static ThemeHandle? Create(string className, bool throwExceptionOnFail, HWND hWndRef) - { - // HThemes require an HWND when display scaling is different between monitors. - HTHEME hTheme = PInvoke.OpenThemeData(hWndRef, className); - - return hTheme.IsNull - ? throwExceptionOnFail ? throw new InvalidOperationException(SR.VisualStyleHandleCreationFailed) : null - : new ThemeHandle(hTheme); - } - - public void Dispose() - { - if (!Handle.IsNull) - { - PInvoke.CloseThemeData(Handle); - Handle = HTHEME.Null; - } - - GC.SuppressFinalize(this); - } - - ~ThemeHandle() => Dispose(); - } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/VisualStylesMode.cs b/src/System.Windows.Forms/src/System/Windows/Forms/VisualStylesMode.cs new file mode 100644 index 00000000000..6a1ce14e590 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/VisualStylesMode.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms; + +/// +/// Represents the version of the visual renderer. +/// +[Experimental("WFO9000")] +public enum VisualStylesMode : short +{ + /// + /// Visual renderers are not in use. See ; Controls are based on Version 5 of ComCtl. + /// + Disabled = 0, + + /// + /// The classic version of the visual renderer (.NET 8 and earlier), using the Version 6 of ComCtl. + /// + Classic = 1, + + /// + /// The latest version of the visual renderer. Controls are rendered using the latest version + /// of ComCtl and customized adorner rendering and layouting in addition. + /// + Latest = 2 +} diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/StatusStripTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/StatusStripTests.cs index 375900906f9..50701b32b3d 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/StatusStripTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/StatusStripTests.cs @@ -121,8 +121,11 @@ public void StatusStrip_Ctor_Default() Assert.Null(control.Region); Assert.NotNull(control.Renderer); Assert.Same(control.Renderer, control.Renderer); - Assert.IsType(control.Renderer); - Assert.Equal(ToolStripRenderMode.System, control.RenderMode); + + // TODO: Assume control.Renderer can be either ToolStripSystemRenderer or ToolStripProfessionalRenderer for the Moment. + Assert.True(control.Renderer is ToolStripSystemRenderer or ToolStripProfessionalRenderer, "Renderer is not one of the expected types."); + Assert.True(control.RenderMode is ToolStripRenderMode.System or ToolStripRenderMode.ManagerRenderMode); + Assert.True(control.ResizeRedraw); Assert.Equal(200, control.Right); Assert.Equal(RightToLeft.No, control.RightToLeft);