using AuroraUIComponent.UserControls.KeyValueBase; using Microsoft.Xaml.Behaviors; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; namespace AuroraUIComponent.UserControls.Behavior { public class BlinkHighlightBehavior : Behavior { // 외부에서 직접 바인딩할 필요 없는 내부 상태 public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(nameof(IsActive), typeof(bool), typeof(BlinkHighlightBehavior), new PropertyMetadata(false, OnIsActiveChanged)); public bool IsActive { get => (bool)GetValue(IsActiveProperty); private set => SetValue(IsActiveProperty, value); // ← set을 private로 } // 하이라이트 색/속도 파라미터 public static readonly DependencyProperty HighlightColorProperty = DependencyProperty.Register(nameof(HighlightColor), typeof(Color), typeof(BlinkHighlightBehavior), new PropertyMetadata(Colors.Orange, OnAnimParamChanged)); public Color HighlightColor { get => (Color)GetValue(HighlightColorProperty); set => SetValue(HighlightColorProperty, value); } public static readonly DependencyProperty DurationMillisProperty = DependencyProperty.Register(nameof(DurationMillis), typeof(int), typeof(BlinkHighlightBehavior), new PropertyMetadata(400, OnAnimParamChanged)); public int DurationMillis { get => (int)GetValue(DurationMillisProperty); set => SetValue(DurationMillisProperty, value); } // ★ 이벤트 소스 주입 (컨버터 대신) public static readonly DependencyProperty HoverSourceProperty = DependencyProperty.Register(nameof(HoverSource), typeof(FrameworkElement), typeof(BlinkHighlightBehavior), new PropertyMetadata(null, OnSourceChanged)); public FrameworkElement HoverSource { get => (FrameworkElement)GetValue(HoverSourceProperty); set => SetValue(HoverSourceProperty, value); } public static readonly DependencyProperty ToolTipSourceProperty = DependencyProperty.Register(nameof(ToolTipSource), typeof(ToolTip), typeof(BlinkHighlightBehavior), new PropertyMetadata(null, OnSourceChanged)); public ToolTip ToolTipSource { get => (ToolTip)GetValue(ToolTipSourceProperty); set => SetValue(ToolTipSourceProperty, value); } // 내부 상태 bool _hover; bool _open; SolidColorBrush _keyBgBrush; SolidColorBrush _valueBgBrush; Color _originKeyColor; Color _originValueColor; ColorAnimation _anim; protected override void OnAttached() { base.OnAttached(); _keyBgBrush = EnsureRuntimeBrush(AssociatedObject.KeyTextBgColor, out _originKeyColor); _valueBgBrush = EnsureRuntimeBrush(AssociatedObject.ValueTextBgColor, out _originValueColor); AssociatedObject.KeyTextBgColor = _keyBgBrush; AssociatedObject.ValueTextBgColor = _valueBgBrush; RebuildAnimation(); HookSources(); // ★ 이벤트 연결 UpdateIsActive(); // 현재 상태 반영 } protected override void OnDetaching() { UnhookSources(); Stop(); base.OnDetaching(); } static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var bh = (BlinkHighlightBehavior)d; if (bh.AssociatedObject == null) return; if ((bool)e.NewValue) bh.Start(); else bh.Stop(); } static void OnAnimParamChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var bh = (BlinkHighlightBehavior)d; if (bh.AssociatedObject == null) return; bh.RebuildAnimation(); if (bh.IsActive) bh.Start(); } static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var bh = (BlinkHighlightBehavior)d; if (bh.AssociatedObject == null) return; bh.UnhookSources(); bh.HookSources(); bh.UpdateIsActive(); } SolidColorBrush EnsureRuntimeBrush(SolidColorBrush source, out Color original) { var color = (source as SolidColorBrush)?.Color ?? Colors.Transparent; original = color; return new SolidColorBrush(color); // 동결되지 않은 브러시 } void RebuildAnimation() { _anim = new ColorAnimation { To = HighlightColor, Duration = TimeSpan.FromMilliseconds(Math.Max(1, DurationMillis)), AutoReverse = true, RepeatBehavior = RepeatBehavior.Forever }; } void Start() { _keyBgBrush?.BeginAnimation(SolidColorBrush.ColorProperty, _anim); _valueBgBrush?.BeginAnimation(SolidColorBrush.ColorProperty, _anim); } void Stop() { if (_keyBgBrush != null) { _keyBgBrush.BeginAnimation(SolidColorBrush.ColorProperty, null); _keyBgBrush.Color = _originKeyColor; } if (_valueBgBrush != null) { _valueBgBrush.BeginAnimation(SolidColorBrush.ColorProperty, null); _valueBgBrush.Color = _originValueColor; } } // ===== 이벤트 소스 처리 ===== void HookSources() { if (HoverSource != null) { HoverSource.MouseEnter += HoverSource_MouseEnter; HoverSource.MouseLeave += HoverSource_MouseLeave; _hover = HoverSource.IsMouseOver; } if (ToolTipSource != null) { ToolTipSource.Opened += ToolTipSource_Opened; ToolTipSource.Closed += ToolTipSource_Closed; _open = ToolTipSource.IsOpen; } } void UnhookSources() { if (HoverSource != null) { HoverSource.MouseEnter -= HoverSource_MouseEnter; HoverSource.MouseLeave -= HoverSource_MouseLeave; } if (ToolTipSource != null) { ToolTipSource.Opened -= ToolTipSource_Opened; ToolTipSource.Closed -= ToolTipSource_Closed; } } void HoverSource_MouseEnter(object s, System.Windows.Input.MouseEventArgs e) { _hover = true; UpdateIsActive(); } void HoverSource_MouseLeave(object s, System.Windows.Input.MouseEventArgs e) { _hover = false; UpdateIsActive(); } void ToolTipSource_Opened(object s, RoutedEventArgs e) { _open = true; UpdateIsActive(); } void ToolTipSource_Closed(object s, RoutedEventArgs e) { _open = false; UpdateIsActive(); } void UpdateIsActive() { IsActive = _hover || _open; // ★ OR 연산을 내부에서 처리 } } }