Краткий листинг программного кода класса 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:
// Таймер счетчика
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;
}
}
}