Fix gradient button paint artifacts and WrapMode crash on .NET Framework.

Use toolbar-themed background fill, lighter shadow, and remove invalid LinearGradientBrush.WrapMode that caused OnPaint failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Magomed 2026-07-14 14:39:45 +03:00
parent 1ccb2b33cf
commit a9fd467b5d
5 changed files with 56 additions and 61 deletions

View File

@ -91,7 +91,7 @@ namespace Электронная_Фармация.UserControls
const int row1Y = 10; const int row1Y = 10;
const int controlHeight = 32; const int controlHeight = 32;
const int filterHeight = 33; const int filterHeight = 33;
const int buttonHeight = 37; const int buttonHeight = 34;
const int rowGap = 14; const int rowGap = 14;
const int row2Y = row1Y + buttonHeight + rowGap; const int row2Y = row1Y + buttonHeight + rowGap;
const int commentWidth = 200; const int commentWidth = 200;
@ -126,11 +126,11 @@ namespace Электронная_Фармация.UserControls
x = pad; x = pad;
txtGoodFromOrders.SetBounds(x, row2Y, 220, controlHeight); txtGoodFromOrders.SetBounds(x, row2Y, 220, controlHeight);
x = txtGoodFromOrders.Right + gap; x = txtGoodFromOrders.Right + gap;
btnClearGoodSearch.SetBounds(x, row2Y, 32, controlHeight + 3); btnClearGoodSearch.SetBounds(x, row2Y, 32, controlHeight);
x = btnClearGoodSearch.Right + gap; x = btnClearGoodSearch.Right + gap;
txtOrderNumberFilter.SetBounds(x, row2Y, 220, controlHeight); txtOrderNumberFilter.SetBounds(x, row2Y, 220, controlHeight);
x = txtOrderNumberFilter.Right + gap; x = txtOrderNumberFilter.Right + gap;
btnClearOrderNumberFilter.SetBounds(x, row2Y, 32, controlHeight + 3); btnClearOrderNumberFilter.SetBounds(x, row2Y, 32, controlHeight);
x = btnClearOrderNumberFilter.Right + gap; x = btnClearOrderNumberFilter.Right + gap;
btnAddComment.SetBounds(x, row2Y, commentWidth, buttonHeight); btnAddComment.SetBounds(x, row2Y, commentWidth, buttonHeight);

View File

@ -52,9 +52,9 @@ namespace Электронная_Фармация.UserControls
Tag = "chrome-child"; Tag = "chrome-child";
btnDeleteGood.Height = 34; btnDeleteGood.Height = 34;
btnClearSearchInPriceList.Height = 35; btnClearSearchInPriceList.Height = 32;
txtSearchGoodNameInPriceList.Height = 32; txtSearchGoodNameInPriceList.Height = 32;
btnClearSupplierFilter.Height = 35; btnClearSupplierFilter.Height = 32;
panel1.SizeChanged += (_, __) => LayoutMarkupRow(); panel1.SizeChanged += (_, __) => LayoutMarkupRow();
LayoutMarkupRow(); LayoutMarkupRow();

View File

@ -94,13 +94,29 @@ namespace Elfisa.UI.Controls
return; return;
} }
var parentColor = Parent?.BackColor ?? ThemeManager.Colors.ToolbarBackground; using (var brush = new SolidBrush(ResolveChromeBackground()))
using (var brush = new SolidBrush(parentColor))
{ {
pevent.Graphics.FillRectangle(brush, ClientRectangle); pevent.Graphics.FillRectangle(brush, ClientRectangle);
} }
} }
private Color ResolveChromeBackground()
{
var theme = _theme ?? ThemeManager.Colors;
var parentTag = Parent?.Tag as string;
if (parentTag == "toolbar")
{
return theme.ToolbarBackground;
}
if (parentTag == "chrome-child" || parentTag == "status-footer")
{
return theme.BackgroundSecondary;
}
return Parent?.BackColor ?? theme.ToolbarBackground;
}
public ModernButton() public ModernButton()
{ {
SetStyle(ControlStyles.AllPaintingInWmPaint | SetStyle(ControlStyles.AllPaintingInWmPaint |
@ -135,6 +151,12 @@ namespace Elfisa.UI.Controls
? new Font(theme.FontSemibold14.FontFamily, 10f, FontStyle.Bold) ? new Font(theme.FontSemibold14.FontFamily, 10f, FontStyle.Bold)
: new Font("Segoe UI Semibold", 10f, FontStyle.Bold); : new Font("Segoe UI Semibold", 10f, FontStyle.Bold);
} }
if (SkillGradientButtonPaint.UsesGradient(_buttonStyle, _compactIconMode) && !_headerNavMode && !_topNavMode)
{
BackColor = ResolveChromeBackground();
}
Invalidate(); Invalidate();
} }
@ -240,6 +262,11 @@ namespace Elfisa.UI.Controls
if (usesGradient && !_topNavMode && !_sidebarMode) if (usesGradient && !_topNavMode && !_sidebarMode)
{ {
using (var bgBrush = new SolidBrush(ResolveChromeBackground()))
{
g.FillRectangle(bgBrush, bounds);
}
SkillGradientButtonPaint.Paint( SkillGradientButtonPaint.Paint(
g, g,
bounds, bounds,

View File

@ -60,9 +60,7 @@ namespace Elfisa.UI.Controls
button.AllowTextEllipsis = false; button.AllowTextEllipsis = false;
button.Font = ActionFont; button.Font = ActionFont;
button.Padding = new Padding(16, 6, 16, 6); button.Padding = new Padding(16, 6, 16, 6);
var height = style == ModernButtonStyle.Primary || style == ModernButtonStyle.Danger var height = DefaultHeight;
? DefaultHeight + 3
: DefaultHeight;
button.Height = Math.Max(button.Height, height); button.Height = Math.Max(button.Height, height);
FitToText(button, height); FitToText(button, height);
@ -85,8 +83,8 @@ namespace Elfisa.UI.Controls
button.Font = new Font("Segoe UI Semibold", 13f, FontStyle.Bold); button.Font = new Font("Segoe UI Semibold", 13f, FontStyle.Bold);
button.IconGlyph = null; button.IconGlyph = null;
button.AllowTextEllipsis = false; button.AllowTextEllipsis = false;
button.MinimumSize = new Size(ClearSize, ClearSize + 3); button.MinimumSize = new Size(ClearSize, ClearSize);
button.Size = new Size(ClearSize, ClearSize + 3); button.Size = new Size(ClearSize, ClearSize);
} }
public static void FitToText(ModernButton button, int height = DefaultHeight) public static void FitToText(ModernButton button, int height = DefaultHeight)

View File

@ -5,42 +5,18 @@ using Elfisa.UI.Controls;
namespace Elfisa.UI.Helpers namespace Elfisa.UI.Helpers
{ {
/// <summary> /// <summary>
/// Градиент и тень из skill winforms-modern-buttons для ModernButton. /// Градиент из skill winforms-modern-buttons для ModernButton (без тяжёлой тени — она давала артефакты на toolbar).
/// </summary> /// </summary>
internal static class SkillGradientButtonPaint internal static class SkillGradientButtonPaint
{ {
private static readonly Color ShadowBase = Color.FromArgb(60, 0, 0, 0);
public static bool UsesGradient(ModernButtonStyle style, bool compactIconMode) public static bool UsesGradient(ModernButtonStyle style, bool compactIconMode)
{ {
return style == ModernButtonStyle.Primary return style == ModernButtonStyle.Primary || style == ModernButtonStyle.Danger;
|| style == ModernButtonStyle.Danger
|| (compactIconMode && style == ModernButtonStyle.Danger);
} }
public static int GetCornerRadius(ModernButtonStyle style, bool compactIconMode) public static int GetCornerRadius(ModernButtonStyle style, bool compactIconMode)
{ {
if (compactIconMode) return compactIconMode ? 6 : style == ModernButtonStyle.Primary ? 10 : 8;
{
return 6;
}
return style == ModernButtonStyle.Primary ? 12 : 8;
}
public static int GetShadowDepth(ModernButtonStyle style, bool compactIconMode, bool enabled)
{
if (!enabled)
{
return 0;
}
if (compactIconMode)
{
return 3;
}
return style == ModernButtonStyle.Primary ? 5 : 4;
} }
public static void GetGradient( public static void GetGradient(
@ -71,7 +47,6 @@ namespace Elfisa.UI.Helpers
return; return;
} }
// Primary preset from skill
if (pressed) if (pressed)
{ {
top = Color.FromArgb(25, 70, 160); top = Color.FromArgb(25, 70, 160);
@ -98,13 +73,14 @@ namespace Elfisa.UI.Helpers
bool pressed, bool pressed,
bool enabled) bool enabled)
{ {
var shadowDepth = GetShadowDepth(style, compactIconMode, enabled);
var radius = GetCornerRadius(style, compactIconMode); var radius = GetCornerRadius(style, compactIconMode);
var buttonRect = new Rectangle( var buttonRect = new Rectangle(
clientRect.X, clientRect.X,
clientRect.Y, clientRect.Y,
clientRect.Width - 1, clientRect.Width - 1,
clientRect.Height - 1 - shadowDepth); clientRect.Height - 1);
g.SetClip(clientRect);
if (!enabled) if (!enabled)
{ {
@ -116,44 +92,38 @@ namespace Elfisa.UI.Helpers
return; return;
} }
for (var i = shadowDepth; i > 0; i--) // Лёгкая тень — один слой, без чёрной полосы снизу
var shadowRect = new Rectangle(buttonRect.X, buttonRect.Y + 1, buttonRect.Width, buttonRect.Height);
using (var shadowPath = GraphicsExtensions.CreateRoundedRectangle(shadowRect, radius))
using (var shadowBrush = new SolidBrush(Color.FromArgb(28, 0, 0, 0)))
{ {
var alpha = ShadowBase.A * i / (shadowDepth * 3); g.FillPath(shadowBrush, shadowPath);
using (var shadowBrush = new SolidBrush(Color.FromArgb(alpha, ShadowBase)))
using (var shadowPath = GraphicsExtensions.CreateRoundedRectangle(
new Rectangle(
clientRect.X + i,
buttonRect.Y + i + 1,
buttonRect.Width - i,
buttonRect.Height - 1),
radius))
{
g.FillPath(shadowBrush, shadowPath);
}
} }
var faceRect = new Rectangle(buttonRect.X, buttonRect.Y, buttonRect.Width, buttonRect.Height - 1);
GetGradient(style, hover, pressed, out var top, out var bottom); GetGradient(style, hover, pressed, out var top, out var bottom);
using (var path = GraphicsExtensions.CreateRoundedRectangle(buttonRect, radius)) using (var path = GraphicsExtensions.CreateRoundedRectangle(faceRect, radius))
using (var brush = new LinearGradientBrush(buttonRect, top, bottom, LinearGradientMode.Vertical)) using (var brush = new LinearGradientBrush(faceRect, top, bottom, LinearGradientMode.Vertical))
{ {
g.FillPath(brush, path); g.FillPath(brush, path);
using (var highlightPen = new Pen(Color.FromArgb(40, Color.White), 1f)) using (var highlightPen = new Pen(Color.FromArgb(36, Color.White), 1f))
{ {
g.DrawArc(highlightPen, buttonRect.X, buttonRect.Y, radius * 2, radius * 2, 180, 90); g.DrawArc(highlightPen, faceRect.X, faceRect.Y, radius * 2, radius * 2, 180, 90);
} }
} }
g.ResetClip();
} }
public static Rectangle GetContentRect(Rectangle clientRect, ModernButtonStyle style, bool compactIconMode, bool enabled) public static Rectangle GetContentRect(Rectangle clientRect, ModernButtonStyle style, bool compactIconMode, bool enabled)
{ {
var shadowDepth = GetShadowDepth(style, compactIconMode, enabled);
return new Rectangle( return new Rectangle(
clientRect.X, clientRect.X,
clientRect.Y, clientRect.Y,
clientRect.Width - 1, clientRect.Width - 1,
clientRect.Height - 1 - shadowDepth); clientRect.Height - 1);
} }
} }
} }