如何避免可缩放的UserControl带有圆角的彩色边框的视觉伪像?

时间:2019-02-20 13:40:34

标签: c# winforms user-controls rounded-corners visual-artifacts

我有一个Form,其中包含:

  1. 一个TrackBar(最小值= 1,最大值= 200,代表缩放百分比);
  2. 带有UserControl的{​​{1}}。

相关代码

Form1

根据设计人员代码

BorderStyle = BorderStyle.None

来自隐藏代码

trackBar1.Value = 100;
BackColor = Color.Gray;

UserControl1

private void trackBar1_Scroll(object sender, EventArgs e)
{
    userControl11.SetZoomFactor(trackBar1.Value / 100F);
}

初始屏幕截图

initially

使用轨迹栏后的屏幕截图

after zoom

缩小后,黄色边框的右侧不可见;放大时,右侧有多个黄色边框。

更新

答案有效,但是控件的一部分超出了边界。右上角的屏幕截图,internal float MyBaseWidth; public UserControl1() { InitializeComponent(); MyBaseWidth = Width; SetZoomFactor(1); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); e.Graphics.SmoothingMode = SmoothingMode.HighQuality; e.Graphics.CompositingQuality = CompositingQuality.HighQuality; e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; Pen p = new Pen(Color.Yellow); e.Graphics.DrawPath(p, GraphicsPathWithBorder); } internal GraphicsPath GraphicsPathWithBorder; internal void SetZoomFactor(float z) { Width = (int)(MyBaseWidth * z); GraphicsPathWithBorder = RoundedCornerRectangle(ClientRectangle); Region = new Region(GraphicsPathWithBorder); } internal static GraphicsPath RoundedCornerRectangle(Rectangle r) { GraphicsPath path = new GraphicsPath(); float size = 10 * 2F; path.StartFigure(); path.AddArc(r.X, r.Y, size, size, 180, 90); path.AddArc((r.X + (r.Width - size)), r.Y, size, size, 270, 90); path.AddArc((r.X + (r.Width - size)), (r.Y + (r.Height - size)), size, size, 0, 90); path.AddArc(r.X, (r.Y + (r.Height - size)), size, size, 90, 90); path.CloseFigure(); return path; }

curve 1

curveSize = 20

curve 2

1 个答案:

答案 0 :(得分:2)

我建议绘制边框和用户控件内容的方法稍有不同,该方法还应治愈重新绘制控件时生成的工件。

当您为控件创建一个Region,然后按原样绘制Region时,绘画的外部边界不会被消除锯齿:别名像素位于Region之外。在区域的边界周围绘制边框时,当然会产生相同的效果。

在这里,我应用Scale MatrixTranslate Matrix缩放并移动该区域的边界在定义控件边界的外部Region的内部。 br /> 比例尺的大小和平移转换由Pen大小确定。
有关Matrix使用情况的更多信息,请点击此处:Flip the GraphicsPath

在这种情况下,当绘制边框时,边框的外部抗锯齿部分位于“区域”边界之内,并且保留了抗锯齿。
控件的背景色设置为Color.Transparent(用户控件本身支持颜色透明)。

我还添加了几个(未修饰的)属性,这些属性允许定义内部颜色(控件的BackgroundColor)以及边框的大小和颜色。其余的或多或少是以前的。

样本结果:

Rounded UserControl


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

public partial class RoundControl : UserControl
{
    private GraphicsPath GraphicsPathWithBorder;
    private float MyBaseWidth;
    private float m_PenSize = 2f;
    private Color m_BorderColor = Color.Yellow;
    private Color m_FillColor = Color.Green;

    public RoundControl()
    {
        this.ResizeRedraw = true;
        InitializeComponent();
        MyBaseWidth = Width;
    }

    public float BorderSize
    {
        get => this.m_PenSize;
        set {
            this.m_PenSize = value;
            this.Invalidate();
        }
    }

    public Color BorderColor
    {
        get => this.m_BorderColor;
        set {
            this.m_BorderColor = value;
            this.Invalidate();
        }
    }

    public Color FillColor
    {
        get => this.m_FillColor;
        set {
            this.m_FillColor = value;
            this.Invalidate();
        }
    }

    protected override void OnLayout(LayoutEventArgs e) {
        this.UpdateRegion();
        base.OnLayout(e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        RectangleF rect = GraphicsPathWithBorder.GetBounds();
        float scaleX = 1 - ((m_PenSize + 1) / rect.Width);
        float scaleY = 1 - ((m_PenSize + 1) / rect.Height);
        using (Pen pen = new Pen(m_BorderColor, m_PenSize))
        using (Brush brush = new SolidBrush(m_FillColor))
        using (Matrix mx = new Matrix(scaleX, 0, 0, scaleY, pen.Width / 2, pen.Width / 2))
        {
            e.Graphics.Transform = mx;
            e.Graphics.FillPath(brush, GraphicsPathWithBorder);
            e.Graphics.DrawPath(pen, GraphicsPathWithBorder);
        }
    }

    private void UpdateRegion() {
        GraphicsPathWithBorder = RoundedCornerRectangle(ClientRectangle);
        Region = new Region(GraphicsPathWithBorder);
        this.Invalidate();
    }

    internal void SetZoomFactor(float z) {
        int newWidth = (int)(MyBaseWidth * z);
        if (newWidth <= (30 + this.m_PenSize * 2)) return;
        this.Width = newWidth;
        this.UpdateRegion();
    }

    private GraphicsPath RoundedCornerRectangle(Rectangle r)
    {
        GraphicsPath path = new GraphicsPath();
        float curveSize = 10 * 2.4F;

        path.StartFigure();
        path.AddArc(r.X, r.Y, curveSize, curveSize, 180, 90);
        path.AddArc(r.Right - curveSize, r.Y, curveSize, curveSize, 270, 90);
        path.AddArc(r.Right - curveSize, r.Bottom - curveSize, curveSize, curveSize, 0, 90);
        path.AddArc(r.X, r.Bottom - curveSize, curveSize, curveSize, 90, 90);
        path.CloseFigure();
        return path;
    }
}