File: System\Windows\Forms\ParentBackgroundRenderer.cs
Web Access
Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms)
// 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;
 
namespace System.Windows.Forms;
 
internal static class ParentBackgroundRenderer
{
    internal static void Paint(
        Control control,
        Graphics graphics,
        Rectangle bounds,
        Color fallbackColor)
    {
        ArgumentNullException.ThrowIfNull(control);
        ArgumentNullException.ThrowIfNull(graphics);
 
        using GraphicsStateScope state = new(graphics);
        using Region paintRegion = new(bounds);
 
        // Keep an existing clip (for example, the native TextBox client area) in effect while the
        // parent background is painted beneath the complete antialiased control body.
        using Region currentClip = graphics.Clip;
        paintRegion.Intersect(currentClip);
 
        Control? parent = control.ParentInternal;
        if (parent is null || parent.IsDisposed)
        {
            using var fallbackBrush = fallbackColor.GetCachedSolidBrushScope();
            graphics.FillRegion(fallbackBrush, paintRegion);
            return;
        }
 
        using PaintEventArgs paintEventArgs = new(graphics, bounds);
        control.PaintTransparentBackground(paintEventArgs, bounds, paintRegion);
    }
 
    /// <summary>
    ///  Draws a two-pixel line in the parent background color immediately outside the rounded
    ///  border described by <paramref name="borderBounds"/> and <paramref name="cornerRadius"/>.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   The modern rounded chrome masks its corners with a <see cref="Region"/>, which cannot be
    ///   anti-aliased. That leaves jagged pixels just outside the anti-aliased border where the
    ///   region-clipped parent fill meets the control body. Tracing a short, anti-aliased line in
    ///   the parent's background color over that band blends the artifact into the parent.
    ///  </para>
    /// </remarks>
    internal static void PaintRoundedBorderRegionMitigation(
        Graphics graphics,
        Rectangle borderBounds,
        Size cornerRadius,
        int borderThickness,
        Color parentBackColor)
    {
        ArgumentNullException.ThrowIfNull(graphics);
 
        if (borderBounds.Width <= 0 || borderBounds.Height <= 0)
        {
            return;
        }
 
        const int MitigationThickness = 2;
 
        // Offset the outline so the two-pixel stroke sits just outside the border's outer edge.
        int outset = (borderThickness / 2) + 1;
 
        Rectangle outerBounds = borderBounds;
        outerBounds.Inflate(outset, outset);
 
        Size outerRadius = new(
            cornerRadius.Width + outset,
            cornerRadius.Height + outset);
 
        using GraphicsStateScope state = new(graphics);
        graphics.SmoothingMode = SmoothingMode.AntiAlias;
 
        using GraphicsPath outline = new();
        outline.AddRoundedRectangle(outerBounds, outerRadius);
 
        using var pen = parentBackColor.GetCachedPenScope(MitigationThickness);
        graphics.DrawPath(pen, outline);
    }
}