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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,35 @@ namespace System.Windows.Forms.Primitives
// Borrowed from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs
internal static partial class LocalAppContextSwitches
{
// Switch names declared internal below are used in unit/integration tests. Refer to
// https://github.com/microsoft/winforms/blob/tree/main/docs/design/anchor_layout_changes_in_net80.md

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing name to anchor-layout-changes-in-net80.md when renaming it on doc PR. Intentionally underscored here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗ This link is still incorrect.

// for more details on how to enable these switches in the application.
private const string ScaleTopLevelFormMinMaxSizeForDpiSwitchName = "System.Windows.Forms.ScaleTopLevelFormMinMaxSizeForDpi";
internal const string AnchorLayoutV2SwitchName = "System.Windows.Forms.AnchorLayoutV2";

private static int s_scaleTopLevelFormMinMaxSizeForDpi;
private static int s_AnchorLayoutV2;

public static bool ScaleTopLevelFormMinMaxSizeForDpi
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue(ScaleTopLevelFormMinMaxSizeForDpiSwitchName, ref s_scaleTopLevelFormMinMaxSizeForDpi);
}

/// <summary>
/// Indicates whether AnchorLayoutV2 feature is enabled.
/// </summary>
/// <devdoc>
/// Returns AnchorLayoutV2 switch value from runtimeconfig.json. Defaults to true if application is targeting .NET 8.0 and beyond.
Comment thread
dreddy-work marked this conversation as resolved.
/// Refer to
/// https://github.com/microsoft/winforms/blob/tree/main/docs/design/anchor_layout_changes_in_net80.md for more details.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diito

/// </devdoc>
public static bool AnchorLayoutV2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue(AnchorLayoutV2SwitchName, ref s_AnchorLayoutV2);
}

private static readonly FrameworkName? s_targetFrameworkName = GetTargetFrameworkName();

private static readonly bool s_isNetCoreApp = (s_targetFrameworkName?.Identifier) == ".NETCoreApp";
Expand All @@ -33,9 +53,14 @@ private static bool GetCachedSwitchValue(string switchName, ref int cachedSwitch
{
// The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false
if (cachedSwitchValue < 0)
Comment thread
Tanya-Solyanik marked this conversation as resolved.
{
return false;
}

if (cachedSwitchValue > 0)
{
return true;
}

return GetSwitchValue(switchName, ref cachedSwitchValue);
}
Expand Down Expand Up @@ -64,6 +89,8 @@ static bool GetSwitchDefaultValue(string switchName)
return false;
}

// We are introducing switch defaults in .NET 8.0+ and support matrix for this product is
// limited to Windows 10 and above versions.
if (OsVersion.IsWindows10_1703OrGreater())
{
if (s_targetFrameworkName!.Version.CompareTo(new Version("8.0")) >= 0)
Expand All @@ -72,6 +99,11 @@ static bool GetSwitchDefaultValue(string switchName)
{
return true;
}

if (switchName == AnchorLayoutV2SwitchName)
{
Comment thread
dreddy-work marked this conversation as resolved.
return true;
}
}
}

Expand Down
31 changes: 25 additions & 6 deletions src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Text;
using System.Windows.Forms.Automation;
using System.Windows.Forms.Layout;
using System.Windows.Forms.Primitives;
using Microsoft.Win32;
using static Interop;
using Encoding = System.Text.Encoding;
Expand Down Expand Up @@ -4686,6 +4687,7 @@ internal virtual void AssignParent(Control? value)
OnParentChanged(EventArgs.Empty);
}

UpdateAnchorsIfRequired();
SetState(States.CheckedHost, false);
ParentInternal?.LayoutEngine.InitLayout(this, BoundsSpecified.All);
}
Expand Down Expand Up @@ -7714,8 +7716,7 @@ internal virtual void OnParentBecameInvisible()
{
for (int i = 0; i < controlsCollection.Count; i++)
{
Control ctl = controlsCollection[i];
ctl.OnParentBecameInvisible();
controlsCollection[i].OnParentBecameInvisible();
}
}
}
Expand Down Expand Up @@ -10210,8 +10211,9 @@ but we break things at every step.
{
for (int i = 0; i < controlsCollection.Count; i++)
{
LayoutEngine.InitLayout(controlsCollection[i], BoundsSpecified.All);
CommonProperties.xClearPreferredSizeCache(controlsCollection[i]);
Control control = controlsCollection[i];
Comment thread
dreddy-work marked this conversation as resolved.
LayoutEngine.InitLayout(control, BoundsSpecified.All);
CommonProperties.xClearPreferredSizeCache(control);
}
}
}
Expand Down Expand Up @@ -10583,10 +10585,15 @@ protected virtual void ScaleControl(SizeF factor, BoundsSpecified specified)
Size scaledSize = LayoutUtils.IntersectSizes(rawScaledBounds.Size, maximumSize);
scaledSize = LayoutUtils.UnionSizes(scaledSize, minSize);

if (DpiHelper.IsScalingRequirementMet && (ParentInternal is not null) && (ParentInternal.LayoutEngine == DefaultLayout.Instance))
if (DpiHelper.IsScalingRequirementMet
// In the v2 layout, anchors are updated/computed after the controls bounds changed
// and, thus, don't need scaling.
&& !DefaultLayout.UseAnchorLayoutV2(this)
&& ParentInternal is { } parent
&& (parent.LayoutEngine == DefaultLayout.Instance))
{
// We need to scale AnchorInfo to update distances to container edges
DefaultLayout.ScaleAnchorInfo((IArrangedElement)this, factor);
DefaultLayout.ScaleAnchorInfo(this, factor);
}

// Set in the scaled bounds as constrained by the newly scaled min/max size.
Expand Down Expand Up @@ -10808,6 +10815,16 @@ public void SetBounds(int x, int y, int width, int height)
}
}

private void UpdateAnchorsIfRequired()
{
if (!LocalAppContextSwitches.AnchorLayoutV2)
{
return;
}

DefaultLayout.UpdateAnchorInfoV2(this);
}

/// <summary>
/// Sets the bounds of the control.
/// </summary>
Expand Down Expand Up @@ -10837,6 +10854,7 @@ public void SetBounds(int x, int y, int width, int height, BoundsSpecified speci
_height != height)
{
SetBoundsCore(x, y, width, height, specified);
UpdateAnchorsIfRequired();

// WM_WINDOWPOSCHANGED will trickle down to an OnResize() which will
// have refreshed the interior layout or the resized control. We only need to layout
Expand Down Expand Up @@ -12195,6 +12213,7 @@ private void WmCreate(ref Message m)
_parent?.UpdateChildZOrder(this);

UpdateBounds();
UpdateAnchorsIfRequired();

// Let any interested sites know that we've now created a handle
OnHandleCreated(EventArgs.Empty);
Expand Down
Loading