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

ttp71kqs  于 2022-12-30  发布在  .NET
关注(0)|答案(1)|浏览(159)

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

**UPD:**按照Jimi的建议,我只解决了部分问题:

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);
}

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

vktxenjb

vktxenjb1#

有关使用TextureBrush填充用作画布的控件表面的示例。LinearRepeatingGradient类公开可绑定的ColorBands属性(类型为BindingList<ColorBand>),该属性允许添加或移除ColorBand对象,该记录定义要生成的每个带区的颜色和大小。
RotationAngle属性指定要应用于渲染的旋转。
在用作画布的Control的Paint事件中,调用Fill(Graphics g)方法,传递由PaintEventArgs参数提供的e.Graphics对象。
将根据ColorBands属性的内容生成新的位图。
当旋转Angular 不能被90整除时,画布的尺寸将增大其对角线的三分之一(作为与未旋转矩形的最大距离)。
TextureBrush将填充此膨胀的表面,因此画布两侧不会留下任何空白。
因为这个测试示例是用. NET 7构建的,所以我使用record来存储色带设置,你可以用一个类对象来替换它,而不需要修改其余的代码。

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

同上:using declaration而不是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对象时使用数据绑定来执行操作,还可以将集合绑定到Controls,如动画所示:

public partial class SomeForm : Form {

    LinearRepeatingGradient gradient = new();

    public SomeForm()
    {
        InitializeComponent();
        [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();

相关问题