| File: System\Windows\Forms\Controls\Labels\LinkUtilities.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.Globalization; using Microsoft.Win32; namespace System.Windows.Forms; internal static class LinkUtilities { // IE fonts and colors private static Color s_ielinkColor = Color.Empty; private static Color s_ieactiveLinkColor = Color.Empty; private static Color s_ievisitedLinkColor = Color.Empty; private const string IESettingsRegPath = "Software\\Microsoft\\Internet Explorer\\Settings"; public const string IEMainRegPath = "Software\\Microsoft\\Internet Explorer\\Main"; private const string IEAnchorColor = "Anchor Color"; private const string IEAnchorColorVisited = "Anchor Color Visited"; private const string IEAnchorColorHover = "Anchor Color Hover"; /// <summary> /// Retrieves a named IE color from the registry. There are constants at the top /// of this file of the valid names to retrieve. /// </summary> private static Color GetIEColor(string name) { RegistryKey? key = Registry.CurrentUser.OpenSubKey(IESettingsRegPath); if (key is not null) { // Since this comes from the registry, be very careful about its contents. string? s = (string?)key.GetValue(name); key.Close(); if (s is not null) { Span<Range> rgbs = stackalloc Range[4]; int rgbsCount = s.AsSpan().Split(rgbs, ','); Span<int> rgb = stackalloc int[3]; rgb.Clear(); int nMax = Math.Min(rgb.Length, rgbsCount); // NOTE: if we can't parse rgbs[i], rgb[i] will be set to 0. for (int i = 0; i < nMax; i++) { int.TryParse(s.AsSpan(rgbs[i]), out rgb[i]); } return Color.FromArgb(rgb[0], rgb[1], rgb[2]); } } if (string.Equals(name, IEAnchorColor, StringComparison.OrdinalIgnoreCase)) { return Color.Blue; } else if (string.Equals(name, IEAnchorColorVisited, StringComparison.OrdinalIgnoreCase)) { return Color.Purple; } else if (string.Equals(name, IEAnchorColorHover, StringComparison.OrdinalIgnoreCase)) { return Color.Red; } else { return Color.Red; } } public static Color IELinkColor { get { if (s_ielinkColor.IsEmpty) { s_ielinkColor = GetIEColor(IEAnchorColor); } return s_ielinkColor; } } public static Color IEActiveLinkColor { get { if (s_ieactiveLinkColor.IsEmpty) { s_ieactiveLinkColor = GetIEColor(IEAnchorColorHover); } return s_ieactiveLinkColor; } } public static Color IEVisitedLinkColor { get { if (s_ievisitedLinkColor.IsEmpty) { s_ievisitedLinkColor = GetIEColor(IEAnchorColorVisited); } return s_ievisitedLinkColor; } } /// <summary> /// Gets a light blue link color suitable for dark mode backgrounds. /// </summary> public static Color DarkModeLinkColor => Color.FromArgb(102, 178, 255); /// <summary> /// Gets a light red active link color suitable for dark mode backgrounds. /// </summary> public static Color DarkModeActiveLinkColor => Color.FromArgb(255, 128, 128); /// <summary> /// Gets a light purple visited link color suitable for dark mode backgrounds. /// </summary> public static Color DarkModeVisitedLinkColor => Color.FromArgb(200, 162, 255); /// Produces a color for visited links using SystemColors public static Color GetVisitedLinkColor() { int r = (SystemColors.Window.R + SystemColors.WindowText.R + 1) / 2; int g = SystemColors.WindowText.G; int b = (SystemColors.Window.B + SystemColors.WindowText.B + 1) / 2; return Color.FromArgb(r, g, b); } /// <summary> /// Retrieves the IE settings for link behavior from the registry. /// </summary> public static LinkBehavior GetIELinkBehavior() { RegistryKey? key = null; try { key = Registry.CurrentUser.OpenSubKey(IEMainRegPath); } catch (Security.SecurityException) { // User does not have right to access Registry path HKCU\\Software\\Microsoft\\Internet Explorer\\Main. // Catch SecurityException silently and let the return value fallback to AlwaysUnderline. } if (key is not null) { string? s = (string?)key.GetValue("Anchor Underline"); key.Close(); if (s is not null && string.Compare(s, "no", true, CultureInfo.InvariantCulture) == 0) { return LinkBehavior.NeverUnderline; } if (s is not null && string.Compare(s, "hover", true, CultureInfo.InvariantCulture) == 0) { return LinkBehavior.HoverUnderline; } else { return LinkBehavior.AlwaysUnderline; } } return LinkBehavior.AlwaysUnderline; } public static void EnsureLinkFonts( Font baseFont, LinkBehavior link, [AllowNull] ref Font linkFont, [AllowNull] ref Font hoverLinkFont) { if (linkFont is not null && hoverLinkFont is not null) { return; } bool underlineLink = true; bool underlineHover = true; if (link == LinkBehavior.SystemDefault) { link = GetIELinkBehavior(); } switch (link) { case LinkBehavior.AlwaysUnderline: underlineLink = true; underlineHover = true; break; case LinkBehavior.HoverUnderline: underlineLink = false; underlineHover = true; break; case LinkBehavior.NeverUnderline: underlineLink = false; underlineHover = false; break; } Font f = baseFont; // We optimize for the "same" value (never & always) to avoid creating an extra font object. if (underlineHover == underlineLink) { FontStyle style = f.Style; if (underlineHover) { style |= FontStyle.Underline; } else { style &= ~FontStyle.Underline; } hoverLinkFont = new Font(f, style); linkFont = hoverLinkFont; } else { FontStyle hoverStyle = f.Style; if (underlineHover) { hoverStyle |= FontStyle.Underline; } else { hoverStyle &= ~FontStyle.Underline; } hoverLinkFont = new Font(f, hoverStyle); FontStyle linkStyle = f.Style; if (underlineLink) { linkStyle |= FontStyle.Underline; } else { linkStyle &= ~FontStyle.Underline; } linkFont = new Font(f, linkStyle); } } }