Как избежать визуальных артефактов цветной рамки масштабируемого UserControl с закругленными углами?

2

У меня есть Form которая содержит:

  1. TrackBar (минимум = 1, максимум = 200, представляет процент увеличения);
  2. UserControl с BorderStyle = BorderStyle.None.

Соответствующий код

Form1

Из дизайнерского кода

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

Из-за кода

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

Начальный скриншот

Изображение 174551

Скриншот после использования трекбара

Изображение 174551

Правая сторона желтой границы становится невидимой после уменьшения масштаба, а при увеличении изображения - несколько желтых границ с правой стороны.

Обновить:

Ответ работает, но есть часть контроля, которая выходит за границы. Снимок экрана для верхнего правого угла, для curveSize = 20:

Изображение 174551

а для curveSize = 24:

Изображение 174551

  • 1
    Вам необходимо сделать недействительным элемент управления после изменения размера, вручную переопределив OnSizeChanged и вызвав Invalidate или автоматически, установив ResizeRedraw=true .
  • 0
    Вы ищете что - то вроде этого ?
Показать ещё 2 комментария
Теги:
rounded-corners

1 ответ

2
Лучший ответ

Я предлагаю немного другой способ рисования границы и содержимого пользовательского элемента управления, который также должен излечивать артефакты, возникающие при перерисовке элемента управления.

Когда вы создаете область для элемента управления, а затем рисуете область как есть, внешние границы рисования не сглаживаются: пиксели с псевдонимами выходят за пределы области. Тот же самый эффект, конечно, применяется, когда граница нарисована вокруг границ Региона.

Здесь я применяю Scale Matrix и Translate Matrix, которые масштабируют и перемещают границы Region во внутренней области, которая определяет границы управления.
Размер масштаба и трансляционные преобразования определяются размером пера.
Больше информации об использовании Матрицы здесь: Переверните GraphicsPath

В этом случае, когда границы окрашены, внешний сглаженный участок границы находится внутри границ Региона, и сглаживание сохраняется.
Цвет фона элемента управления установлен на Color.Transparent (пользовательский элемент управления самостоятельно поддерживает прозрачность цвета).

Я также добавил несколько (не оформленных) свойств, которые позволяют определять внутренний цвет (Control BackgroundColor), а также размер и цвет границы. Остальное более или менее то, что было раньше.

Пример результатов:

Изображение 174551


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;
    }
}
  • 0
    Я обновил свой вопрос. Спасибо.
  • 0
    Вы установили BackColor = Color.Transparent как в описании? При написании кода для производства вы должны переопределить / исключить все те свойства, которые пользователь не должен изменять. Если это для вас, вы можете оставить свойство видимым и установить его в дизайнере. Или используйте метод OnPaint и самостоятельно рисуйте фон. Или переопределить OnPaintBackground . Или используйте SetStyle(ControlStyles.Opaque, true); , У вас не будет фона, поэтому вы снова можете нарисовать его так, как вам нравится.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню