替换 .NET (WinForms) 中的 CSS3 函数重复线性梯度()

问题描述 投票:0回答:1

.NET(WinForms,而不是 WPF)中的 CSS3 函数 repeating-linear-gradient() 是否有任何替代(模拟)? 我需要以 45 度角绘制重复的“斑马条纹”(例如红、蓝、绿、红、蓝、绿……)。

更新: 按照吉米的建议,我只部分解决了问题:

private void DrawRepeatingStripes(int degree, int stripeWidth, Color[] colors, Rectangle rect, Graphics graphics)
{
    using (var img = new Bitmap(colors.Length * stripeWidth, rect.Height))
    {
        using (var g = Graphics.FromImage(img))
        {
            for (int i = 0; i < colors.Length; i++)
            {
                // TODO: cache SolidBrush
                g.FillRectangle(new SolidBrush(colors[i]), stripeWidth * i, 0, stripeWidth, rect.Height);
            }
        }
        using (var tb = new TextureBrush(img, WrapMode.Tile))
        {
            using (var myMatrix = new Matrix())
            {
                myMatrix.Rotate(degree);
                graphics.Transform = myMatrix;
                graphics.FillRectangle(tb, rect);
                graphics.ResetTransform();
            }
        }
    }
}

用法(以某种形式的代码):

protected override void OnPaintBackground(PaintEventArgs e)
{
    base.OnPaintBackground(e);
    DrawRepeatingStripes(45, 10, new Color[] { Color.Red, Color.Yellow, Color.Green }, e.ClipRectangle, e.Graphics);
}

问题是旋转是......好吧,一个旋转,所以矩形的一部分充满了条纹,而部分是空的。不知道如何解决:(

c# winforms graphics
1个回答
1
投票

有关使用 TextureBrush 填充用作画布的控件表面的示例。

LinearRepeatingGradient
类公开了一个可绑定的
ColorBands
属性(类型为
BindingList<ColorBand>
),允许添加或删除
ColorBand
对象,该记录定义了要生成的每个带的颜色和大小。

RotationAngle
属性指定应用于渲染的旋转。

在用作画布的 Control 的

Paint
事件中,调用
Fill(Graphics g)
方法,并传递
e.Graphics
参数提供的
PaintEventArgs
对象。
根据
ColorBands
属性的内容生成一个新的位图。
当旋转角度无法精确除以
90
时,画布的尺寸会膨胀其对角线的三分之一(作为距非旋转矩形的最大距离)。
TextureBrush 填充了这个膨胀的表面,因此画布的两侧不会留下任何空白。

由于此测试示例是使用 .NET 7 构建的,因此我使用

record
来存储色带的设置。您可以将其替换为类对象,而无需更改其余代码。

public record ColorBand(Color Color, int Size) {
    public override string ToString() => $"Color: {Color.Name} Size: {Size}";
}

同上:

using
声明代替
using
声明

using System.Drawing;
using System.Drawing.Drawing2D;

public class LinearRepeatingGradient
{
    public LinearRepeatingGradient(float rotation = .0f)
    {
        ColorBands = new BindingList<ColorBand>();
        RotationAngle = rotation;
    }

    public float RotationAngle { get; set; }

    [Bindable(true), ListBindable(BindableSupport.Default)]
    public BindingList<ColorBand> ColorBands { get; }

    public void Fill(Graphics g) => Fill(g, g.ClipBounds);

    public void Fill(Graphics g, Rectangle fillArea) => Fill(g, new RectangleF(fillArea.Location, fillArea.Size));

    protected virtual void Fill(Graphics g, RectangleF display)
    {
        if (ColorBands is null || ColorBands.Count == 0 || g.Clip.IsInfinite(g)) return;

        var canvas = InflateCanvas(display);
        var centerPoint = new PointF(canvas.X + canvas.Width / 2, canvas.Y + canvas.Height / 2);

        using var texture = GetTexture(canvas.Width);
        if (texture is null) return;
        using var brush = new TextureBrush(texture, WrapMode.Tile);
        using var mx = new Matrix();
        mx.RotateAt(RotationAngle, centerPoint);
        g.Transform = mx;
        g.FillRectangle(brush, canvas);
        g.ResetTransform();
    }

    private RectangleF InflateCanvas(RectangleF rect)
    {
        if (RotationAngle % 90.0f == 0) return rect;
        float maxInflate = (float)Math.Sqrt(Math.Pow(rect.X - rect.Right, 2) +
                                            Math.Pow(rect.Y - rect.Bottom, 2)) / 3.0f;
        var canvas = rect;
        canvas.Inflate(maxInflate, maxInflate);
        return canvas;
    }

    private Bitmap? GetTexture(float width)
    {
        int height = ColorBands!.Sum(c => c.Size);
        if (height <= 0) return null;
        var texture = new Bitmap((int)(width + .5f), height);
        int startPosition = 0;

        using var g = Graphics.FromImage(texture);
        for (int i = 0; i < ColorBands!.Count; i++) {
            var rect = new Rectangle(0, startPosition, texture.Width, ColorBands![i].Size);
            using var brush = new SolidBrush(ColorBands![i].Color);
            g.FillRectangle(brush, rect);
            startPosition += ColorBands![i].Size;
        }
        return texture;
    }
}

这就是它的工作原理:

由于

ColorBands
属性是可绑定的,因此当添加或删除
ColorBand
对象并将
ColorBands
集合绑定到 Controls 时,您可以使用数据绑定来执行操作,如动画所示:

public partial class SomeForm : Form {

    LinearRepeatingGradient gradient = new();

    public SomeForm()
    {
        InitializeComponent();
        [Your DataGridView].DataSource = gradient.ColorBands;
        gradient.ColorBands.ListChanged += (s, e) => someControl.Invalidate();
    }

    private void someControl_Paint(object sender, PaintEventArgs e) =>
        gradient.Fill(e.Graphics);

因此,当您添加新的

ColorBand
(或删除它)时,内部集合会发生变化,并且用作画布的控件将失效,显示新的填充:

gradient.ColorBands.Add(new ColorBand(Color.Red, 45f));

RotationAngle
属性不使用数据绑定,因此在更改画布时必须手动使其无效。您当然可以更改它并使该属性可绑定:

gradient.RotationAngle = 215f;
someControl.Invalidate();
© www.soinside.com 2019 - 2024. All rights reserved.