WPF создание графического элемента

Все исходники / Язык программирования C# / OS Windows / Desktop / WPF программирование / WPF создание графического элемента
Оглавление:
  1. Пользовательский графический элемент Цифра
  2. Создание графики из Polygon
  3. XAML пользовательского интерфейса
  4. Класс Digit
  5. Счетчик на графическом элементе Digit
  6. Исходник графического пользовательского элемента

Пользовательский графический элемент Цифра

WPF пользовательский графический элемент Цифра
Пользовательский графический элемент

Пользовательский элемент Digit предназначен для графической симуляции семисегментной цифры электронных дисплеев. Элемент Digit может использоваться в качестве индикатора текущий процессов и для повышения привлекательности пользовательских интерфейсов.

Графический элемент Digit для платформы WPF создан как класс производный от Panel. Наследование от класса Panel дает возможность элементу Digit способность размещения и упорядочения дочерних Polygon в разметке XAML и программном коде. Класс Digit написан на языке быстрой разработки C#.

Создание графики из Polygon

Последовательность создания графики из полигонов

Графическое векторное изображение семисегментной цифры создается в такой последовательности:
- из полигонов (System.Windows.Shapes.Polygon) создаются ромбовидные фигуры вплотную друг к другу для заготовки цифры;
- далее производится клипирование (System.Windows.Media.Geometry Clip), т. е. вырезание сегментов цифры из созданных ромбов. Для каждой фигуры определяется соответствующая прямоугольная геометрия отсечения;
- области, не входящие в прямоугольники, отсекаются и не рисуются системой. В результате образуются четкие сегменты электронной цифры.

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

XAML пользовательского интерфейса

Пользовательский графический элемент Digit наследуя возможности Panel позволяет разрабатывать интерфейс в дизайнерском режиме. Для этого надо добавить элемент Digit в разметку XAML и далее атрибутами определить требуемые значения доступных свойств.

Кроме свойств самого пользовательского элемента Digit (например, цвет сегментов цифры) доступны для определений свойства родительского класса Panel (например, ширина, координаты размещения).

<Window x:Class="WpfCustomGraphics.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:mycode="clr-namespace:WpfCustomGraphics" 
        mc:Ignorable="d"
        Title="Пользовательский элемент Цифра" Height="600" 
        Width="800" Background="White">

    <Grid  x:Name="maingrid" Margin="5,5,5,5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <!--Пользовательские элементы Цифра-->
        <StackPanel Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" 
        HorizontalAlignment="Center" VerticalAlignment="Center"  
        Orientation="Horizontal">
            <mycode:Digit x:Name="digit1" Width="80" Margin="5"/>
            <mycode:Digit x:Name="digit2" Width="80"  Margin="5"/>
        </StackPanel>

        <!--Кнопки управления-->
        <StackPanel Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" 
        VerticalAlignment="Center">
            <Button Content="Start"  Click="Button_Click" Margin="5" 
            MinWidth="120" MinHeight="30"/>
            <Button Content="Zoom"  Click="Button_Click" Margin="5" 
            MinWidth="120" MinHeight="30"/>
        </StackPanel>
    </Grid>
</Window>

Класс Digit

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

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

Отношение ширины к высоте элемента Digit имеет фиксированное значение. Размер семисегментной цифры определяет только свойство Width родительского класса Panel, значение Height автоматически вычисляется после изменения ширины элемента.

Для правильного размещения и отображения фигур Polygon, пользовательский элемент Digit переопределяет родительские методы FrameworkElement.MeasureOverride(Size) и FrameworkElement.ArrangeOverride(Size) для, соответственно, измерения сегментов и их расстановки в пределах области элемента. После выполнения своих обязанностей родительскими методами окончательная корректировка размера элемента Digit производится методом Digit.ComputeSize().

Краткий листинг программного кода класса Digit
public class Digit : Panel
{
    #region Поля и свойства

    // Ширина цифры без фаски и промежутков.
    double widthBlankSegment = 100;

    // Высота квадрата верхней или нижней половинок цифры.
    // Цифра состоит из верхней и нижней половинок.
    public double heightBlankSegment = 100;

    // Толщина сегментов - обрезанных (клипированных) полигонов.
    double thicknessSegment = 30;

    // Смещение точек линий смежных полигонов
    // к центральному полигону (элементу).
    // Для уравнивания толщины центрального сегмента
    double correctCenter = 10;

    // Смещение сегментов цифры друг от друга,
    // значение изменяет промежуток между сегментами. 
    double _strokeThickness = 1;
    public double StrokeThickness
    {
        get { return _strokeThickness; }
        set { _strokeThickness = value; }
    }

    // Цвет сегментов цифры.
    SolidColorBrush _colorDigit = Brushes.Black;
    public SolidColorBrush ColorDigit
    {
        get { return _colorDigit; }
        set
        {
            _colorDigit = value;
            // Если в XAML был сброс или в других случаях,
            // то восстанавливаем кисть по умолчанию.
            if (value == null) _colorDigit = Brushes.Black;

            // Перерисовываем сегменты.
            DrawDigit(0, 0);
        }
    }

    #endregion


    #region Инициализация

    public Digit() : base()
    {
        // Размер цифры по умолчанию
        Width = 100;

        // Инициализация полигонов - сегментов цифры,
        // и добавления их в коллекцию дочерних сегментов 
        // родительской панели.
        for (int i = 0; i < 7; i++)
        {
            Children.Add(new Polygon());
        }
    }

    #endregion


    #region Управление значением цифры

    // Визуализация значений цифры.
    public void ValueDigit(int digit)
    {
        switch (digit)
        {
            case 0:
                // Left
                Children[0].Opacity = 1;
                // Top
                Children[1].Opacity = 1;
                // Right
                Children[2].Opacity = 1;
                // Center
                Children[3].Opacity = 0;
                // LeftBottom
                Children[4].Opacity = 1;
                // Bottom
                Children[5].Opacity = 1;
                // RightBottom
                Children[6].Opacity = 1;

                break;
            case 1:
                // Left
                Children[0].Opacity = 0;
                // Top
                Children[1].Opacity = 0;
                // Right
                Children[2].Opacity = 1;
                // Center
                Children[3].Opacity = 0;
                // LeftBottom
                Children[4].Opacity = 0;
                // Bottom
                Children[5].Opacity = 0;
                // RightBottom
                Children[6].Opacity = 1;

                break;
            case 2:
                ...
                break;
            case 3:
                ...
                break;
            case 4:
                ...
                break;
            case 5:
                ...
                break;
            case 6:
                ...
                break;
            case 7:
                ...
                break;
            case 8:
                ...
                break;
            case 9:
                ...
                break;
        }
    }

    #endregion


    #region Вычисление размеров для сегментов цифры

    private void ComputeSize()
    {
        // Вычисление ширины цифры на основе заданной пользователем общей ширины.
        widthBlankSegment = Width - (2 * thicknessSegment / 3 + 
            _strokeThickness * 2);

        // Высота сегментов половинки и ширина цифры всегда одинаковые.
        // Цифра состоит из верхней половинки и нижней половинки.
        heightBlankSegment = widthBlankSegment;

        // Корректировка толщины центрального сегмента,
        // для уравнивания с толщинами других сегментов.
        correctCenter = widthBlankSegment / 8;

        // Корректировка толщины сегментов цифры.
        thicknessSegment = widthBlankSegment / 4;

        // Вычисление общей высоты цифры с верхней и нижней половинками.
        Height = heightBlankSegment * 2 + 2 * thicknessSegment / 3 + 
            _strokeThickness * 2 + _strokeThickness * 2;

        // Рисование полной цифры с новыми размерами.
        DrawDigit(0, 0);
    }

    #endregion


    #region Рисование графики элементов цифры

    void DrawDigit(double x, double y)
    {
        x = x +
            // Смещение вправо на фаску левых сегментов
            thicknessSegment / 3 +
            // Смещение вправо на толщину промежутка
            // (смещение левых сегментов вправо для создания промежутка).
            _strokeThickness;

        // Смещение по высоте на фаску сегмента и
        // межсегментного промежутка 
        // (смещения верхнего элемента вверх для создания промежутка между сегментами).
        y = y + thicknessSegment / 3 + _strokeThickness;

        // Рисование сегментов цифры.
        SegmentLeft(x, y, _colorDigit, _strokeThickness);
        SegmentTop(x, y, _colorDigit, _strokeThickness);
        SegmentRight(x, y, _colorDigit, _strokeThickness);
        SegmentCenter(x, y, _colorDigit, _strokeThickness);
        SegmentLeftBottom(x, y, _colorDigit, _strokeThickness);
        SegmentBottom(x, y, _colorDigit, _strokeThickness);
        SegmentRightBottom(x, y, _colorDigit, _strokeThickness);
    }

    void SegmentLeft(double x, double y, SolidColorBrush color, double strokeThickness)
    {
        Polygon pg = (Polygon)Children[0];
        PointCollection Points = new()
        {
            // left
            new System.Windows.Point(x - widthBlankSegment / 2, y + 
                heightBlankSegment / 2),
            // top
            new Point(x, y),
            // right
            new Point(x + widthBlankSegment / 2, y + heightBlankSegment / 2),
            // right2
            new Point(x + widthBlankSegment / 2, y + heightBlankSegment / 2 + 
                correctCenter /*корректирвка центрального*/),
            // bottom
            new Point(x, y + heightBlankSegment)
        };
        pg.Points = Points;
        pg.Fill = color;

        TranslateTransform tt = new()
        {
            X = -strokeThickness
        };
        pg.RenderTransform = tt;

        // Обрезание прямоугольного полигона до трапеции.
        RectangleGeometry rg = new()
        {
            Rect = new Rect(x - thicknessSegment / 3, y, 
                thicknessSegment, heightBlankSegment)
        };
        pg.Clip = rg;
    }

    void SegmentTop(double x, double y, SolidColorBrush color, double strokeThickness)
    {
        ...
    }

    void SegmentRight(double x, double y, SolidColorBrush color, double strokeThickness)
    {
        ...
    }

    void SegmentCenter(double x, double y, SolidColorBrush color, double strokeThickness)
    {
        ...
    }

    void SegmentLeftBottom(double x, double y, SolidColorBrush color, double strokeThickness)
    {
        ...
    }

    void SegmentBottom(double x, double y, SolidColorBrush color, double strokeThickness)
    {
        ...
    }

    void SegmentRightBottom(double x, double y, SolidColorBrush color, double strokeThickness)
    {
        ...
    }

    #endregion


    #region Переопределенные  методы класса Panel


    // Измерение дочерних элементов
    protected override Size MeasureOverride(Size availableSize)
    {
        Size panelDesiredSize = new();

        foreach (UIElement child in InternalChildren)
        {
            // Метод измерения Measure должен вызываться для 
            // каждого дочернего элемента Panel,
            // в противном случае дочерние элементы 
            // не будут иметь правильного размера или упорядочения.
            child.Measure(availableSize);
            panelDesiredSize = child.DesiredSize;
        }

        return panelDesiredSize;
    }


    // Размещение дочерних элементов
    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement child in InternalChildren)
        {
            // Родительский класс Panel будет вызывать Arrange(Rect)
            // для каждого дочернего элемента,
            // в противном случае дочерние элементы не будут отображаться правильно.
            child.Arrange(new Rect(new Point(), child.DesiredSize));
        }

        // Вычисление размеров всей цифры в зависимости от ширины панели.
        ComputeSize();

        return finalSize;
    }

    #endregion
}

Счетчик на графическом элементе Digit

Пример применения класса электронных цифр Digit показан как приложение двухразрядного счетчика. Программа демонстрации работы счетчика прикреплена к архиву исходника. Анимацию работы программы можно посмотреть выше.

Программный код управления приложением двухразрядного счетчика на электронных цифрах:
 // Таймер счетчика
readonly DispatcherTimer dispatcherTimer;

public MainWindow()
{
    InitializeComponent();

    // Инициализация и запуск таймера.
    dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
    dispatcherTimer.Tick += DispatcherTimer_Tick; ;
    dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
}

int count = 0;
private void DispatcherTimer_Tick(object? sender, EventArgs e)
{
    // Разряд - десятки
    int pos1 = count / 10;
    // Разряд - единицы
    int pos2 = count % 10;

    digit1.ValueDigit(pos1);
    digit2.ValueDigit(pos2);

    // Периодические изменения цвета цифр.
    if (pos1 % 4 == 0) digit1.ColorDigit = digit2.ColorDigit = Brushes.Red;
    else if (pos1 % 3 == 0) digit1.ColorDigit = digit2.ColorDigit = Brushes.Green;
    else if (pos1 % 2 == 0) digit1.ColorDigit = digit2.ColorDigit = Brushes.Black;
    else digit1.ColorDigit = digit2.ColorDigit = Brushes.Blue;

    // Считаем до 100
    // и сброс в начало отсчета.
    count++;
    if (count == 100) count = 0;
}

// Обработчик события нажатия на кнопки управления.
private void Button_Click(object sender, RoutedEventArgs e)
{
    if(sender is Button button)
    {
        string? text = button.Content.ToString();

        switch (text)
        {
            case "Start":
                if (dispatcherTimer.IsEnabled == false) dispatcherTimer.Start();
                else dispatcherTimer.Stop();
                break;
            case "Zoom":
                if (digit1.Width == 80) digit1.Width = digit2.Width = 200;
                else digit1.Width = digit2.Width = 80;
                break;
        }
    }
}

Исходник графического пользовательского элемента

Исходник семисегментной цифры написан на языке C# .NET6 в среде MS Visual Studio 2022. В состав файла архива входит файлы проекта и скомпилированный файл программы демонстрирования графического счетчика на электронных цифрах.

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

Тема: «WPF создание графического элемента» Язык программирования C# WpfCustomGraphics-vs17.zip Размер:134 КбайтЗагрузки:124