2025-11-12 11:15:07 +00:00
|
|
|
|
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;
|
2025-11-12 11:15:07 +00:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|