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

How to avoid visual artifacts of colored border of zoomable UserControl with rounded corners?(如何避免带有圆角的可缩放 UserControl 彩色边框的视觉伪影?)
本文介绍了如何避免带有圆角的可缩放 UserControl 彩色边框的视觉伪影?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Form,其中包含:

  1. 一个TrackBar(最小值=1,最大值=200,代表缩放百分比);
  2. 一个 UserControlBorderStyle = BorderStyle.None.

相关代码

表格1

来自设计器代码

trackBar1.Value = 100;背景颜色=颜色.灰色;

来自代码隐藏

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

用户控件1

内部浮动 MyBaseWidth;公共用户控件1(){初始化组件();MyBaseWidth = 宽度;设置缩放因子(1);}受保护的覆盖无效 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);}内部 GraphicsPath GraphicsPathWithBorder;内部无效 SetZoomFactor(float z){宽度 = (int)(MyBaseWidth * z);GraphicsPathWithBorder = RoundedCornerRectangle(ClientRectangle);区域 = 新区域(GraphicsPathWithBorder);}内部静态 GraphicsPath RoundedCornerRectangle(Rectangle r){GraphicsPath 路径 = new GraphicsPath();浮动大小 = 10 * 2F;path.StartFigure();path.AddArc(r.X, r.Y,尺寸,尺寸,180、90);path.AddArc((r.X + (r.Width - size)), r.Y,大小,大小,270、90);path.AddArc((r.X + (r.Width - size)), (r.Y + (r.Height - size)),大小, 大小, 0, 90);path.AddArc(r.X, (r.Y + (r.Height - size)),尺寸,尺寸,90、90);path.CloseFigure();返回路径;}

初始截图

使用轨迹栏后的屏幕截图

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

更新:

答案有效,但是控件的一部分超出了边界.curveSize = 20 右上角的屏幕截图:

对于 curveSize = 24:

解决方案

我建议使用稍微不同的方法来绘制边框和用户控件的内容,该方法还应该治愈控件被重绘.

当您为控件创建区域,然后按原样绘制区域时,绘制的外部边界不会消除锯齿:锯齿像素落在区域之外.当围绕区域的边界绘制边框时,当然会应用相同的效果.

在这里,我应用了一个


使用 System.Drawing;使用 System.Drawing.Drawing2D;公共部分类 RoundControl : UserControl{私有 GraphicsPath GraphicsPathWithBorder;私人浮动 MyBaseWidth;私人浮动 m_PenSize = 2f;私有颜色 m_BorderColor = Color.Yellow;私有颜色 m_FillColor = Color.Green;公共圆形控制(){调整大小重绘=真;初始化组件();MyBaseWidth = 宽度;}公共花车 BorderSize{得到=>m_PenSize;放 {m_PenSize = 值;无效();}}公共颜色边框颜色{得到=>m_BorderColor;放 {m_BorderColor = 值;无效();}}公共颜色填充颜色{得到=>m_FillColor;放 {m_FillColor = 值;无效();}}受保护的覆盖无效 OnLayout(LayoutEventArgs e) {更新区域();base.OnLayout(e);}受保护的覆盖无效 OnPaint(PaintEventArgs e){e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;RectangleF rect = GraphicsPathWithBorder.GetBounds();浮动 scaleX = 1 - ((m_PenSize + 1)/rect.Width);float scaleY = 1 - ((m_PenSize + 1)/rect.Height);使用 (Pen pen = new Pen(m_BorderColor, m_PenSize))使用(画笔刷 = 新的 SolidBrush(m_FillColor))使用 (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);}base.OnPaint(e);}内部无效 SetZoomFactor(float z) {int newWidth = (int)(MyBaseWidth * z);if (newWidth <= (30 + m_PenSize * 2)) 返回;宽度 = 新宽度;更新区域();}私人无效更新区域(){GraphicsPathWithBorder = RoundedCornerRectangle(ClientRectangle);区域 = 新区域(GraphicsPathWithBorder);无效();}私有 GraphicsPath RoundedCornerRectangle(矩形 r){GraphicsPath 路径 = new GraphicsPath();//固定曲线大小,因为我们只在 X 维度上缩放//否则,调整也要考虑高度浮动曲线大小 = 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();返回路径;}}

I have a Form which contains:

  1. a TrackBar (minimum = 1, maximum = 200, represents zoom percent);
  2. a UserControl with BorderStyle = BorderStyle.None.

Relevant code

Form1

Fromdesignercode

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

Fromcode-behind

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

UserControl1

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

Initial screenshot

Screenshot after using the trackbar

The right side of the yellow border becomes invisible after zooming out, and when zooming in there are multiple yellow borders on the right side.

Update:

The answer Works, but there is a part of the control that goes beyond the border. Screenshot for top-right corner, for curveSize = 20:

and for curveSize = 24:

解决方案

I suggest a slightly different method to draw the Border and the content of the User Control that should also cure the artifacts generated when the control is redrawn.

When you create a Region for a Control and then you paint the Region as it is, the outer borders of the painting are not anti-aliased: the aliased pixels fall outside the Region. The same effect of course is applied when a border is painted around the bounds of the Region.

Here, I apply a Scale Matrix and a Translate Matrix that scale and move the bounds of the Region on the inside of the outer Region that defines the control's bounds.
The size of the scale and the translate transformations are determined by the Pen size.
More information on the Matrix usage here: Flip the GraphicsPath

In this case, when the borders are painted, the outer, anti-aliased, section of the border is inside the Region bounds and the anti-aliasing is preserved.
The background color of the Control is set to Color.Transparent (a User Control supports color transparency on its own).

I've also added a couple of (non decorated) properties that allow to define the inner Color (the Control's BackColor) and Size and Color of the Border. The rest is more or less what it was before.

Sample results:


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()
    {
        ResizeRedraw = true;
        InitializeComponent();
        MyBaseWidth = Width;
    }

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

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

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

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

    protected override void OnPaint(PaintEventArgs 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);
        }
        base.OnPaint(e);
    }

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


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

    private GraphicsPath RoundedCornerRectangle(Rectangle r)
    {
        GraphicsPath path = new GraphicsPath();
        // Fixed curve size since we only scale on X-dimension
        // Otherwise, adjust also considering the height
        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;
    }
}

这篇关于如何避免带有圆角的可缩放 UserControl 彩色边框的视觉伪影?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)