GaugeControl.cs 5.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Media;
  4. using System;
  5. using System.Globalization;
  6. namespace YZWater.Avalonia.Controls;
  7. public class GaugeControl : Control
  8. {
  9. public static readonly StyledProperty<double> ValueProperty = AvaloniaProperty.Register<GaugeControl, double>(nameof(Value), 0.0);
  10. public static readonly StyledProperty<double> MinValueProperty = AvaloniaProperty.Register<GaugeControl, double>(nameof(MinValue), 0.0);
  11. public static readonly StyledProperty<double> MaxValueProperty = AvaloniaProperty.Register<GaugeControl, double>(nameof(MaxValue), 100.0);
  12. public static readonly StyledProperty<string> TitleProperty = AvaloniaProperty.Register<GaugeControl, string>(nameof(Title), "Flow");
  13. public static readonly StyledProperty<string> UnitProperty = AvaloniaProperty.Register<GaugeControl, string>(nameof(Unit), "m鲁/h");
  14. public static readonly StyledProperty<IBrush> ValueColorProperty = AvaloniaProperty.Register<GaugeControl, IBrush>(nameof(ValueColor));
  15. public double Value { get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); }
  16. public double MinValue { get => GetValue(MinValueProperty); set => SetValue(MinValueProperty, value); }
  17. public double MaxValue { get => GetValue(MaxValueProperty); set => SetValue(MaxValueProperty, value); }
  18. public string Title { get => GetValue(TitleProperty); set => SetValue(TitleProperty, value); }
  19. public string Unit { get => GetValue(UnitProperty); set => SetValue(UnitProperty, value); }
  20. public IBrush ValueColor { get => GetValue(ValueColorProperty); set => SetValue(ValueColorProperty, value); }
  21. static GaugeControl() { AffectsRender<GaugeControl>(ValueProperty, MinValueProperty, MaxValueProperty, TitleProperty, UnitProperty, ValueColorProperty); }
  22. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); ThemeHelper.ThemeChanged += OnThemeChanged; }
  23. protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { ThemeHelper.ThemeChanged -= OnThemeChanged; base.OnDetachedFromVisualTree(e); }
  24. private void OnThemeChanged() => InvalidateVisual();
  25. public override void Render(DrawingContext context)
  26. {
  27. base.Render(context);
  28. var bounds = new Rect(Bounds.Size);
  29. var cx = bounds.Width / 2; var cy = bounds.Height / 2 + 10;
  30. var radius = Math.Min(bounds.Width, bounds.Height) / 2 - 20;
  31. var typeface = new Typeface("Microsoft YaHei", FontStyle.Normal, FontWeight.Bold);
  32. context.DrawEllipse(ThemeHelper.PanelBg, null, new Rect(cx - radius - 5, cy - radius - 5, (radius + 5) * 2, (radius + 5) * 2));
  33. DrawArc(context, new Pen(ThemeHelper.Border, 8), cx, cy, radius, 135, 270);
  34. var normalized = Math.Max(0, Math.Min(1, (Value - MinValue) / (MaxValue - MinValue)));
  35. var valueAngle = 135 + normalized * 270;
  36. var valueColor = ValueColor ?? ThemeHelper.Success;
  37. DrawArc(context, new Pen(valueColor, 8), cx, cy, radius, 135, valueAngle - 135);
  38. for (int i = 0; i <= 10; i++)
  39. {
  40. var angle = 135 + (270.0 * i / 10);
  41. var rad = angle * Math.PI / 180;
  42. var isMajor = i % 5 == 0;
  43. var tickLen = isMajor ? 12 : 6;
  44. context.DrawLine(new Pen(isMajor ? ThemeHelper.TextPrimary : ThemeHelper.TextDisabled, isMajor ? 2 : 1),
  45. new Point(cx + Math.Cos(rad) * (radius - tickLen), cy + Math.Sin(rad) * (radius - tickLen)),
  46. new Point(cx + Math.Cos(rad) * radius, cy + Math.Sin(rad) * radius));
  47. }
  48. var pAngle = valueAngle * Math.PI / 180;
  49. var pLen = radius * 0.7;
  50. context.DrawLine(new Pen(valueColor, 3), new Point(cx, cy), new Point(cx + Math.Cos(pAngle) * pLen, cy + Math.Sin(pAngle) * pLen));
  51. context.DrawEllipse(valueColor, null, new Rect(cx - 6, cy - 6, 12, 12));
  52. var titleText = new FormattedText(Title, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 12, ThemeHelper.TextPrimary);
  53. context.DrawText(titleText, new Point(cx - titleText.Width / 2, bounds.Y + 5));
  54. var valText = new FormattedText($"{Value:F1}", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Microsoft YaHei", FontStyle.Normal, FontWeight.Bold), 20, ThemeHelper.TextPrimary);
  55. context.DrawText(valText, new Point(cx - valText.Width / 2, cy + radius * 0.3));
  56. var unitText = new FormattedText(Unit, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 10, ThemeHelper.TextDisabled);
  57. context.DrawText(unitText, new Point(cx - unitText.Width / 2, cy + radius * 0.3 + valText.Height + 2));
  58. }
  59. private void DrawArc(DrawingContext context, IPen pen, double cx, double cy, double radius, double startAngle, double sweepAngle)
  60. {
  61. var startRad = startAngle * Math.PI / 180;
  62. var endRad = (startAngle + sweepAngle) * Math.PI / 180;
  63. var steps = 36;
  64. var px = cx + Math.Cos(startRad) * radius;
  65. var py = cy + Math.Sin(startRad) * radius;
  66. for (int i = 1; i <= steps; i++)
  67. {
  68. var t = (double)i / steps;
  69. var angle = startRad + (endRad - startRad) * t;
  70. var x = cx + Math.Cos(angle) * radius;
  71. var y = cy + Math.Sin(angle) * radius;
  72. context.DrawLine(pen, new Point(px, py), new Point(x, y));
  73. px = x; py = y;
  74. }
  75. }
  76. protected override Size MeasureOverride(Size availableSize) => new Size(120, 140);
  77. }