agrobot_base/AgroBase/OperationControl/Controls/TemperatureIndicator.xaml.cs

157 lines
6.8 KiB
C#
Raw Normal View History

using System.Windows;
using System.Windows.Media.Animation;
using System.Windows.Media;
2025-11-24 16:05:50 +00:00
using UserControl = System.Windows.Controls.UserControl;
using Color = System.Windows.Media.Color;
namespace OperationControl.Controls
{
/// <summary>
/// Interaction logic for TemperatureIndicator.xaml
/// </summary>
public partial class TemperatureIndicator : UserControl
{
public TemperatureIndicator()
{
InitializeComponent();
Loaded += (_, __) => UpdateMarker();
SizeChanged += (_, __) => UpdateMarker();
}
// ===== Dependency Properties =====
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(double), typeof(TemperatureIndicator),
new PropertyMetadata(0d, OnValueChanged));
public static readonly DependencyProperty MinProperty =
DependencyProperty.Register(nameof(Min), typeof(double), typeof(TemperatureIndicator),
new PropertyMetadata(0d, OnAnyRangeChanged));
public static readonly DependencyProperty MaxProperty =
DependencyProperty.Register(nameof(Max), typeof(double), typeof(TemperatureIndicator),
new PropertyMetadata(80d, OnAnyRangeChanged));
public static readonly DependencyProperty WarnStartProperty =
DependencyProperty.Register(nameof(WarnStart), typeof(double), typeof(TemperatureIndicator),
new PropertyMetadata(50d));
public static readonly DependencyProperty CritStartProperty =
DependencyProperty.Register(nameof(CritStart), typeof(double), typeof(TemperatureIndicator),
new PropertyMetadata(55d));
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register(nameof(Label), typeof(string), typeof(TemperatureIndicator),
new PropertyMetadata("Temperatura"));
public static readonly DependencyProperty UnitsProperty =
DependencyProperty.Register(nameof(Units), typeof(string), typeof(TemperatureIndicator),
new PropertyMetadata(" °C"));
public static readonly DependencyProperty AnimateOnChangeProperty =
DependencyProperty.Register(nameof(AnimateOnChange), typeof(bool), typeof(TemperatureIndicator),
new PropertyMetadata(true));
public static readonly DependencyProperty BlinkOnCriticalProperty =
DependencyProperty.Register(nameof(BlinkOnCritical), typeof(bool), typeof(TemperatureIndicator),
new PropertyMetadata(true));
// ===== CLR wrappers =====
public double Value { get => (double)GetValue(ValueProperty); set => SetValue(ValueProperty, value); }
public double Min { get => (double)GetValue(MinProperty); set => SetValue(MinProperty, value); }
public double Max { get => (double)GetValue(MaxProperty); set => SetValue(MaxProperty, value); }
public double WarnStart { get => (double)GetValue(WarnStartProperty); set => SetValue(WarnStartProperty, value); }
public double CritStart { get => (double)GetValue(CritStartProperty); set => SetValue(CritStartProperty, value); }
public string Label { get => (string)GetValue(LabelProperty); set => SetValue(LabelProperty, value); }
public string Units { get => (string)GetValue(UnitsProperty); set => SetValue(UnitsProperty, value); }
public bool AnimateOnChange { get => (bool)GetValue(AnimateOnChangeProperty); set => SetValue(AnimateOnChangeProperty, value); }
public bool BlinkOnCritical { get => (bool)GetValue(BlinkOnCriticalProperty); set => SetValue(BlinkOnCriticalProperty, value); }
// ===== Callbacks =====
static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (TemperatureIndicator)d;
ctl.UpdateMarker(animated: ctl.AnimateOnChange);
ctl.UpdateCriticalBlink();
}
static void OnAnyRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (TemperatureIndicator)d;
ctl.UpdateMarker();
}
// ===== Marker positioning + animation =====
void UpdateMarker(bool animated = false)
{
if (!IsLoaded) return;
var barWidth = ActualWidth - 12; // margem interna aproximada
if (barWidth <= 0) return;
double clamped = Math.Max(Min, Math.Min(Max, Value));
double t = (clamped - Min) / Math.Max(1e-6, (Max - Min));
double targetX = t * barWidth;
if (MarkerTT == null) return;
if (animated)
{
var anim = new DoubleAnimation
{
To = Math.Max(0, targetX - 5), // centraliza o triângulo de 10px
Duration = TimeSpan.FromMilliseconds(140),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
MarkerTT.BeginAnimation(TranslateTransform.XProperty, anim, HandoffBehavior.SnapshotAndReplace);
}
else
{
MarkerTT.BeginAnimation(TranslateTransform.XProperty, null);
MarkerTT.X = Math.Max(0, targetX - 5);
}
}
// ===== Glow crítico =====
Storyboard _blinkSb;
void UpdateCriticalBlink()
{
if (!BlinkOnCritical || CritStart <= WarnStart)
{
StopBlink();
return;
}
if (Value >= CritStart)
StartBlink();
else
StopBlink();
}
void StartBlink()
{
if (_blinkSb != null) return;
var anim = new ColorAnimation
{
From = Color.FromArgb(0x00, 0xFF, 0x00, 0x00),
To = Color.FromArgb(0x22, 0xFF, 0x00, 0x00),
Duration = TimeSpan.FromMilliseconds(600),
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever,
EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut }
};
var brush = new SolidColorBrush(Color.FromArgb(0, 255, 0, 0));
AlertGlow.Background = brush;
_blinkSb = new Storyboard();
Storyboard.SetTarget(_blinkSb, brush);
Storyboard.SetTargetProperty(_blinkSb, new PropertyPath(SolidColorBrush.ColorProperty));
_blinkSb.Children.Add(anim);
_blinkSb.Begin();
}
void StopBlink()
{
_blinkSb?.Stop();
_blinkSb = null;
if (AlertGlow?.Background is SolidColorBrush b)
b.Color = Color.FromArgb(0x00, 0xFF, 0x00, 0x00);
}
}
}