Прозрачные перекрывающиеся круговые индикаторы выполнения (пользовательский контроль) - PullRequest
0 голосов
/ 19 ноября 2018

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

Вот что я вижу:

Overlapping Circular Progress Bars

Я исследовал стекпотоки и нашел несколько ответов для людей, имеющих эту проблему с пользовательскими элементами управления Picturebox. Похоже, что большинство решений никак не влияют на круговой индикатор выполнения. Вот некоторые из решений, которые я попробовал:

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;

            cp.ExStyle |= 0x20;

            return cp;
        }
    }

У меня также есть код в пользовательском элементе управления для разрешения прозрачных фонов. Очевидно, как я уже говорил, это не влияет на перекрывающиеся элементы управления.

SetStyle(ControlStyles.SupportsTransparentBackColor, true);

Существует также решение TransparentControl для stackoverflow, которое, как я видел, используют люди. Я создал элемент управления, но либо не знаю, как его использовать, либо он не работает в моей ситуации. Вот код этого элемента управления.

public class TransparentControl : Panel
{
    public bool drag = false;
    public bool enab = false;
    private int m_opacity = 100;

    private int alpha;
    public TransparentControl()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        SetStyle(ControlStyles.Opaque, true);
        this.BackColor = Color.Transparent;
    }

    public int Opacity
    {
        get
        {
            if (m_opacity > 100)
            {
                m_opacity = 100;
            }
            else if (m_opacity < 1)
            {
                m_opacity = 1;
            }
            return this.m_opacity;
        }
        set
        {
            this.m_opacity = value;
            if (this.Parent != null)
            {
                Parent.Invalidate(this.Bounds, true);
            }
        }
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle = cp.ExStyle | 0x20;
            return cp;
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        Rectangle bounds = new Rectangle(0, 0, this.Width - 1, this.Height - 1);

        Color frmColor = this.Parent.BackColor;
        Brush bckColor = default(Brush);

        alpha = (m_opacity * 255) / 100;

        if (drag)
        {
            Color dragBckColor = default(Color);

            if (BackColor != Color.Transparent)
            {
                int Rb = BackColor.R * alpha / 255 + frmColor.R * (255 - alpha) / 255;
                int Gb = BackColor.G * alpha / 255 + frmColor.G * (255 - alpha) / 255;
                int Bb = BackColor.B * alpha / 255 + frmColor.B * (255 - alpha) / 255;
                dragBckColor = Color.FromArgb(Rb, Gb, Bb);
            }
            else
            {
                dragBckColor = frmColor;
            }

            alpha = 255;
            bckColor = new SolidBrush(Color.FromArgb(alpha, dragBckColor));
        }
        else
        {
            bckColor = new SolidBrush(Color.FromArgb(alpha, this.BackColor));
        }

        if (this.BackColor != Color.Transparent | drag)
        {
            g.FillRectangle(bckColor, bounds);
        }

        bckColor.Dispose();
        g.Dispose();
        base.OnPaint(e);
    }

    protected override void OnBackColorChanged(EventArgs e)
    {
        if (this.Parent != null)
        {
            Parent.Invalidate(this.Bounds, true);
        }
        base.OnBackColorChanged(e);
    }

    protected override void OnParentBackColorChanged(EventArgs e)
    {
        this.Invalidate();
        base.OnParentBackColorChanged(e);
    }
}

Любая помощь будет принята с благодарностью. Это сводит меня с ума в течение нескольких часов. Спасибо:)

ОБНОВЛЕНИЕ 1: Я попытался использовать следующий фрагмент кода из приведенных ниже примеров. Это дало те же результаты. У меня все еще есть пустое пространство между круглыми индикаторами выполнения (как показано на рисунке).

                Parent.Controls.Cast<Control>()
                  .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
                  .Where(c => c.Bounds.IntersectsWith(this.Bounds))
                  .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
                  .ToList()
                  .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));

Все еще в тупике. (

ОБНОВЛЕНИЕ 2: Я попытался настроить переднюю круговую панель прогресса, чтобы использовать заднюю круговую панель прогресса в качестве ее родителя в FormLoad. Это тоже не сработало. Это делало их прозрачными друг для друга, но отрезало любую часть верхнего кругового индикатора выполнения, которая не была в пределах границ спины.

var pts = this.PointToScreen(circularprogressbar1.Location);
pts = circularprogressbar2.PointToClient(pts);
circularprogressbar1.Parent = circularprogressbar2;
circularprogressbar1.Location = pts;

1 Ответ

0 голосов
/ 19 ноября 2018

Я собираюсь дать вам пару советов о том, как действовать.

Начните с этого прозрачного элемента управления (TransparentPanel).
Этот класс является производным от Panel. Это первый выбор: Panel - это правильный элемент управления для наследования или расширения для этой задачи? Может быть, а может и нет.
Например, Panel - это контейнер. Вам нужны особенности контейнера, здесь? Контейнер очень много значит. Он наследует ScrollableControl и имеет ContainerControl среди своих стилей окна. Он уже с багажом.

Вместо этого вы можете выбрать Label, он легкий. Или создайте UserControl.
Я не думаю, что есть абсолютный лучший выбор . Это зависит от того, для чего используется этот пользовательский элемент управления. Вы должны попробовать это.

class TransparentPanel : Panel
{
    internal const int WS_EX_TRANSPARENT = 0x00000020;

    public TransparentPanel() => InitializeComponent();

    protected void InitializeComponent()
    {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint |
                      ControlStyles.Opaque |
                      ControlStyles.ResizeRedraw |
                      ControlStyles.SupportsTransparentBackColor |
                      ControlStyles.UserPaint, true);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
    }

    protected override CreateParams CreateParams
    {
        get {
            CreateParams parameters = base.CreateParams;
            parameters.ExStyle |= WS_EX_TRANSPARENT;
            return parameters;
        }
    }
}


Примечания
Здесь ControlStyles.SupportsTransparentBackColor устанавливается явно. Класс Panel уже поддерживает это. В любом случае он указан, поскольку дает представление о том, что представляет собой этот пользовательский элемент управления для простого чтения в его конструкторе.

Также для ControlStyles.OptimizedDoubleBuffer установлено значение false.
Это предотвращает любое вмешательство системы в окраску элемента управления. Там нет кэширования , пользовательский элемент управления отображается новым, когда он недействителен. Форма контейнера предпочтительно должна иметь свойство DoubleBuffer, установленное на true, но вы можете проверить его без, чтобы увидеть, есть ли разница.


Этот Пользовательский элемент управления (не путать с UserControl) полностью прозрачен. Он не рисует свой фон. Но на его поверхности можно рисовать что угодно.

Воспользуйтесь ссылками, размещенными ранее:
- Это Полупрозрачная метка (без фоновой окраски, отключено DoubleDuffering)
- Reza Aghaei прозрачная панель (используя Opacity по-другому)
- TaW S Панель сетки (Color.Transparent и DoubleBuffer)
- Эти примечания: Причины, по которым ярлык WinForms не хочет быть прозрачным?

4 разных способа получить один и тот же результат. Какой из них выбрать, зависит от контекста / пункта назначения.

Совет времени разработки : при тестировании пользовательских функций управления не забывайте всегда перестраивать проект. Может случиться, что CustomControl, сброшенный на Form из Toolbox, не будет обновлен новыми изменениями при запуске проекта.
Кроме того, если вы добавляете или удаляете свойства , вам необходимо удалить элемент управления, перестроить и добавить новый в форму.
Если вы этого не сделаете, есть очень хороший шанс, что ваши изменения / дополнения будут полностью проигнорированы, и вы продолжаете тестировать функции, которые никогда не вступают в игру.

Пример, использующий 2 перекрывающихся пользовательских элемента управления.
(используя обычай TransparentPanel)

Transparent Overlapped Controls Rotate

Это тестовый код, используемый для создания этих чертежей:
- Создайте новый пользовательский элемент управления, используя класс TransparentPanel, показанный ранее:
- Бросить два TransparentPanel объектов на тестовой форме
- Назначьте TransparentPanel1 и TransparentPanel2 обработчикам событий transparentPanel1_Paint и transparentPanel2_Paint.
- Перекрыть две прозрачные панели, , убедившись, что вы не вложите их по ошибке .
- Адаптируйте остальную часть кода (вам нужна только кнопка, здесь с именем btnRotate, назначьте обработчик btnRotate_Click)

private System.Windows.Forms.Timer RotateTimer = null;
private float RotationAngle1 = 90F;
private float RotationAngle2 = 0F;
public bool RotateFigures = false;

public form1()
{
    InitializeComponent();
    RotateTimer = new Timer();
    RotateTimer.Interval = 50;
    RotateTimer.Enabled = false;
    RotateTimer.Tick += new EventHandler(this.RotateTick);
}

protected void RotateTick(object sender, EventArgs e)
{
    RotationAngle1 += 10F;
    RotationAngle2 += 10F;
    transparentPanel1.Invalidate();
    transparentPanel2.Invalidate();
}

private void btnRotate_Click(object sender, EventArgs e)
{
    RotateTimer.Enabled = !RotateTimer.Enabled;
    if (RotateTimer.Enabled == false)
    {
        RotateFigures = false;
        RotationAngle1 = 90F;
        RotationAngle2 = 0F;
    }
    else
    {
        RotateFigures = true;
    }
}


private void transparentPanel1_Paint(object sender, PaintEventArgs e)
{
    if (!RotateFigures) return;
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
    e.Graphics.CompositingMode = CompositingMode.SourceOver;
    Rectangle rect = transparentPanel1.ClientRectangle;
    Rectangle rectInner = rect;

    using (Pen transpPen = new Pen(transparentPanel1.Parent.BackColor, 10))
    using (Pen penOuter = new Pen(Color.SteelBlue, 8))
    using (Pen penInner = new Pen(Color.Teal, 8))
    using (Matrix m1 = new Matrix())
    using (Matrix m2 = new Matrix())
    {
        m1.RotateAt(-RotationAngle1, new PointF(rect.Width / 2, rect.Height / 2));
        m2.RotateAt(RotationAngle1, new PointF(rect.Width / 2, rect.Height / 2));
        rect.Inflate(-(int)penOuter.Width, -(int)penOuter.Width);
        rectInner.Inflate(-(int)penOuter.Width * 3, -(int)penOuter.Width * 3);

        e.Graphics.Transform = m1;
        e.Graphics.DrawArc(transpPen, rect, -4, 94);
        e.Graphics.DrawArc(penOuter, rect, -90, 90);
        e.Graphics.ResetTransform();
        e.Graphics.Transform = m2;
        e.Graphics.DrawArc(transpPen, rectInner, 190, 100);
        e.Graphics.DrawArc(penInner, rectInner, 180, 90);
    }
}

private void transparentPanel2_Paint(object sender, PaintEventArgs e)
{
    if (!RotateFigures) return;
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
    e.Graphics.CompositingMode = CompositingMode.SourceOver;
    Rectangle rect = transparentPanel2.ClientRectangle;
    Rectangle rectInner = rect;

    using (Pen transpPen = new Pen(transparentPanel2.Parent.BackColor, 10))
    using (Pen penOuter = new Pen(Color.Orange, 8))
    using (Pen penInner = new Pen(Color.DarkRed, 8))
    using (Matrix m1 = new Matrix())
    using (Matrix m2 = new Matrix())
    {
        m1.RotateAt(RotationAngle2, new PointF(rect.Width / 2, rect.Height / 2));
        m2.RotateAt(-RotationAngle2, new PointF(rect.Width / 2, rect.Height / 2));
        rect.Inflate(-(int)penOuter.Width, -(int)penOuter.Width);
        rectInner.Inflate(-(int)penOuter.Width * 3, -(int)penOuter.Width * 3);

        e.Graphics.Transform = m1;
        e.Graphics.DrawArc(transpPen, rect, -4, 94);
        e.Graphics.DrawArc(penOuter, rect, 0, 90);
        e.Graphics.ResetTransform();
        e.Graphics.Transform = m2;
        e.Graphics.DrawArc(transpPen, rectInner, 190, 100);
        e.Graphics.DrawArc(penInner, rectInner, 180, 90);
    }
}
...