Градиентный индикатор ProgressRing

исходники++ / Язык программирования C# / Градиентный индикатор ProgressRing
Оглавление:
  1. Кольцевой индикатор ProgressRing
  2. Скриншоты индикатора ProgressRing
  3. Относительная размерность
  4. Настраиваемые виды ProgressRing
  5. Сегментная закраска
  6. Скругленные прямоугольники
  7. Кольцевой градиент
  8. Настройки элемента ProgressRing
  9. Исходный код индикатора ProgressRing

Кольцевой индикатор ProgressRing

Кольцевой индикатор прогресса ProgressRing дополняет тему цветных программных индикаторов для приложений C# Windows Forms, описанных на страницах ProgressLinear - индикатор прогресса и ProgressRadial - радиальный индикатор прогресса. Градиентный индикатор ProgressRing визуализирует прогрессию выполнения задач способом увеличения количества или угла раскрытия сегментов по кольцевой траектории.

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

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

Скриншоты индикатора ProgressRing

Режим работы отсчета по значению OptionsModeWork.ByPosition предназначен для отображения процессов с заранее вычисляемыми значениями. На анимационном скриншоте изображены варианты видов цветных индикаторов ProgressRing в режиме ByPosition:

Для визуализации активности интерфейса при выполнении задач с неизвестным временем выполнения подойдет режим OptionsModeWork.Animation. Скриншот ниже отображает работу индикатора в режиме анимации:

Относительная размерность

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

Относительные размеры индикатора ProgressRing

Программный код метода обработки события изменения размеров элемента. Размеры габаритных прямоугольников компонентов индикатора строятся на базовом максимальном прямоугольнике.

protected override void OnResize(EventArgs e)
{
    // Защита от исключений при нулевых и отрицательных
    // параметрах рабочих прямоугольников.
    if (Width < 5 || Height < 5) return;

    // Наибольший рабочая область индикатора.
    // 1.0f - поправка для удаления артефакта сглаживания линий, когда
    // линии становятся толще и в результате верхний и нижний края срезаются.
    // Уменьшение размера на 1 единицу дает правильную отрисовку линий краев.
    _rectMax = new RectangleF(
        ClientRectangle.X + 1f,
        ClientRectangle.Y + 1f,
        ClientRectangle.Width - 2.0f,
        ClientRectangle.Height - 2.0f);

    // Толщина рамки в процентах от максимального размера индикатора.
    // Максимально 12.5%.
    float framethickness = (_rectMax.Height / 100f) * 0.125f * _frameThickness;

    // Размер области фона индикатора
    _rectBG = new RectangleF(
        _rectMax.X + framethickness,
        _rectMax.Y + framethickness,
        _rectMax.Width - framethickness * 2,
        _rectMax.Height - framethickness * 2);

    // Ширина зазора между рамкой и фигурой прогресса.
    // Максимально 12.5% от максимального размера индикатора.
    // Т.е. рамка и зазор вместе не более 25% от максимального
    // размера индикатора.
    // Остальная часть приходится на сам индикатор.
    float gap = (_rectMax.Height / 100f) * 0.125f * _frameGap;

    // Размеры кольца индикатора.
    _rectProgress = new RectangleF(
        _rectBG.X + gap,
        _rectBG.Y + gap,
        _rectBG.Width - gap * 2,
        _rectBG.Height - gap * 2);

    // Толщина кольца в процентах от размера индикатора
    // минус толщина рамки и ширина зазора между рамкой.
    // Толщина кольца максимум 70%.
    float ratio = _ringThickness / 100f;
    float rx = _rectProgress.Width * ratio * 0.30f;
    float ry = _rectProgress.Height * ratio * 0.30f;

    // Размер поля внутри кольца.
    _rectInternal = new RectangleF(
        _rectProgress.X + rx,
       _rectProgress.Y + ry,
       _rectProgress.Width - rx * 2,
       _rectProgress.Height - ry * 2);

    // Последним вычисляется размер шрифта.
    ComputeHeightText(_rectInternal.Width, _rectInternal.Height);

    Invalidate();
}

Настраиваемые виды ProgressRing

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

/// <summary>
/// Вид индикатора
/// </summary>
public enum TypeIndicator
{
    Rounded,
    Segments,
    Solid,
}
. . .

[Description("Вид индикатора."), Category("ProgressSettings")]
public TypeIndicator TypeIndicator
{
    get => _typeIndicator;
    set
    {
        _typeIndicator = value;
        // Пересчет размеров и положений рамки и фигуры индикатора.
        OnResize(new EventArgs());
    }
}

Цветовые возможности ProgressRing представлены настройками цветных сплошных и одноцветных градиентных кистей. Создание объектов SolidBrush в программном коде класса ProgressRing:

SolidBrush _progressColor = new();

// Одноцветная кисть сплошной закраски.
_progressColor.Color = Color.FromArgb(color.R, color.G, color.B);

// Одноцветная кисть для создания полупрозрачной градиентной закраски.
_progressColor.Color = Color.FromArgb(alpha, color.R, color.G, color.B);

Сегментная закраска

Сегменты кольца индикатора создаются усеченными секторами графической фигуры Pie рисуемой объектом Graphics C# Windows Forms. Начальный и конечный углы каждого сегмента вычисляются в режиме реального времени используя входные данные:
количество сегментов ProgressRing._segmentsNumber и
значение зазора между сегментами ProgressRing._figureGap.

Листинг кода метода рисования сегментов в режимах анимации и по позиции в зависимости от установленных пользователем настроек:

/// <summary>
/// Рисование сегментированной фигуры прогресса.
/// </summary>
private void RingPieSegments(Graphics g)
{
    // Вычисление угла раскрытия индикатора.
    float anglePos = ComputeAnglesPos();

    // Угол каждого сегмента кольца.
    float segmentAngle = 360.0f / (float)_segmentsNumber;

    // Вычисление зазора на основании соотношения 
    // установленного пользователем.
    // Полная ширина сегмента 100%, делится на зазор и фигурки.
    float k = _figureGap / 100f;
    float gap = k * segmentAngle;

    // Угол раскрытия сегмента, начинается от стартового угла.
    // Уменьшается на величину зазора между фигурами.
    float sweepAngle = segmentAngle - gap;

    // Селектор режимов индикатора.
    switch (_modeWork)
    {
        case OptionsModeWork.ByPosition:
            RingPieSegmentsByPosition(g, anglePos, segmentAngle, sweepAngle);
            break;

        case OptionsModeWork.Animation:
            RingPieSegmentsAnimation(g, anglePos, segmentAngle, sweepAngle);
            break;
    }


    // Внутренний эллипс обрезает сегменты, формирует кольцо.
    g.FillEllipse(_bgColor,
       _rectInternal.X,
       _rectInternal.Y,
       _rectInternal.Width,
       _rectInternal.Height);

    // Показ процентов прогресса выполнения операции.
    if (_modeWork == OptionsModeWork.ByPosition && _percentVisible == true) DrawPercent(g, anglePos);
}

Скругленные прямоугольники

Скругленные прямоугольники индикатора ProgressRing

Фигуры скругленных прямоугольников для индикатора ProgressRing рисуются двумя эллипсами и одним, соединяющим их прямоугольником. Для вычисления положения нижнего эллипса создан рекурсивный метод ProgressRing.ComputeRecursiveDiameterEllipse(..).

Для чего понадобилось рекурсивное вычисление? Нижние эллипсы скругленных прямоугольников не должны накладываться на область рисования символов текста внутри кольца и при нулевом зазоре должны касаться друг друга. Для расчета диаметров эллипсов есть только окружность внутренней области и количество фигур:
диаметр = длина окружности/кол-во фигур.
С этим результатом мы получаем диаметр эллипса при его расположении на окружности внутренней области.

Расчет диаметра эллипса рекурсивным методом

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

Но в таком случае увеличенная площадь эллипса вновь накладывается на внутреннюю область и вновь требуется подъем эллипса и вновь диаметр увеличивается, но уже на ме́ньшую величину и т. д.

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

Программный код метода рисования скругленных прямоугольников:

/// <summary>
/// Рисование кольца из отдельных скругленных прямоугольников.
/// </summary>
/// <param name="g"></param>
private void RectangleRounded(Graphics g)
{
    // Вычисление конечного угла раскрытия кольца индикатора.
    float anglePos = ComputeAnglesPos();

    // Угол позиции одного прямоугольника.
    float segmentAngle = 360.0f / (float)_segmentsNumber;

    // Вычисление зазора между фигурами и положения фигур
    // на окружности внутренней области.
    float k = _figureGap / 100f;
    float gap = (_rectInternal.Height * MathF.PI / _segmentsNumber) * k;
    float sizeEllipse = ComputeRecursiveDiameterEllipse(0, gap, _segmentsNumber);

    switch (_modeWork)
    {
        case OptionsModeWork.ByPosition:
            RectangleRoundedByPosition(g, anglePos, segmentAngle, sizeEllipse);
            break;
        case OptionsModeWork.Animation:
            RectangleRoundedAnimation(g, segmentAngle, sizeEllipse);
            break;
    }

    // Показывается проценты прогресса выполнения операции.
    if (_modeWork == OptionsModeWork.ByPosition && _percentVisible == true) DrawPercent(g, anglePos);
}

Кольцевой градиент

Радиальный vs кольцевой градиент

Кольцевая градиентная закраска – это интересная задача для графики C# Windows Forms, которая предоставляет нам только кисть радиальной закраски PathGradientBrush.

В исходнике ProgressRing эффект кольцевой градиентной закраски обеспечивает сочетание сегментов геометрической фигуры Pie с различными графическими настройками.

Для создания фигуры индикатора ProgressRing со сплошной градиентной закраской применяются 3 метода: ProgressRing.PieGradientByPosition(..) и ProgressRing.PieGradientAnimation(..) и метод-диспетчер ProgressRing.RingPieGradient(..).

Программный код распределяющего метода градиентной закраски кольца индикатора:

/// <summary>
/// Для эффекта кольцевой градиентной закраски
/// будем использовать сегменты.
/// Из-за сложности кольцевого градиентного рисования
/// создана отдельная данная функция.
/// </summary>
/// <param name="g"></param>
private void RingPieGradient(Graphics g)
{
    // Вычисление начальных и конечных углов прогресса.
    float anglePos = ComputeAnglesPos();


    // Селектор режимов
    switch (_modeWork)
    {
        case OptionsModeWork.ByPosition:
            PieGradientByPosition(g, anglePos);
            break;

        case OptionsModeWork.Animation:
            PieGradientAnimation(g);
            break;
    }


    // Внутренний круг фона для формирования кольца.
    g.FillEllipse(_bgColor,
       _rectInternal.X,
       _rectInternal.Y,
       _rectInternal.Width,
       _rectInternal.Height);

    // Показывается проценты прогресса выполнения операции.
    if (_modeWork == OptionsModeWork.ByPosition && _percentVisible == true) DrawPercent(g, anglePos);
}

Настройки элемента ProgressRing

Ниже описаны открытые свойства для создания кольцевого индикатора требуемого вида.

Для стартового состояния индикатора перед включением анимационного режима используется режим по позиции с выключенными символами процентов и установленное свойство Position=0.

Категория ProgressSettings:
  • bool Alpha – включение полупрозрачной градиентной закраски кольца индикатора.
  • byte AlphaStart – начальное значение прозрачности градиента.
  • uint FigureGap – Зазор между сегментами, от 0 до 100. Фигура и зазор составляют 100%, при значении зазора – 100, фигура уменьшается до нуля.
  • uint RingThickness – отношение толщины кольца к радиусу области фигуры индикатора процентах, от 0 до 100". Но даже при максимальных 100% сегменты кольца не перекрывают символы процентов.
  • uint SegmentsNumber – количество сегментов кольца.
  • TypeIndicator TypeIndicator – вид кольца индикатора: сплошное кольцо, усеченные сегменты круга, скругленные прямоугольники.
  • OptionsModeWork ModeWork – режим работы индикатора: по позиции или анимация
  • bool FrameVisible – видимость рамки вокруг элемента
  • int FrameThickness – толщина рамки
  • Color FrameColor – цвет рамки
  • Color ProgressColor – цвет сектора прогресса и анимации
  • Color BGColor – цвет заднего фона индикатора
  • int FrameGap – Зазор между фигурой прогресса и рамкой
Категория ProgressPosition:
  • int Position – положение активной линии радиуса радиального индикатора
  • int Maximum – максимальное значение позиции равно 100. Свойство только для чтения.
  • bool PercentVisible – видимость строки процентов
  • float FontZoom – масштабирование шрифта, меньше единицы - уменьшение
Категория ProgressAnimation:
  • float AnimationSpeed – Скорость анимации 1 - 100. Наивысшая скорость - 100 единиц.
Наследуемые от класса Control свойства:
  • System.Drawing.Color BackColor – задний фон элемента на котором располагается кольцо индикатора.
  • System.Drawing.Color ForeColor – цвет шрифта символов процентов.

Исходный код индикатора ProgressRing

Исходник кольцевого индикатора написан языке C# для библиотеки для библиотеки Windows Forms, программная среда MS Visual Studio 2022 .NET6.

Программный код может быть понижен до версии языка C# 7.3 и адаптироваться под проекты в средах от .NET3.1 и выше, .NET Framework 4.0 и выше.

Архивный файл исходника содержит тестовое приложение с классом ProgressRing. Класс ProgressRing физически находится в папке Indicators. Индикатор добавляется в приложение C# Windows Forms копированием в папки Indicators в проект приложения.

Доступ к исходнику

Для скачивания исходника необходимо ввести код:

Оплата кода доступа

Стоимость доступа к исходнику - 150 ₽

Сразу после оплаты активируется ссылка на скачивание исходника. Также, на указанный e-mail высылается код доступа. Код действует 2 суток. Внимание: письмо может попасть в папку спама.

Скачать исходник

Тема: «Градиентный индикатор ProgressRing» Пример работы кольцевого индикатора ProgressRing💾TestProgressRing-vs17.zipРазмер:197 КбайтЗагрузки:17