1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/yhuse-SunnyUI

 / Детали:

Индустриальные нулевые корректоры (или более точно: Индустриальные нулевые регулировочные элементы)

Предстоит сделать
Владелец
Создано  
04.03.2025

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

Если вам понравится данное решение, вы можете интегрировать его в свой проект.

Мои художественные способности недостаточно высоки, чтобы провести адаптацию и тестирование.

Результат представлен на следующем рисунке:
Пример результата

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace TestMiddleProgress
{
    public class CustomProcess : Control
    {
        private float minValue = 0;
        private float maxValue = 100;
        private float middleValue = 50;
        private float value = 0;
        private const int verticalMargin = 14; // вертикальный отступ сверху и снизу
        private const int horizontalMargin = 14; // горизонтальный отступ слева и справа
        private bool isLog = false; // использовать логарифмическое значение

        public bool IsLog
        {
            get { return isLog; }
            set
            {
                isLog = value;
                Invalidate(); // перерисовать контрол при изменении значения свойства
            }
        }

        public float MinValue
        {
            get { return minValue; }
            set
            {
                minValue = value;
                Invalidate(); // перерисовать контрол при изменении значения свойства
            }
        }

        public float MaxValue
        {
            get { return maxValue; }
            set
            {
                maxValue = value;
                Invalidate();
            }
        }

        public float MiddleValue
        {
            get { return middleValue; }
            set
            {
                middleValue = value;
                Invalidate();
            }
        }

        public float Value
        {
            get { return value; }
            set
            {
                this.value = value;
                Invalidate();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            Rectangle rect = ClientRectangle;
            float _maxValue = isLog ? AsLog(maxValue) : maxValue;
            float _minValue = isLog ? AsLog(minValue) : minValue;
            float _middleValue = isLog ? AsLog(middleValue) : middleValue;
            float _value = isLog ? AsLog(value) : value;

            // Отключаем заполнение фона
            //g.FillRectangle(Brushes.LightGray, rect);

            // Вычисляем диапазон значений
            float range = _maxValue - _minValue;
            float valueRange;
            // Верно вычисляем диапазон между value и middleValue
            if (_value >= _middleValue)
            {
                valueRange = _value - _middleValue;
            }
            else
            {
                valueRange = _middleValue - _value;
            }
            float percentage = valueRange / range;

            // Вычисляем область для отрисовки
            int height = rect.Height - 2 * verticalMargin;
            int width = (int)((rect.Width - 2 * horizontalMargin) * percentage);
            Rectangle drawRect;
            int valueX;
            if (_value >= _middleValue)
            {
                // Вычисляем позицию X для среднего значения
                int middleValueX = (int)((_middleValue - _minValue) / range * (rect.Width - 2 * horizontalMargin)) + horizontalMargin;
                drawRect = new Rectangle(rect.Left + middleValueX, rect.Top + verticalMargin, width, height);
            }
            else
            {
                // Вычисляем позицию X для текущего значения
                valueX = (int)((_value - _minValue) / range * (rect.Width - 2 * horizontalMargin)) + horizontalMargin;
                drawRect = new Rectangle(rect.Left + valueX, rect.Top + verticalMargin, width, height);
            }
            if (drawRect.Width > 0 && drawRect.Height > 0)
            {
                // Заливаем область градиентом
                using (LinearGradientBrush brush = new LinearGradientBrush(drawRect, Color.LightBlue, Color.DarkGreen, LinearGradientMode.Horizontal))
                {
                    g.FillRectangle(brush, drawRect);
                }
            }

            // Отрисовываем текст максимального значения и стрелку
            string maxText = maxValue.ToString();
            SizeF maxTextSize = g.MeasureString(maxText, Font);
            maxTextSize = new SizeF(maxTextSize.Width + 6, maxTextSize.Height); // коррекция размера текста
            int maxValueX = (int)((_maxValue - _minValue) / range * (rect.Width - 2 * horizontalMargin)) + horizontalMargin;
            g.DrawString(maxText, Font, Brushes.Black, rect.Left + maxValueX - maxTextSize.Width / 2, rect.Top);
            DrawYArrow(g, Color.Gray, rect.Left + maxValueX, rect.Top + verticalMargin, 4);

            // Отрисовываем текст минимального значения и стрелку
            string minText = minValue.ToString();
            SizeF minTextSize = g.MeasureString(minText, Font);
            minTextSize = new SizeF(minTextSize.Width + 6, minTextSize.Height); // коррекция размера текста
            int minValueX = (int)((_minValue - _minValue) / range * (rect.Width - 2 * horizontalMargin)) + horizontalMargin;
            g.DrawString(minText, Font, Brushes.Black, rect.Left + minValueX - minTextSize.Width / 2, rect.Top);
            DrawYArrow(g, Color.Gray, rect.Left + minValueX, rect.Top + verticalMargin, 4);

            // Отрисовываем текст среднего значения и стрелку
            string midText = middleValue.ToString();
            SizeF midTextSize = g.MeasureString(midText, Font); // для среднего значения коррекция размера текста не требуется
            int midValueX = (int)((_middleValue - _minValue) / range * (rect.Width - 2 * horizontalMargin)) + horizontalMargin;
            g.DrawString(midText, Font, Brushes.Black, rect.Left + midValueX - midTextSize.Width / 2, rect.Top);
            DrawXArrow(g, Color.Gray, rect.Left + midValueX, rect.Top + verticalMargin, rect.Left + midValueX, rect.Bottom - verticalMargin, 4);

            // Отрисовываем текст текущего значения и стрелку
            string valueText = value.ToString();
            SizeF valueTextSize = g.MeasureString(valueText, Font);
            if (_value >= _middleValue)
            {
                valueX = (int)((_value - _minValue) / range * (rect.Width - 2 * horizontalMargin)) + horizontalMargin;
            }
            else
            {
                valueX = (int)((_value - _minValue) / range * (rect.Width - 2 * horizontalMargin)) + horizontalMargin;
            }
            g.DrawString(valueText, Font, Brushes.Black, rect.Left + valueX - valueTextSize.Width / 2, rect.Bottom - valueTextSize.Height);
            //DrawYArrow(g,Color.Red, rect.Left + valueX, rect.Bottom - verticalMargin, 4,true); // отрисовка после указания значения приводит к плохому совпадению
        }

        private float AsLog(float value)
        {
            if (value == 0)
            {
                return 0;
            }
            return (float)Math.Log(Math.Abs(value)) * (value > 0 ? 1 : -1);
        }
}
``````csharp
        /// <summary>
        /// Draws a double arrow for the Y-axis.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="x1">the X-coordinate of the first point</param>
        /// <param name="y1">the Y-coordinate of the first point</param>
        /// <param name="size">arrow size</param>
        private void DrawYArrow(Graphics g, Color color, int x1, int y1, int size, bool IsUp = false)
        {
            using (Pen pen = new Pen(color, 2))
            {
                if (!IsUp)
                {
                    g.DrawLine(pen, x1, y1, x1 - size, y1 - size);
                    g.DrawLine(pen, x1, y1, x1 + size, y1 - size);
                    g.DrawLine(pen, x1, y1 - 4, x1 - size, y1 - size - 4);
                    g.DrawLine(pen, x1, y1 - 4, x1 + size, y1 - size - 4);
                }
                else
                {
                    g.DrawLine(pen, x1, y1, x1 - size, y1 + size);
                    g.DrawLine(pen, x1, y1, x1 + size, y1 + size);
                    g.DrawLine(pen, x1, y1 + 4, x1 - size, y1 + size + 4);
                    g.DrawLine(pen, x1, y1 + 4, x1 + size, y1 + size + 4);
                }
            }
        }

        /// <summary>
        /// Draws a double arrow for the X-axis.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="x1">the X-coordinate of the first point</param>
        /// <param name="y1">the Y-coordinate of the first point</param>
        /// <param name="x2">the X-coordinate of the second point</param>
        /// <param name="y2">the Y-coordinate of the second point</param>
        /// <param name="size">arrow size</param>
        private void DrawXArrow(Graphics g, Color color, int x1, int y1, int x2, int y2, int size)
        {
            using (Pen pen = new Pen(color, 2))
            {
                g.DrawLine(pen, x1, y1, x1 - size, y1 - size);
                g.DrawLine(pen, x1, y1, x1 + size, y1 - size);
                g.DrawLine(pen, x1, y1, x2, y2);
                g.DrawLine(pen, x2, y2, x2 - size, y2 + size);
                g.DrawLine(pen, x2, y2, x2 + size, y2 + size);
            }
        }
    }

Комментарий (0)

GitLife Service Account Задача создана

Вход Перед тем как оставить комментарий

Статус
Ответственный
Контрольная точка
Pull Requests
Связанные запросы на слияние могут быть закрыты после их объединения
Ветки
Дата начала   -   Крайний срок
-
Закрепить/Открепить
Приоритет
Участники(1)
1
https://api.gitlife.ru/oschina-mirror/yhuse-SunnyUI.git
git@api.gitlife.ru:oschina-mirror/yhuse-SunnyUI.git
oschina-mirror
yhuse-SunnyUI
yhuse-SunnyUI