Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/runtime-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ For example, the content of `[appname].runtimeconfig.json` generated from above
}
```

#### Enabling modern rendering for composite controls

`ToolStrip`, `MenuStrip`, and `DataGridView` use Classic rendering when `VisualStylesMode`
is `Net11` or `Latest` by default. Applications can opt in to modern rendering with these
switches:

```json
{
"configProperties": {
"System.Windows.Forms.ToolStripModernRendering": true,
"System.Windows.Forms.DataGridViewModernRendering": true
}
}
```

`System.Windows.Forms.ToolStripModernRendering` controls both `ToolStrip` and `MenuStrip`.
Both switches default to `false`.

The target framework information added to `[appname].runtimeconfig.json` file always match with the application target framework irrespective of runtime/SDK installed on the machine or runtime used ([roll-forward scenarios](https://docs.microsoft.com/dotnet/core/versions/selection#framework-dependent-apps-roll-forward)) by the Windows Forms application.

#### .NET runtime support in reading `runtimeconfig.json`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ internal static partial class LocalAppContextSwitches
internal const string TreeNodeCollectionAddRangeRespectsSortOrderSwitchName = "System.Windows.Forms.TreeNodeCollectionAddRangeRespectsSortOrder";
internal const string MoveTreeViewTextLocationOnePixelSwitchName = "System.Windows.Forms.TreeView.MoveTreeViewTextLocationOnePixel";
internal const string DataGridViewDarkModeThemingSwitchName = "System.Windows.Forms.DataGridViewDarkModeTheming";
internal const string ToolStripModernRenderingSwitchName = "System.Windows.Forms.ToolStripModernRendering";
internal const string DataGridViewModernRenderingSwitchName = "System.Windows.Forms.DataGridViewModernRendering";

private static int s_scaleTopLevelFormMinMaxSizeForDpi;
private static int s_anchorLayoutV2;
Expand All @@ -41,6 +43,8 @@ internal static partial class LocalAppContextSwitches

private static int s_moveTreeViewTextLocationOnePixel;
private static int s_dataGridViewDarkModeTheming;
private static int s_toolStripModernRendering;
private static int s_dataGridViewModernRendering;

private static FrameworkName? s_targetFrameworkName;

Expand Down Expand Up @@ -254,4 +258,24 @@ public static bool DataGridViewDarkModeTheming
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue(DataGridViewDarkModeThemingSwitchName, ref s_dataGridViewDarkModeTheming);
}

/// <summary>
/// Indicates whether ToolStrip controls, including MenuStrip, support modern visual styles.
/// Defaults to <see langword="false"/>.
/// </summary>
public static bool ToolStripModernRendering
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue(ToolStripModernRenderingSwitchName, ref s_toolStripModernRendering);
}

/// <summary>
/// Indicates whether DataGridView controls support modern visual styles.
/// Defaults to <see langword="false"/>.
/// </summary>
public static bool DataGridViewModernRendering
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue(DataGridViewModernRenderingSwitchName, ref s_dataGridViewModernRendering);
}
}
97 changes: 83 additions & 14 deletions src/System.Windows.Forms/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -924,18 +924,30 @@ private bool ShouldSerializeVisualStylesMode()
private void ResetVisualStylesMode()
=> VisualStylesMode = VisualStylesMode.Inherit;

private VisualStylesMode ResolvedVisualStylesMode
private VisualStylesMode UncoercedVisualStylesMode
{
get
{
VisualStylesMode value = VisualStylesMode;

return value == VisualStylesMode.Inherit
? ParentInternal?.ResolvedVisualStylesMode ?? DefaultVisualStylesMode
? ParentInternal?.UncoercedVisualStylesMode ?? DefaultVisualStylesMode
: value;
}
}

private VisualStylesMode ResolvedVisualStylesMode
=> GetSupportedVisualStylesMode(
VisualStylesMode == VisualStylesMode.Inherit
? ParentInternal?.ResolvedVisualStylesMode ?? DefaultVisualStylesMode
: VisualStylesMode);

/// <summary>
/// Coerces a requested visual styles mode to one supported by this control.
/// </summary>
private protected virtual VisualStylesMode GetSupportedVisualStylesMode(VisualStylesMode mode)
=> mode;

/// <summary>
/// Gets the renderer-authoritative <see cref="Forms.VisualStylesMode"/> that controls must honor when deciding
/// <see cref="CreateParams"/> or paint behavior, after applying the High Contrast and disabled clamps.
Expand Down Expand Up @@ -7443,20 +7455,62 @@ protected virtual void OnParentVisualStylesModeChanged(EventArgs e)
return;
}

VisualStylesModeChangeEventArgs? transition = e as VisualStylesModeChangeEventArgs;
if (transition is not null
&& (!transition.IsCurrent
|| ParentInternal?.EffectiveVisualStylesMode != transition.NewEffectiveVisualStylesMode))
{
return;
}

if (Properties.ContainsKey(s_visualStylesModeProperty))
{
// A local value isolates this subtree from parent changes.
if (ParentInternal is not { } parent
|| Properties.GetValueOrDefault<VisualStylesMode>(s_visualStylesModeProperty)
!= parent.UncoercedVisualStylesMode
|| parent.UncoercedVisualStylesMode == parent.EffectiveVisualStylesMode)
{
Comment thread
SimonZhao888 marked this conversation as resolved.
// A local value isolates this subtree from parent changes.
return;
}

VisualStylesMode oldEffectiveVisualStylesMode = EffectiveVisualStylesMode;

// Inherit the parent's coerced mode when the local value matches its uncoerced mode.
Properties.RemoveValue(s_visualStylesModeProperty);

VisualStylesMode newEffectiveVisualStylesMode = EffectiveVisualStylesMode;
if (oldEffectiveVisualStylesMode == newEffectiveVisualStylesMode)
{
return;
}

OnVisualStylesModeChanged(
transition?.CreateForControl(
this,
oldEffectiveVisualStylesMode,
newEffectiveVisualStylesMode)
?? e);

return;
}

if (e is VisualStylesModeChangeEventArgs transition
&& (!transition.IsCurrent
|| ParentInternal?.EffectiveVisualStylesMode != transition.NewEffectiveVisualStylesMode))
if (transition is not null)
{
VisualStylesModeChangeEventArgs transitionForControl = transition.CreateForControl(this);
if (transitionForControl.OldEffectiveVisualStylesMode
!= transitionForControl.NewEffectiveVisualStylesMode)
{
OnVisualStylesModeChanged(transitionForControl);
}
else if (ChildControls is { } children)
{
CascadeVisualStylesModeChanged(children, transitionForControl, transitionForControl);
}

return;
}

// In every other case we're going to raise the event.
OnVisualStylesModeChanged(e);
}

Expand Down Expand Up @@ -7523,19 +7577,34 @@ public bool IsCurrent

public VisualStylesModeChangeEventArgs CreateForControl(Control control)
{
if (SystemVisualSettingsTransition is not { } systemVisualSettingsTransition)
if (SystemVisualSettingsTransition is { } systemVisualSettingsTransition)
{
return this;
return new(
_state,
control,
control.Properties.GetValueOrDefault(s_visualStylesModeChangeVersionProperty, 0),
control.GetEffectiveVisualStylesMode(systemVisualSettingsTransition.OldSettings.HighContrastEnabled),
control.GetEffectiveVisualStylesMode(systemVisualSettingsTransition.NewSettings.HighContrastEnabled),
systemVisualSettingsTransition);
}

return new(
return CreateForControl(
control,
control.GetSupportedVisualStylesMode(OldEffectiveVisualStylesMode),
control.GetSupportedVisualStylesMode(NewEffectiveVisualStylesMode));
}

public VisualStylesModeChangeEventArgs CreateForControl(
Control control,
VisualStylesMode oldEffectiveVisualStylesMode,
VisualStylesMode newEffectiveVisualStylesMode)
=> new(
_state,
control,
control.Properties.GetValueOrDefault(s_visualStylesModeChangeVersionProperty, 0),
control.GetEffectiveVisualStylesMode(systemVisualSettingsTransition.OldSettings.HighContrastEnabled),
control.GetEffectiveVisualStylesMode(systemVisualSettingsTransition.NewSettings.HighContrastEnabled),
systemVisualSettingsTransition);
}
oldEffectiveVisualStylesMode,
newEffectiveVisualStylesMode,
systemVisualSettingsTransition: null);

public void PerformLayouts()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ namespace System.Windows.Forms;
[SRDescription(nameof(SR.DescriptionDataGridView))]
public partial class DataGridView : Control, ISupportInitialize
{
private protected override VisualStylesMode GetSupportedVisualStylesMode(VisualStylesMode mode)
=> mode < VisualStylesMode.Net11 || AppContextSwitches.DataGridViewModernRendering
? mode
: VisualStylesMode.Classic;

private static readonly object s_allowUserToAddRowsChangedEvent = new();
private static readonly object s_allowUserToDeleteRowsChangedEvent = new();
private static readonly object s_allowUserToOrderColumnsChangedEvent = new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ namespace System.Windows.Forms;
[DefaultEvent(nameof(ItemClicked))]
public partial class ToolStrip : ScrollableControl, IArrangedElement, ISupportToolStripPanel
{
private protected override VisualStylesMode GetSupportedVisualStylesMode(VisualStylesMode mode)
=> mode < VisualStylesMode.Net11 || AppContextSwitches.ToolStripModernRendering
? mode
: VisualStylesMode.Classic;

private static Size s_onePixel = new(1, 1);
internal static Point s_invalidMouseEnter = new(int.MaxValue, int.MaxValue);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,33 @@ public void Control_VisualStylesMode_ReentrantChange_SuppressesStaleChildCascade
Assert.Equal(1, childChangedCallCount);
}

[WinFormsFact]
public void Control_VisualStylesMode_LocalOverrideRemovedByCoercedParent_RaisesChangedAndCascades()
{
using DataGridView parent = new() { VisualStylesMode = VisualStylesMode.Disabled };
using SubControlWithVisualStyles child = new()
{
HighContrast = false,
VisualStylesMode = VisualStylesMode.Latest
};
using SubControlWithVisualStyles grandchild = new() { HighContrast = false };
child.Controls.Add(grandchild);
parent.Controls.Add(child);

int childChangedCallCount = 0;
child.VisualStylesModeChanged += (sender, e) => childChangedCallCount++;
int grandchildChangedCallCount = 0;
grandchild.VisualStylesModeChanged += (sender, e) => grandchildChangedCallCount++;

parent.VisualStylesMode = VisualStylesMode.Latest;

Assert.Equal(VisualStylesMode.Inherit, child.VisualStylesMode);
Assert.Equal(VisualStylesMode.Classic, child.EffectiveVisualStylesModeAccessor);
Assert.Equal(VisualStylesMode.Classic, grandchild.EffectiveVisualStylesModeAccessor);
Assert.Equal(1, childChangedCallCount);
Assert.Equal(1, grandchildChangedCallCount);
}

[WinFormsFact]
public void Appearance_ToggleSwitch_HasExpectedValue()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,80 @@ public void DataGridView_Ctor_Default()
Assert.Same(control.RowTemplate, control.RowTemplate);
}

[WinFormsTheory]
[InlineData(VisualStylesMode.Net11)]
[InlineData(VisualStylesMode.Latest)]
public void DataGridView_VisualStylesMode_ModernMode_UsesClassicEffectiveMode(VisualStylesMode value)
{
using AppContextSwitchScope scope = new(
WinFormsAppContextSwitchNames.DataGridViewModernRendering,
enable: false);
using SubDataGridView control = new() { VisualStylesMode = value };

Assert.Equal(value, control.VisualStylesMode);
Assert.Equal(VisualStylesMode.Classic, control.EffectiveVisualStylesModeAccessor);
Assert.False(control.IsHandleCreated);
}

[WinFormsTheory]
[InlineData(VisualStylesMode.Net11)]
[InlineData(VisualStylesMode.Latest)]
public void DataGridView_VisualStylesMode_ModernRenderingEnabled_UsesRequestedEffectiveMode(
VisualStylesMode value)
{
using AppContextSwitchScope scope = new(
WinFormsAppContextSwitchNames.DataGridViewModernRendering,
enable: true);
using SubDataGridView control = new() { VisualStylesMode = value };

Assert.Equal(value, control.VisualStylesMode);
Assert.Equal(value, control.EffectiveVisualStylesModeAccessor);
Assert.False(control.IsHandleCreated);
}

[WinFormsTheory]
[InlineData(VisualStylesMode.Net11)]
[InlineData(VisualStylesMode.Latest)]
public void DataGridView_VisualStylesMode_InheritedModernMode_UsesClassicEffectiveMode(VisualStylesMode value)
{
using Control parent = new() { VisualStylesMode = value };
using SubDataGridView control = new();
parent.Controls.Add(control);

Assert.Equal(VisualStylesMode.Inherit, control.VisualStylesMode);
Assert.Equal(VisualStylesMode.Classic, control.EffectiveVisualStylesModeAccessor);
Assert.False(control.IsHandleCreated);
}

[WinFormsFact]
public void DataGridView_VisualStylesMode_ParentChangesToModernMode_DoesNotRaiseChanged()
{
using Control parent = new() { VisualStylesMode = VisualStylesMode.Classic };
using SubDataGridView control = new();
parent.Controls.Add(control);
int callCount = 0;
control.VisualStylesModeChanged += (sender, e) => callCount++;

parent.VisualStylesMode = VisualStylesMode.Net11;

Assert.Equal(VisualStylesMode.Classic, control.EffectiveVisualStylesModeAccessor);
Assert.Equal(0, callCount);
}

[WinFormsFact]
public void DataGridView_VisualStylesMode_ExplicitClassic_RemainsLocalOverride()
{
using Control parent = new() { VisualStylesMode = VisualStylesMode.Net11 };
using SubDataGridView control = new();
parent.Controls.Add(control);
control.VisualStylesMode = VisualStylesMode.Classic;

parent.VisualStylesMode = VisualStylesMode.Disabled;

Assert.Equal(VisualStylesMode.Classic, control.VisualStylesMode);
Assert.Equal(VisualStylesMode.Classic, control.EffectiveVisualStylesModeAccessor);
}

private const int DefaultColumnHeadersHeight = 23;

public static IEnumerable<object[]> ColumnHeadersHeight_Set_TestData()
Expand Down Expand Up @@ -2892,6 +2966,8 @@ private class SubDataGridViewCell : DataGridViewCell

private class SubDataGridView : DataGridView
{
public VisualStylesMode EffectiveVisualStylesModeAccessor => base.EffectiveVisualStylesMode;

public new void OnColumnHeadersHeightChanged(EventArgs e) => base.OnColumnHeadersHeightChanged(e);

public new void OnColumnHeadersHeightSizeModeChanged(DataGridViewAutoSizeModeEventArgs e) => base.OnColumnHeadersHeightSizeModeChanged(e);
Expand Down
Loading