|
// 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.Windows.Forms.VisualStyles;
namespace System.Windows.Forms;
/// <summary>
/// Provides methods used to render a radio button control with or without visual styles.
/// </summary>
public static class RadioButtonRenderer
{
// Make this per-thread, so that different threads can safely use these methods.
[ThreadStatic]
private static VisualStyleRenderer? t_visualStyleRenderer;
private static readonly VisualStyleElement s_radioElement = VisualStyleElement.Button.RadioButton.UncheckedNormal;
/// <inheritdoc cref="ButtonRenderer.RenderMatchingApplicationState"/>
public static bool RenderMatchingApplicationState { get; set; } = true;
private static bool RenderWithVisualStyles => !RenderMatchingApplicationState || Application.RenderWithVisualStyles;
/// <inheritdoc cref="ButtonRenderer.IsBackgroundPartiallyTransparent(PushButtonState)"/>
public static bool IsBackgroundPartiallyTransparent(RadioButtonState state)
{
if (RenderWithVisualStyles)
{
InitializeRenderer((int)state);
return t_visualStyleRenderer.IsBackgroundPartiallyTransparent();
}
else
{
return false;
}
}
/// <inheritdoc cref="ButtonRenderer.DrawParentBackground(Graphics, Rectangle, Control)"/>
public static void DrawParentBackground(Graphics g, Rectangle bounds, Control childControl)
{
if (RenderWithVisualStyles)
{
InitializeRenderer(0);
t_visualStyleRenderer.DrawParentBackground(g, bounds, childControl);
}
}
/// <inheritdoc cref="DrawRadioButton(Graphics, Point, Rectangle, string?, Font?, TextFormatFlags, Image, Rectangle, bool, RadioButtonState)" />
public static void DrawRadioButton(Graphics g, Point glyphLocation, RadioButtonState state) =>
DrawRadioButton(g, glyphLocation, state, HWND.Null);
internal static void DrawRadioButtonWithVisualStyles(
HDC hdc,
Point glyphLocation,
RadioButtonState state,
HWND hwnd)
{
InitializeRenderer((int)state);
Rectangle glyphBounds = new(glyphLocation, GetGlyphSize(hdc, state, hwnd));
t_visualStyleRenderer.DrawBackground(hdc, glyphBounds, hwnd);
}
internal static void DrawRadioButton(
Graphics graphics,
Point glyphLocation,
RadioButtonState state,
HWND hwnd)
{
Rectangle glyphBounds;
if (RenderWithVisualStyles)
{
using DeviceContextHdcScope hdc = new(graphics);
DrawRadioButtonWithVisualStyles(hdc, glyphLocation, state, hwnd);
}
else
{
using (DeviceContextHdcScope hdc = new(graphics))
{
glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(hdc, state, hwnd));
}
ControlPaint.DrawRadioButton(graphics, glyphBounds, ConvertToButtonState(state));
}
}
/// <inheritdoc cref="DrawRadioButton(Graphics, Point, Rectangle, string?, Font?, TextFormatFlags, Image, Rectangle, bool, RadioButtonState)" />
public static void DrawRadioButton(
Graphics g,
Point glyphLocation,
Rectangle textBounds,
string? radioButtonText,
Font? font,
bool focused,
RadioButtonState state)
{
DrawRadioButton(g, glyphLocation, textBounds, radioButtonText, font,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine,
focused, state);
}
/// <inheritdoc cref="DrawRadioButton(Graphics, Point, Rectangle, string?, Font?, TextFormatFlags, Image, Rectangle, bool, RadioButtonState)" />
public static void DrawRadioButton(
Graphics g,
Point glyphLocation,
Rectangle textBounds,
string? radioButtonText,
Font? font,
TextFormatFlags flags,
bool focused,
RadioButtonState state)
{
DrawRadioButton(g, glyphLocation, textBounds, radioButtonText, font, flags, focused, state, HWND.Null);
}
internal static void DrawRadioButton(
Graphics g,
Point glyphLocation,
Rectangle textBounds,
string? radioButtonText,
Font? font,
TextFormatFlags flags,
bool focused,
RadioButtonState state,
HWND hwnd)
{
Rectangle glyphBounds;
using (DeviceContextHdcScope hdc = new(g))
{
glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(hdc, state, hwnd));
}
Color textColor;
if (RenderWithVisualStyles)
{
InitializeRenderer((int)state);
t_visualStyleRenderer.DrawBackground(g, glyphBounds);
textColor = t_visualStyleRenderer.GetColor(ColorProperty.TextColor);
}
else
{
ControlPaint.DrawRadioButton(g, glyphBounds, ConvertToButtonState(state));
textColor = SystemColors.ControlText;
}
TextRenderer.DrawText(g, radioButtonText, font, textBounds, textColor, flags);
if (focused)
{
ControlPaint.DrawFocusRectangle(g, textBounds);
}
}
/// <inheritdoc cref="DrawRadioButton(Graphics, Point, Rectangle, string?, Font?, TextFormatFlags, Image, Rectangle, bool, RadioButtonState)" />
public static void DrawRadioButton(
Graphics g,
Point glyphLocation,
Rectangle textBounds,
string? radioButtonText,
Font? font,
Image image,
Rectangle imageBounds,
bool focused,
RadioButtonState state) => DrawRadioButton(
g,
glyphLocation,
textBounds,
radioButtonText,
font,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine,
image,
imageBounds,
focused,
state);
/// <summary>
/// Renders a RadioButton control.
/// </summary>
public static void DrawRadioButton(
Graphics g,
Point glyphLocation,
Rectangle textBounds,
string? radioButtonText,
Font? font,
TextFormatFlags flags,
Image image,
Rectangle imageBounds,
bool focused,
RadioButtonState state) => DrawRadioButton(
g,
glyphLocation,
textBounds,
radioButtonText,
font,
flags,
image,
imageBounds,
focused,
state,
HWND.Null);
internal static void DrawRadioButton(
Graphics g,
Point glyphLocation,
Rectangle textBounds,
string? radioButtonText,
Font? font,
TextFormatFlags flags,
Image image,
Rectangle imageBounds,
bool focused,
RadioButtonState state,
HWND hwnd)
{
Rectangle glyphBounds;
using (DeviceContextHdcScope hdc = new(g))
{
glyphBounds = new Rectangle(glyphLocation, GetGlyphSize(hdc, state, hwnd));
}
Color textColor;
if (RenderWithVisualStyles)
{
InitializeRenderer((int)state);
// Keep this drawing order! It matches default drawing order.
t_visualStyleRenderer.DrawImage(g, imageBounds, image);
t_visualStyleRenderer.DrawBackground(g, glyphBounds);
textColor = t_visualStyleRenderer.GetColor(ColorProperty.TextColor);
}
else
{
g.DrawImage(image, imageBounds);
ControlPaint.DrawRadioButton(g, glyphBounds, ConvertToButtonState(state));
textColor = SystemColors.ControlText;
}
TextRenderer.DrawText(g, radioButtonText, font, textBounds, textColor, flags);
if (focused)
{
ControlPaint.DrawFocusRectangle(g, textBounds);
}
}
/// <summary>
/// Returns the size of the RadioButton glyph.
/// </summary>
public static Size GetGlyphSize(Graphics g, RadioButtonState state)
{
using DeviceContextHdcScope hdc = new(g);
return GetGlyphSize(hdc, state, HWND.Null);
}
internal static Size GetGlyphSize(HDC hdc, RadioButtonState state, HWND hwnd)
{
if (RenderWithVisualStyles)
{
InitializeRenderer((int)state);
return t_visualStyleRenderer.GetPartSize(hdc, ThemeSizeType.Draw, hwnd);
}
return new Size(13, 13);
}
internal static ButtonState ConvertToButtonState(RadioButtonState state) => state switch
{
RadioButtonState.CheckedNormal or RadioButtonState.CheckedHot => ButtonState.Checked,
RadioButtonState.CheckedPressed => ButtonState.Checked | ButtonState.Pushed,
RadioButtonState.CheckedDisabled => ButtonState.Checked | ButtonState.Inactive,
RadioButtonState.UncheckedPressed => ButtonState.Pushed,
RadioButtonState.UncheckedDisabled => ButtonState.Inactive,
_ => ButtonState.Normal,
};
internal static RadioButtonState ConvertFromButtonState(ButtonState state, bool isHot)
{
if ((state & ButtonState.Checked) == ButtonState.Checked)
{
if ((state & ButtonState.Pushed) == ButtonState.Pushed)
{
return RadioButtonState.CheckedPressed;
}
else if ((state & ButtonState.Inactive) == ButtonState.Inactive)
{
return RadioButtonState.CheckedDisabled;
}
else if (isHot)
{
return RadioButtonState.CheckedHot;
}
return RadioButtonState.CheckedNormal;
}
else
{
// Unchecked
if ((state & ButtonState.Pushed) == ButtonState.Pushed)
{
return RadioButtonState.UncheckedPressed;
}
else if ((state & ButtonState.Inactive) == ButtonState.Inactive)
{
return RadioButtonState.UncheckedDisabled;
}
else if (isHot)
{
return RadioButtonState.UncheckedHot;
}
return RadioButtonState.UncheckedNormal;
}
}
[MemberNotNull(nameof(t_visualStyleRenderer))]
private static void InitializeRenderer(int state)
{
RadioButtonState radioButtonState = (RadioButtonState)state;
int part = s_radioElement.Part;
if (SystemInformation.HighContrast
&& (radioButtonState == RadioButtonState.CheckedDisabled || radioButtonState == RadioButtonState.UncheckedDisabled)
&& VisualStyleRenderer.IsCombinationDefined(s_radioElement.ClassName, VisualStyleElement.Button.RadioButton.HighContrastDisabledPart))
{
part = VisualStyleElement.Button.RadioButton.HighContrastDisabledPart;
}
if (t_visualStyleRenderer is null)
{
t_visualStyleRenderer = new VisualStyleRenderer(s_radioElement.ClassName, part, state);
}
else
{
t_visualStyleRenderer.SetParameters(s_radioElement.ClassName, part, state);
}
}
}
|